4

一般的に と の間には大きな違いがあることは知っていますが、これを参照IComparableしてください。しかし、この検索方法では違いはありませんか?IComparable<T>

public static int Search<T>(List<T> a, T target) where T : IComparable
{
    for (int i = 0; i < a.Count; i++)
    {
        if (target.CompareTo(a[i]) == 0)
            return i;
    }
    return -1;
}

に比べ:

public static int Search<T>(List<T> a, T target) where T : IComparable<T>
{
    ...
}

どちらも機能し、同じ結果が得られますか?

4

4 に答える 4

9

どちらも機能し、同じ結果が得られますか?

両方の検索方法でコンパイラが出力するものを見てみましょう。

Search:
IL_0000:  ldc.i4.0    
IL_0001:  stloc.0     // i
IL_0002:  br.s        IL_0025
IL_0004:  ldarga.s    01 
IL_0006:  ldarg.0     
IL_0007:  ldloc.0     // i
IL_0008:  callvirt    06 00 00 0A 
IL_000D:  box         03 00 00 1B <---- Notice this!
IL_0012:  constrained. 03 00 00 1B 
IL_0018:  callvirt    System.IComparable.CompareTo
IL_001D:  brtrue.s    IL_0021
IL_001F:  ldloc.0     // i
IL_0020:  ret         
IL_0021:  ldloc.0     // i
IL_0022:  ldc.i4.1    
IL_0023:  add         
IL_0024:  stloc.0     // i
IL_0025:  ldloc.0     // i
IL_0026:  ldarg.0     
IL_0027:  callvirt    08 00 00 0A 
IL_002C:  blt.s       IL_0004
IL_002E:  ldc.i4.m1   
IL_002F:  ret         

SearchGeneric:
IL_0000:  ldc.i4.0    
IL_0001:  stloc.0     // i
IL_0002:  br.s        IL_0020
IL_0004:  ldarga.s    01 
IL_0006:  ldarg.0     
IL_0007:  ldloc.0     // i
IL_0008:  callvirt    06 00 00 0A 
IL_000D:  constrained. 03 00 00 1B 
IL_0013:  callvirt    09 00 00 0A 
IL_0018:  brtrue.s    IL_001C
IL_001A:  ldloc.0     // i
IL_001B:  ret         
IL_001C:  ldloc.0     // i
IL_001D:  ldc.i4.1    
IL_001E:  add         
IL_001F:  stloc.0     // i
IL_0020:  ldloc.0     // i
IL_0021:  ldarg.0     
IL_0022:  callvirt    08 00 00 0A 
IL_0027:  blt.s       IL_0004
IL_0029:  ldc.i4.m1   
IL_002A:  ret    

注意深く見ると、主な違いは、非ジェネリック バージョンが型を受け入れるため、Search呼び出す前に の各呼び出しCompareToが値をボックス化する必要があるという事実であることがわかります(これは操作で顕著です) 。boxobject

値型を使用して、2 つのパフォーマンスの違いを分析してみましょう。を使用しますBenchmarkDotNet。これは、JITing、CPU ウォームアップなどを処理する小さな (そして本当に素晴らしい) ベンチマーク フレームワークです。

テスト:

[BenchmarkTask(platform: BenchmarkPlatform.X86)]
[BenchmarkTask(platform: BenchmarkPlatform.X64)]
public class Test
{
    private readonly List<int> list = Enumerable.Range(0, 1000000).ToList();

    [Benchmark]
    public void TestSearch()
    {
        Search(list, 999999);
    }

    [Benchmark]
    public void TestSearchGeneric()
    {
        SearchGeneric(list, 999999);
    }

    public static int Search<T>(List<T> a, T target) where T : IComparable
    {
        for (int i = 0; i < a.Count; i++)
        {
            if (target.CompareTo(a[i]) == 0)
                return i;
        }
        return -1;
    }
    public static int SearchGeneric<T>(List<T> a, T target) where T : IComparable<T>
    {
        for (int i = 0; i < a.Count; i++)
        {
            if (target.CompareTo(a[i]) == 0)
                return i;
        }
        return -1;
    }
}

結果:

***** Competition: Finish  *****

BenchmarkDotNet=v0.7.8.0
OS=Microsoft Windows NT 6.1.7601 Service Pack 1
Processor=Intel(R) Core(TM) i7-4600U CPU @ 2.10GHz, ProcessorCount=4
HostCLR=MS.NET 4.0.30319.42000, Arch=32-bit
Type=Test  Mode=Throughput  Jit=HostJit  .NET=HostFramework


            Method | Platform |    AvrTime |    StdDev |   op/s |
------------------ |--------- |----------- |---------- |------- |
        TestSearch |      X64 | 35.8065 ms | 3.3439 ms |  27.93 |
 TestSearchGeneric |      X64 |  4.6427 ms | 0.3075 ms | 215.40 |
        TestSearch |      X86 | 26.4876 ms | 1.4776 ms |  37.75 |
 TestSearchGeneric |      X86 |  6.6500 ms | 0.1664 ms | 150.38 |

***** Competition: End *****

ボックス化操作を行う非ジェネリック メソッドは、x86 では4 倍以上、x64では 8 倍以上遅くなることがわかります。これは、アプリケーションのパフォーマンスに影響を与える可能性があります。

私は一般的に non-generic を使用しませんIComparable。これは主に、ジェネリックより前の時代との下位互換性のために存在します。もう 1 つの重要な要素は、ジェネリクスで得られる型の安全性です。

于 2015-12-12T19:08:34.477 に答える
3

これは大きな違いです。最初の方法は、理由もなく値の型をボックス化することです。

于 2015-12-12T17:26:01.633 に答える
2

最大の違いは明らかです。バージョンwhere T : IComparableは、 を実装する型で使用できますIComparable。バージョンwhere T : IComparable<T>は、 を実装する型で使用できますIComparable<T>。実装されているが実装されIComparableていない一般的に使用される型IComparable<T>は、さまざまなSystem.Tuple<...>ジェネリック型とすべての列挙型です。

于 2015-12-12T17:36:37.003 に答える
2

最初のもの(i.e. IComparable)はジェネリック型を実装せず、親クラスに使用される型とは関係ありませんが、2番目のもの (つまりIComparable<T>) は、親クラスに指定した型のみを使用できるタイプ セーフです。

于 2015-12-12T17:26:47.880 に答える