Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 为什么在比较中使用IComparable比使用字符串慢?_C#_.net_String_Performance - Fatal编程技术网

C# 为什么在比较中使用IComparable比使用字符串慢?

C# 为什么在比较中使用IComparable比使用字符串慢?,c#,.net,string,performance,C#,.net,String,Performance,我使用Mergesort对50.000.000个字符串进行排序,根据我使用的参数类型有两种不同的结果 使用接口IComparable: 20226毫秒 直接使用字符串: 10912毫秒 合并排序代码: public class Mergesort2 { static private StringComparer comparer1 = StringComparer.Ordinal; public static void merge(IComparable[] a, ICompar

我使用Mergesort对50.000.000个字符串进行排序,根据我使用的参数类型有两种不同的结果

使用接口IComparable:

20226毫秒 直接使用字符串:

10912毫秒 合并排序代码:

public class Mergesort2
{
    static private StringComparer comparer1 = StringComparer.Ordinal;
    public static void merge(IComparable[] a, IComparable[] aux, int lo, int mid, int hi)
    {

        for (int k = lo; k <= hi; k++)
        {
            aux[k] = a[k];
        }

        // merge back to a[]
        int i = lo, j = mid + 1;
        for (int k = lo; k <= hi; k++)
        {
            if (i > mid)
            {
                a[k] = aux[j++];
            }
            else if (j > hi)
            {
                a[k] = aux[i++];
            }
            else if (less(aux[j], aux[i]))
            {
                a[k] = aux[j++];
            }
            else
            {
                a[k] = aux[i++];
            }
        }

    }

    private static void sort(IComparable[] a, IComparable[] aux, int lo, int hi)
    {
        if (hi <= lo)
        {
            return;
        }
        int mid = lo + (hi - lo) / 2;
        sort(a, aux, lo, mid);
        sort(a, aux, mid + 1, hi);
        merge(a, aux, lo, mid, hi);
    }

    public static void sort(IComparable[] a)
    {
        IComparable[] aux = new IComparable[a.Length];
        sort(a, aux, 0, a.Length - 1);
    }


    ///*********************************************************************
    ///  Helper sorting functions
    /// **********************************************************************

    // is v < w ?
    private static bool less(IComparable v, IComparable w)
    {
        return (comparer1.Compare(v, w) < 0);
    }

    // exchange a[i] and a[j]
    private static void exch(Object[] a, int i, int j)
    {
        Object swap = a[i];
        a[i] = a[j];
        a[j] = swap;
    }

    /// <summary>
    ///*********************************************************************
    ///  Index mergesort
    /// **********************************************************************
    /// </summary>
    // stably merge a[lo .. mid] with a[mid+1 .. hi] using aux[lo .. hi]
    private static void merge(IComparable[] a, int[] index, int[] aux, int lo, int mid, int hi)
    {

        // copy to aux[]
        for (int k = lo; k <= hi; k++)
        {
            aux[k] = index[k];
        }

        // merge back to a[]
        int i = lo, j = mid + 1;
        for (int k = lo; k <= hi; k++)
        {
            if (i > mid)
            {
                index[k] = aux[j++];
            }
            else if (j > hi)
            {
                index[k] = aux[i++];
            }
            else if (less(a[aux[j]], a[aux[i]]))
            {
                index[k] = aux[j++];
            }
            else
            {
                index[k] = aux[i++];
            }
        }
    }

    // return a permutation that gives the elements in a[] in ascending order
    // do not change the original array a[]
    public static int[] indexSort(IComparable[] a)
    {
        int N = a.Length;
        int[] index = new int[N];
        for (int i = 0; i < N; i++)
        {
            index[i] = i;
        }

        int[] aux = new int[N];
        sort(a, index, aux, 0, N - 1);
        return index;
    }

    // mergesort a[lo..hi] using auxiliary array aux[lo..hi]
    private static void sort(IComparable[] a, int[] index, int[] aux, int lo, int hi)
    {
        if (hi <= lo)
        {
            return;
        }
        int mid = lo + (hi - lo) / 2;
        sort(a, index, aux, lo, mid);
        sort(a, index, aux, mid + 1, hi);
        merge(a, index, aux, lo, mid, hi);
    }
}
这段代码会产生较慢的运行时


如果我将所有IComparable类型都更改为String,那么性能将会提高。为什么使用不同的类型会有如此巨大的性能差异?

我必须简单地说:对象本身的大小。。。因此,每次你声明一个ICompare,它包含一堆额外的属性、方法等,比声明较小的字符串花费更多的时间…

IMHO,我必须简单地说:对象本身的大小。。。因此,每次您声明一个包含大量额外属性、方法等的ICompare时,要比声明较小的字符串花费更多的时间…

来回答有关性能的问题:您的测试使用的字符串足够小,使用非泛型IComparable接口需要额外的类型检查,随着使用接口分派而不是虚拟分派,像.NET和Java VM这样的虚拟机的低级细节比字符串比较更昂贵。如果使用带有长公共前缀的字符串,比较操作将成为主要的性能成本,两种形式之间的差距将缩小。编辑:在代码的发布版本上运行测试可能会缩小差距,因为没有在本地运行测试,我不确定OP用于测试的版本是什么

现在对于整个实验来说更重要的是,忽略代码的所有其他问题,我将特别指出在.NET中以通用方式支持类似项的常见做法

不要限制泛型类型T可能是字符串,也可能不是字符串,如果它可能实现也可能不实现IComparable的话。 使用IComparer来比较元素。如果用户将comparer参数的null传递给其中一个公共方法,则默认为comparer.default。 以下是更新的代码:

public class Mergesort2
{
    public static void merge<T>(T[] a, T[] aux, int lo, int mid, int hi, IComparer<T> comparer)
    {
        comparer = comparer ?? Comparer<T>.Default;

        for (int k = lo; k <= hi; k++)
        {
            aux[k] = a[k];
        }

        // merge back to a[]
        int i = lo, j = mid + 1;
        for (int k = lo; k <= hi; k++)
        {
            if (i > mid)
            {
                a[k] = aux[j++];
            }
            else if (j > hi)
            {
                a[k] = aux[i++];
            }
            else if (less(aux[j], aux[i], comparer))
            {
                a[k] = aux[j++];
            }
            else
            {
                a[k] = aux[i++];
            }
        }

    }

    private static void sort<T>(T[] a, T[] aux, int lo, int hi, IComparer<T> comparer)
    {
        if (hi <= lo)
        {
            return;
        }
        int mid = lo + (hi - lo) / 2;
        sort(a, aux, lo, mid, comparer);
        sort(a, aux, mid + 1, hi, comparer);
        merge(a, aux, lo, mid, hi, comparer);
    }

    public static void sort<T>(T[] a, IComparer<T> comparer)
    {
        comparer = comparer ?? Comparer<T>.Default;
        T[] aux = new T[a.Length];
        sort(a, aux, 0, a.Length - 1, comparer);
    }


    ///*********************************************************************
    ///  Helper sorting functions
    /// **********************************************************************

    // is v < w ?
    private static bool less<T>(T v, T w, IComparer<T> comparer)
    {
        return (comparer.Compare(v, w) < 0);
    }

    // exchange a[i] and a[j]
    private static void exch<T>(T[] a, int i, int j)
    {
        T swap = a[i];
        a[i] = a[j];
        a[j] = swap;
    }

    /// <summary>
    ///*********************************************************************
    ///  Index mergesort
    /// **********************************************************************
    /// </summary>
    // stably merge a[lo .. mid] with a[mid+1 .. hi] using aux[lo .. hi]
    private static void merge<T>(T[] a, int[] index, int[] aux, int lo, int mid, int hi, IComparer<T> comparer)
    {

        // copy to aux[]
        for (int k = lo; k <= hi; k++)
        {
            aux[k] = index[k];
        }

        // merge back to a[]
        int i = lo, j = mid + 1;
        for (int k = lo; k <= hi; k++)
        {
            if (i > mid)
            {
                index[k] = aux[j++];
            }
            else if (j > hi)
            {
                index[k] = aux[i++];
            }
            else if (less(a[aux[j]], a[aux[i]], comparer))
            {
                index[k] = aux[j++];
            }
            else
            {
                index[k] = aux[i++];
            }
        }
    }

    // return a permutation that gives the elements in a[] in ascending order
    // do not change the original array a[]
    public static int[] indexSort<T>(T[] a, IComparer<T> comparer)
    {
        comparer = comparer ?? Comparer<T>.Default;
        int N = a.Length;
        int[] index = new int[N];
        for (int i = 0; i < N; i++)
        {
            index[i] = i;
        }

        int[] aux = new int[N];
        sort(a, index, aux, 0, N - 1, comparer);
        return index;
    }

    // mergesort a[lo..hi] using auxiliary array aux[lo..hi]
    private static void sort<T>(T[] a, int[] index, int[] aux, int lo, int hi, IComparer<T> comparer)
    {
        if (hi <= lo)
        {
            return;
        }
        int mid = lo + (hi - lo) / 2;
        sort(a, index, aux, lo, mid, comparer);
        sort(a, index, aux, mid + 1, hi, comparer);
        merge(a, index, aux, lo, mid, hi, comparer);
    }
}

为了回答关于性能的问题:您的测试使用了足够小的字符串,使用非泛型IComparable接口需要额外的类型检查,随着使用接口分派而不是虚拟分派,像.NET和Java VM这样的虚拟机的低级细节比字符串比较更昂贵。如果使用带有长公共前缀的字符串,比较操作将成为主要的性能成本,两种形式之间的差距将缩小。编辑:在代码的发布版本上运行测试可能会缩小差距,因为没有在本地运行测试,我不确定OP用于测试的版本是什么

现在对于整个实验来说更重要的是,忽略代码的所有其他问题,我将特别指出在.NET中以通用方式支持类似项的常见做法

不要限制泛型类型T可能是字符串,也可能不是字符串,如果它可能实现也可能不实现IComparable的话。 使用IComparer来比较元素。如果用户将comparer参数的null传递给其中一个公共方法,则默认为comparer.default。 以下是更新的代码:

public class Mergesort2
{
    public static void merge<T>(T[] a, T[] aux, int lo, int mid, int hi, IComparer<T> comparer)
    {
        comparer = comparer ?? Comparer<T>.Default;

        for (int k = lo; k <= hi; k++)
        {
            aux[k] = a[k];
        }

        // merge back to a[]
        int i = lo, j = mid + 1;
        for (int k = lo; k <= hi; k++)
        {
            if (i > mid)
            {
                a[k] = aux[j++];
            }
            else if (j > hi)
            {
                a[k] = aux[i++];
            }
            else if (less(aux[j], aux[i], comparer))
            {
                a[k] = aux[j++];
            }
            else
            {
                a[k] = aux[i++];
            }
        }

    }

    private static void sort<T>(T[] a, T[] aux, int lo, int hi, IComparer<T> comparer)
    {
        if (hi <= lo)
        {
            return;
        }
        int mid = lo + (hi - lo) / 2;
        sort(a, aux, lo, mid, comparer);
        sort(a, aux, mid + 1, hi, comparer);
        merge(a, aux, lo, mid, hi, comparer);
    }

    public static void sort<T>(T[] a, IComparer<T> comparer)
    {
        comparer = comparer ?? Comparer<T>.Default;
        T[] aux = new T[a.Length];
        sort(a, aux, 0, a.Length - 1, comparer);
    }


    ///*********************************************************************
    ///  Helper sorting functions
    /// **********************************************************************

    // is v < w ?
    private static bool less<T>(T v, T w, IComparer<T> comparer)
    {
        return (comparer.Compare(v, w) < 0);
    }

    // exchange a[i] and a[j]
    private static void exch<T>(T[] a, int i, int j)
    {
        T swap = a[i];
        a[i] = a[j];
        a[j] = swap;
    }

    /// <summary>
    ///*********************************************************************
    ///  Index mergesort
    /// **********************************************************************
    /// </summary>
    // stably merge a[lo .. mid] with a[mid+1 .. hi] using aux[lo .. hi]
    private static void merge<T>(T[] a, int[] index, int[] aux, int lo, int mid, int hi, IComparer<T> comparer)
    {

        // copy to aux[]
        for (int k = lo; k <= hi; k++)
        {
            aux[k] = index[k];
        }

        // merge back to a[]
        int i = lo, j = mid + 1;
        for (int k = lo; k <= hi; k++)
        {
            if (i > mid)
            {
                index[k] = aux[j++];
            }
            else if (j > hi)
            {
                index[k] = aux[i++];
            }
            else if (less(a[aux[j]], a[aux[i]], comparer))
            {
                index[k] = aux[j++];
            }
            else
            {
                index[k] = aux[i++];
            }
        }
    }

    // return a permutation that gives the elements in a[] in ascending order
    // do not change the original array a[]
    public static int[] indexSort<T>(T[] a, IComparer<T> comparer)
    {
        comparer = comparer ?? Comparer<T>.Default;
        int N = a.Length;
        int[] index = new int[N];
        for (int i = 0; i < N; i++)
        {
            index[i] = i;
        }

        int[] aux = new int[N];
        sort(a, index, aux, 0, N - 1, comparer);
        return index;
    }

    // mergesort a[lo..hi] using auxiliary array aux[lo..hi]
    private static void sort<T>(T[] a, int[] index, int[] aux, int lo, int hi, IComparer<T> comparer)
    {
        if (hi <= lo)
        {
            return;
        }
        int mid = lo + (hi - lo) / 2;
        sort(a, index, aux, lo, mid, comparer);
        sort(a, index, aux, mid + 1, hi, comparer);
        merge(a, index, aux, lo, mid, hi, comparer);
    }
}

如果你把它改为通用的,使用IComparable呢?是的,这是一个选项,但我在使用字符串方面没有什么问题,我只是想了解其中的区别。至少100000000次转换是昂贵的。例如,在返回比较器1.Comparev中,w<0StringComparer.Compare对对象和字符串具有不同的重载。字符串版本最有可能是高度优化的,而对象版本只提供常规的.NET性能,并且鉴于其一般性质,编译器更难优化。对象比较StringComparer到底有什么用?在我看来,它只是在履行提供对象接口的合同义务。为什么要将StringComparer与字符串以外的任何东西一起使用,对我来说似乎是个谜。我支持@TimSchmelter。无意义的强制转换。如果你让它成为通用的,而使用IComparable呢?是的,这是一个选项,但我在使用字符串方面并没有问题,我只是想了解区别。至少100000000次强制转换是昂贵的。例如,在返回比较器1.Comparev中,w<0StringComparer.Compare对对象和字符串具有不同的重载。字符串版本最有可能是高度优化的,而对象版本只提供常规的.NET性能,并且鉴于其一般性质,编译器更难优化。对象比较StringComparer到底有什么用?在我看来,它只是在履行提供对象接口的合同义务。为什么要将StringComparer与字符串以外的任何东西一起使用,对我来说似乎是个谜。我支持@TimSchmelter。无意义cas
这完全不对。另外,再看一下字符串。它有很多方法,但正如我所说的,这在这里根本不相关。你能给我们指一些支持你答案的文档吗?它在哪里说实例化具有更多属性和/或方法的对象需要更长的时间?你真的是说将字符串强制转换为IComparable会分配一个新对象吗?那不对。12个对象,一个有1000 000个属性,另一个有5个。在单独的网页上,在Page_Load事件上创建每个对象的单个实例。在网页中,放置一条消息。当然,创建具有更多属性的对象需要更长的时间!如果你愿意的话,自己量一量!一个或几个毫秒以上。。。超过50000次,等于秒。2汇编程序级转换:1-查看对象是否兼容,2-创建新对象,3-在新中放置旧属性,4-在mem地址X销毁旧,5-在同一mem地址放置新。。。这是我的知识无论如何。。。在这里,还有几毫秒!那完全不对。另外,再看一下字符串。它有很多方法,但正如我所说的,这在这里根本不相关。你能给我们指一些支持你答案的文档吗?它在哪里说实例化具有更多属性和/或方法的对象需要更长的时间?你真的是说将字符串强制转换为IComparable会分配一个新对象吗?那不对。12个对象,一个有1000 000个属性,另一个有5个。在单独的网页上,在Page_Load事件上创建每个对象的单个实例。在网页中,放置一条消息。当然,创建具有更多属性的对象需要更长的时间!如果你愿意的话,自己量一量!一个或几个毫秒以上。。。超过50000次,等于秒。2汇编程序级转换:1-查看对象是否兼容,2-创建新对象,3-在新中放置旧属性,4-在mem地址X销毁旧,5-在同一mem地址放置新。。。这是我的知识无论如何。。。在这里,还有几毫秒!有趣,但我不明白三件事:1你怎么知道OP用于测试的字符串是什么类型的?2如果您能给我指出一些文献/规范,其中说虚拟分派比.NET中的接口分派快,我将不胜感激。3为什么默认为Comparer。默认为非IComparable类型?就我个人而言,我会将T限制为IComparable,因为如果该接口实现缺失,那么很可能该类型不是设计为可排序的,即排序操作可能对该类型没有任何意义。我将该程序构建为发行版。但我认为你的代码无法编译。我想你把我的同事和我搞混了IComparable@stakx在.NET中,不将类型标记为IComparable是完全有效的,而只依赖于提供IComparaer实现的开发人员。其中一个例子是SortedList类:@user2025998如果我的代码示例中出现IComparable,那么我犯了一个错误。我的意图是将IComparer用作比较T类型对象的唯一接口。@stakx:re 2,它比这更复杂。在.NET中,为接口分派和虚拟分派选择的策略比较复杂,并且随着时间的推移已经发生了变化。肯定有这样的场景,其中一个比另一个快,但我不想准确描述它们在我脑海中的位置。在Roslyn的早期,我们对此做了很多研究,但是我不记得结果了,我现在也没有能力去查找它们。Vance Morrison 2006年的文章是一个很好的开始,但可能已经过时了:很有趣,但我不明白三件事:1.你怎么知道OP用于测试的是哪种字符串?2如果您能给我指出一些文献/规范,其中说虚拟分派比.NET中的接口分派快,我将不胜感激。3为什么默认为Comparer。默认为非IComparable类型?就我个人而言,我会将T限制为IComparable,因为如果该接口实现缺失,那么很可能该类型不是设计为可排序的,即排序操作可能对该类型没有任何意义。我将该程序构建为发行版。但我认为你的代码无法编译。我想你把我的同事和我搞混了IComparable@stakx在.NET中,不将类型标记为IComparable是完全有效的,而只依赖于提供IComparaer实现的开发人员。其中一个例子是SortedList类:@user2025998如果我的代码示例中出现IComparable,那么我犯了一个错误。我的意图是将IComparer用作比较T类型对象的唯一接口。@stakx:re 2,它比这更复杂。在.NET中,为接口分派和虚拟分派选择的策略比较复杂,并且随着时间的推移已经发生了变化。肯定有这样的场景,其中一个比另一个快,但我不想准确描述它们在我脑海中的位置。我们在早期就做了大量的研究 是Roslyn的,但我记不起结果了,我现在没有能力去查。万斯·莫里森(Vance Morrison)2006年的文章是一个很好的开始,但可能已经过时了: