Я создаю настраиваемый сортировочный список целых чисел. Я пробовал оба: используя сравнение и пользовательский IComparer:

Со сравнением:

public int ComparisonMethod(int x, int y)
    {
        //just for testing
        return 1;// just for test
    }

List.Sort((x, y) => ComparisonMethod(x,y));   

С Custome 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 имеют одинаковый логический код, но я был удивлен, что производительность сортировки по сравнению лучше, чем Comparer. Почему производительность сравнения лучше? Или я ошибался?

Рассматривать !

2
Nhan Phan 22 Сен 2018 в 20:18

1 ответ

Лучший ответ

Мне интересно ваше утверждение, поэтому я составил тест, используя широко распространенную библиотеку 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.StackOverflow]
    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 Core 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.

6
Adam Simon 22 Сен 2018 в 19:57