Сравнение производительности между индивидуально реализованными методами LINQ и оригиналом

1

Я просто возился с внедрением некоторых методов LINQ, когда выяснял, что некоторые из пользовательских реализаций действительно работают быстрее оригинала, хотя реализация аналогична. Обработка списков вместо массивов делает разницу скорости еще больше в сторону пользовательских методов!

public static class CustomLINQ
{
    public static IEnumerable<T> WHERE<T>(this IEnumerable<T> items, Func<T, bool> predicate)
    {
        foreach (var item in items)
            if (predicate(item))
                yield return item;
    }

    public static IEnumerable<TReturn> SELECT<TSource, TReturn>(this IEnumerable<TSource> items, Func<TSource, TReturn> projection)
    {
        foreach (var item in items)
            yield return projection(item);
    }
}

public class Program
{
    static int loops  = 1000000;
    static int nTests = 100;

    static void Measure(string msg, Action code)
    {
        long total = 0;
        for (int i = 0; i < nTests; i++)
        {
            var w = Stopwatch.StartNew();
            for (int j = 0; j < loops; j++)
                code();
            total += w.ElapsedMilliseconds;
        }
        Console.WriteLine("(avg) {0} \t {1}ms", msg, total / nTests);
    }

    private static void Main(string[] args)
    {
        var sample = new List<string> { "Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight" };
        //var sample = new[] { "Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight" };

        Measure("Linq Where", () =>    { var res = sample.Where(x => true && x.Length % 2 == 0); });
        Measure("Custom Where", () =>  { var res = sample.WHERE(x => true && x.Length % 2 == 0); });
        Measure("Linq Select", () =>   { var res = sample.Select(x => x.Length);                 });
        Measure("Custom Select", () => { var res = sample.SELECT(x => x.Length);                 });

    }
}

Время выборки:

(используя список)

(avg) Linq Where         102ms
(avg) Custom Where       62ms
(avg) Linq Select        122ms
(avg) Custom Select      59ms

(используя массив)

(avg) Linq Where         75ms
(avg) Custom Where       60ms
(avg) Linq Select        77ms
(avg) Custom Select      60ms

Пользовательские Any и All и оригиналы также выполнялись немного по-другому, хотя реализация практически такая же, меня интересуют только Where and Select

Мои вопросы:

  • Это заставляет меня сомневаться в том, как я это измеряю, правильно ли я это делаю? что-то не так с ним?
  • Почему эта простая реализация выглядит лучше, чем оригиналы? (почти в два раза быстрее со списками и немного быстрее с массивами)
  • При работе со списками, почему разница в производительности между пользовательской реализацией и LINQs больше, чем при работе с массивами?
  • 0
    Почему вы также подразумеваете создание экземпляров Stopwatch и само измерение?
  • 0
    Это не JIT { var res = sample.Where(x => true && x.Length % 2 == 0); } только один раз, верно? Это будет означать, что время должно быть исключено из вашего первого теста.
Показать ещё 1 комментарий
Теги:
linq
performance

2 ответа

6
Лучший ответ

Вы на самом деле не повторяете последовательности. Все, что вы тестируете, - это производительность создания счетчика; а не фактический цикл. В принципе, тест не имеет смысла.

Для получения информации я изменил это, чтобы:

Measure("Linq Where", () => { sample.Where(x => true && x.Length % 2 == 0).Consume(); });
Measure("Custom Where", () => { sample.WHERE(x => true && x.Length % 2 == 0).Consume(); });
Measure("Linq Select", () => { sample.Select(x => x.Length).Consume(); });
Measure("Custom Select", () => { sample.SELECT(x => x.Length).Consume(); });

с помощью:

public static void Consume<T>(this IEnumerable<T> source)
{
    using(var iter = source.GetEnumerator())
    {
        while (iter.MoveNext()) { }
    }
}

(который просто горит через итератор вручную), изменил loops на 500000 и повторил (режим выпуска, консоль и т.д.):

(avg) Linq Where         139ms
(avg) Custom Where       174ms
(avg) Linq Select        132ms
(avg) Custom Select      174ms

LINQ выигрывает.

  • 1
    Как заметил @CodesInChaos, также стоит отметить, что фактическая реализация LINQ делает больше вещей, то есть проверка параметров ... и т. Д.
  • 0
    @ ken2k LINQ все еще выигрывает; см обновление
Показать ещё 5 комментариев
1

Как отмечено другим, ваш тест ошибочен, потому что ни один из кода не выполняется, кроме создания счетчика (вы не реализуете последовательности). Но он также ошибочен по другой причине, вы много раз проверяете, но на чрезвычайно крошечных последовательностях! Попробуйте те же тесты на гораздо больше элементов!

  • 0
    но тестирование распределителя памяти - это весело; p

Ещё вопросы

Сообщество Overcoder
Наверх
Меню