比较 List.Sort() 的性能是否优于自定义 IComparer?
Is performance of List.Sort() by Comparison is better than custom IComparer?
我正在创建自定义排序整数列表。我都试过了:使用比较和自定义 IComparer:
比较:
public int ComparisonMethod(int x, int y)
{
//just for testing
return 1;// just for test
}
List.Sort((x, y) => ComparisonMethod(x,y));
与客户 IComparer:
public class Comparer : IComparer<int>
{
public int Compare(int x, int y)
{
//just for testing
return 1;// just for test
}
}
var comparer = new Comparer();
List.Sort(comparer);
Custom Comparer 和 Comparison 的逻辑代码是一样的,但是比较惊讶的是,Comparison 排序的性能比 Comparer 好。为什么比较的性能更好?还是我错了?
关注!
我对你的陈述很好奇,所以我使用广泛采用的 BenchmarkDotNet 库整理了一个基准:
using System;
using System.Collections.Generic;
using System.Linq;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
namespace perftest
{
#if NETCOREAPP2_1
[CoreJob]
#else
[ClrJob]
#endif
[RankColumn, MarkdownExporterAttribute.Whosebug]
public class Benchmark
{
class Comparer : IComparer<int>
{
public int Compare(int x, int y)
{
return x > y ? 1 : x < y ? -1 : 0;
}
}
static int ComparisonMethod(int x, int y)
{
return x > y ? 1 : x < y ? -1 : 0;
}
Comparison<int> _comparisonDelegate;
IComparer<int> _comparer;
List<int> _data, _tempData;
[GlobalSetup]
public void GlobalSetup()
{
var random = new Random(0);
_comparisonDelegate = ComparisonMethod;
_comparer = new Comparer();
_data = Enumerable.Range(0, 1000).Select(_ => random.Next()).ToList();
_tempData = new List<int>(_data.Count);
}
[Benchmark]
public int Delegate()
{
var result = 0;
for (var i = 0; i < _data.Count; i++)
result += _comparisonDelegate(_data[0], _data[i]);
return result;
}
[Benchmark]
public int Interface()
{
var result = 0;
for (var i = 0; i < _data.Count; i++)
result += _comparer.Compare(_data[0], _data[i]);
return result;
}
[Benchmark]
public int SortUsingDelegate()
{
_tempData.Clear();
_tempData.AddRange(_data);
_tempData.Sort(_comparisonDelegate);
return _tempData[0];
}
[Benchmark]
public int SortUsingInterface()
{
_tempData.Clear();
_tempData.AddRange(_data);
_tempData.Sort(_comparer);
return _tempData[0];
}
}
public class Program
{
public static void Main(string[] args)
{
BenchmarkRunner.Run<Benchmark>();
}
}
}
结果
.NET 核心 2.1
BenchmarkDotNet=v0.11.1, OS=Windows 7 SP1 (6.1.7601.0)
Intel Core2 Duo CPU T9500 2.60GHz, 1 CPU, 2 logical and 2 physical cores
Frequency=2532763 Hz, Resolution=394.8257 ns, Timer=TSC
.NET Core SDK=2.1.402
[Host] : .NET Core 2.1.4 (CoreCLR 4.6.26814.03, CoreFX 4.6.26814.02), 64bit RyuJIT
Core : .NET Core 2.1.4 (CoreCLR 4.6.26814.03, CoreFX 4.6.26814.02), 64bit RyuJIT
Job=Core Runtime=Core
Method | Mean | Error | StdDev | Rank |
------------------- |-----------:|----------:|----------:|-----:|
Delegate | 8.152 us | 0.0087 us | 0.0073 us | 2 |
Interface | 7.476 us | 0.0462 us | 0.0409 us | 1 |
SortUsingDelegate | 103.914 us | 0.0728 us | 0.0608 us | 4 |
SortUsingInterface | 95.900 us | 0.6673 us | 0.5915 us | 3 |
.NET Framework 4.7.2
BenchmarkDotNet=v0.11.1, OS=Windows 7 SP1 (6.1.7601.0)
Intel Core2 Duo CPU T9500 2.60GHz, 1 CPU, 2 logical and 2 physical cores
Frequency=2532763 Hz, Resolution=394.8257 ns, Timer=TSC
[Host] : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3163.0
Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3163.0
Job=Clr Runtime=Clr
Method | Mean | Error | StdDev | Rank |
------------------- |-----------:|----------:|----------:|-----:|
Delegate | 9.677 us | 0.0095 us | 0.0079 us | 2 |
Interface | 9.126 us | 0.0252 us | 0.0236 us | 1 |
SortUsingDelegate | 178.287 us | 0.3618 us | 0.2825 us | 4 |
SortUsingInterface | 174.389 us | 1.5997 us | 1.3358 us | 3 |
这与您的发现相矛盾:使用接口进行比较往往比通过委托进行比较快一点点。
不同之处在于接口方法调用(一种虚拟方法调用)的实现方式与 .NET 中的委托调用不同。
我正在创建自定义排序整数列表。我都试过了:使用比较和自定义 IComparer:
比较:
public int ComparisonMethod(int x, int y)
{
//just for testing
return 1;// just for test
}
List.Sort((x, y) => ComparisonMethod(x,y));
与客户 IComparer:
public class Comparer : IComparer<int>
{
public int Compare(int x, int y)
{
//just for testing
return 1;// just for test
}
}
var comparer = new Comparer();
List.Sort(comparer);
Custom Comparer 和 Comparison 的逻辑代码是一样的,但是比较惊讶的是,Comparison 排序的性能比 Comparer 好。为什么比较的性能更好?还是我错了?
关注!
我对你的陈述很好奇,所以我使用广泛采用的 BenchmarkDotNet 库整理了一个基准:
using System;
using System.Collections.Generic;
using System.Linq;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
namespace perftest
{
#if NETCOREAPP2_1
[CoreJob]
#else
[ClrJob]
#endif
[RankColumn, MarkdownExporterAttribute.Whosebug]
public class Benchmark
{
class Comparer : IComparer<int>
{
public int Compare(int x, int y)
{
return x > y ? 1 : x < y ? -1 : 0;
}
}
static int ComparisonMethod(int x, int y)
{
return x > y ? 1 : x < y ? -1 : 0;
}
Comparison<int> _comparisonDelegate;
IComparer<int> _comparer;
List<int> _data, _tempData;
[GlobalSetup]
public void GlobalSetup()
{
var random = new Random(0);
_comparisonDelegate = ComparisonMethod;
_comparer = new Comparer();
_data = Enumerable.Range(0, 1000).Select(_ => random.Next()).ToList();
_tempData = new List<int>(_data.Count);
}
[Benchmark]
public int Delegate()
{
var result = 0;
for (var i = 0; i < _data.Count; i++)
result += _comparisonDelegate(_data[0], _data[i]);
return result;
}
[Benchmark]
public int Interface()
{
var result = 0;
for (var i = 0; i < _data.Count; i++)
result += _comparer.Compare(_data[0], _data[i]);
return result;
}
[Benchmark]
public int SortUsingDelegate()
{
_tempData.Clear();
_tempData.AddRange(_data);
_tempData.Sort(_comparisonDelegate);
return _tempData[0];
}
[Benchmark]
public int SortUsingInterface()
{
_tempData.Clear();
_tempData.AddRange(_data);
_tempData.Sort(_comparer);
return _tempData[0];
}
}
public class Program
{
public static void Main(string[] args)
{
BenchmarkRunner.Run<Benchmark>();
}
}
}
结果
.NET 核心 2.1
BenchmarkDotNet=v0.11.1, OS=Windows 7 SP1 (6.1.7601.0)
Intel Core2 Duo CPU T9500 2.60GHz, 1 CPU, 2 logical and 2 physical cores
Frequency=2532763 Hz, Resolution=394.8257 ns, Timer=TSC
.NET Core SDK=2.1.402
[Host] : .NET Core 2.1.4 (CoreCLR 4.6.26814.03, CoreFX 4.6.26814.02), 64bit RyuJIT
Core : .NET Core 2.1.4 (CoreCLR 4.6.26814.03, CoreFX 4.6.26814.02), 64bit RyuJIT
Job=Core Runtime=Core
Method | Mean | Error | StdDev | Rank |
------------------- |-----------:|----------:|----------:|-----:|
Delegate | 8.152 us | 0.0087 us | 0.0073 us | 2 |
Interface | 7.476 us | 0.0462 us | 0.0409 us | 1 |
SortUsingDelegate | 103.914 us | 0.0728 us | 0.0608 us | 4 |
SortUsingInterface | 95.900 us | 0.6673 us | 0.5915 us | 3 |
.NET Framework 4.7.2
BenchmarkDotNet=v0.11.1, OS=Windows 7 SP1 (6.1.7601.0)
Intel Core2 Duo CPU T9500 2.60GHz, 1 CPU, 2 logical and 2 physical cores
Frequency=2532763 Hz, Resolution=394.8257 ns, Timer=TSC
[Host] : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3163.0
Clr : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3163.0
Job=Clr Runtime=Clr
Method | Mean | Error | StdDev | Rank |
------------------- |-----------:|----------:|----------:|-----:|
Delegate | 9.677 us | 0.0095 us | 0.0079 us | 2 |
Interface | 9.126 us | 0.0252 us | 0.0236 us | 1 |
SortUsingDelegate | 178.287 us | 0.3618 us | 0.2825 us | 4 |
SortUsingInterface | 174.389 us | 1.5997 us | 1.3358 us | 3 |
这与您的发现相矛盾:使用接口进行比较往往比通过委托进行比较快一点点。
不同之处在于接口方法调用(一种虚拟方法调用)的实现方式与 .NET 中的委托调用不同。