Я просто возился с внедрением некоторых методов 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
Мои вопросы:
Вы на самом деле не повторяете последовательности. Все, что вы тестируете, - это производительность создания счетчика; а не фактический цикл. В принципе, тест не имеет смысла.
Для получения информации я изменил это, чтобы:
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 выигрывает.
Как отмечено другим, ваш тест ошибочен, потому что ни один из кода не выполняется, кроме создания счетчика (вы не реализуете последовательности). Но он также ошибочен по другой причине, вы много раз проверяете, но на чрезвычайно крошечных последовательностях! Попробуйте те же тесты на гораздо больше элементов!
Stopwatch
и само измерение?{ var res = sample.Where(x => true && x.Length % 2 == 0); }
только один раз, верно? Это будет означать, что время должно быть исключено из вашего первого теста.