我知道,有一个很大的区别IComparable
,并IComparable
在一般情况下,看到这,但在这个搜索方法就没有任何区别,还是会?
public static int Search(List 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(List a, T target) where T : IComparable { ... }
两者都会起作用并给出相同的结果吗?
两者都会起作用并给出相同的结果吗?
让我们看一下编译器为两种搜索方法发出的内容:
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
都必须将值包装(这在box
操作中是值得注意的),因为非泛型版本接受一个object
类型.
让我们尝试使用值类型分析两者之间的性能差异.我将使用BenchmarkDotNet
,这是一个小的(并且真正令人敬畏的)基准测试框架,它负责JITing,CPU预热等.
测试:
[BenchmarkTask(platform: BenchmarkPlatform.X86)] [BenchmarkTask(platform: BenchmarkPlatform.X64)] public class Test { private readonly Listlist = Enumerable.Range(0, 1000000).ToList(); [Benchmark] public void TestSearch() { Search(list, 999999); } [Benchmark] public void TestSearchGeneric() { SearchGeneric(list, 999999); } public static int Search (List 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 (List a, T target) where T : IComparable { 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倍.这可能会对您的应用程序性能产生影响.
我通常不会使用非泛型IComparable
,这主要是为了向后兼容泛型之前的日子.请注意,另一个不太重要的因素是通过泛型获得的类型安全性.