Порядок методов расширения LINQ не влияет на производительность?
Я удивлен, что, по-видимому, не имеет значения, добавлю ли я или добавлю методы расширения LINQ.
Протестировано Enumerable.FirstOrDefault
:
-
hugeList.Where(x => x.Text.Contains("10000")).FirstOrDefault();
-
hugeList.FirstOrDefault(x => x.Text.Contains("10000"));
var hugeList = Enumerable.Range(1, 50000000)
.Select(i => new { ID = i, Text = "Item" + i });
var sw1 = new System.Diagnostics.Stopwatch();
var sw2 = new System.Diagnostics.Stopwatch();
sw1.Start();
for(int i=0;i<1000;i++)
hugeList.Where(x => x.Text.Contains("10000")).FirstOrDefault();
sw1.Stop();
sw2.Start();
for(int i=0;i<1000;i++)
hugeList.FirstOrDefault(x => x.Text.Contains("10000"));
sw2.Stop();
var result1 = String.Format("FirstOrDefault after: {0} FirstOrDefault before: {1}", sw1.Elapsed, sw2.Elapsed);
//result1: FirstOrDefault after: 00:00:03.3169683 FirstOrDefault before: 00:00:03.0463219
sw2.Restart();
for (int i = 0; i < 1000; i++)
hugeList.FirstOrDefault(x => x.Text.Contains("10000"));
sw2.Stop();
sw1.Restart();
for (int i = 0; i < 1000; i++)
hugeList.Where(x => x.Text.Contains("10000")).FirstOrDefault();
sw1.Stop();
var result2 = String.Format("FirstOrDefault before: {0} FirstOrDefault after: {1}", sw2.Elapsed, sw1.Elapsed);
//result2: FirstOrDefault before: 00:00:03.6833079 FirstOrDefault after: 00:00:03.1675611
//average after:3.2422647 before: 3.3648149 (all seconds)
Я бы предположил, что было бы медленнее добавлять Where
, так как он должен найти все соответствующие элементы, а затем взять первый и предыдущий FirstOrDefault
может дать первый найденный элемент.
Q: Может кто-нибудь объяснить, почему я ошибаюсь?
Ответы
Ответ 1
Я бы предположил, что будет медленнее добавлять Where, так как он должен найти все соответствующие элементы, а затем взять первый и предшествующий FirstOrDefault, может дать первый найденный элемент. Может ли кто-нибудь объяснить, почему я ошибаюсь?
Вы ошибаетесь, потому что ваше первое утверждение просто неверно. Where
не требуется находить все совпадающие элементы перед получением первого совпадающего элемента. Where
выбирает соответствующие элементы "по запросу"; если вы только запрашиваете первый, он выбирает только первый. Если вы запрашиваете только первые два, он извлекает только первые два.
Джон Скит отлично играет на сцене. Представьте, что у вас три человека. У первого человека есть перетасованная карта. У второго человека есть футболка с надписью "где карта красная". Третий человек вторит второму человеку и говорит: "Дайте мне первую карту". Второй человек повторяет первого человека снова и снова, пока первый человек не раздаст красную карточку, которую второй человек передает третьему лицу. У второго человека нет причин продолжать выталкивать первого человека; задача выполнена!
Теперь, если футболка второго лица говорит "порядок по рангу по возрастанию", тогда у нас совсем другая ситуация. Теперь второй человек действительно должен получить каждую карту от первого лица, чтобы найти самую низкую карту в колоде, прежде чем передать первую карту третьему лицу.
Теперь это даст вам необходимую интуицию, чтобы рассказать, когда заказ имеет значение по соображениям производительности. Чистый результат "дать мне красные карточки, а затем отсортировать их" точно так же, как "сортировать все карты, а затем дать мне красные", но первый намного быстрее, потому что вам не нужно тратить время на сортировку черные карты, которые вы собираетесь сбросить.
Ответ 2
Метод Where()
использует отложенное выполнение и будет предоставлять следующий соответствующий элемент по мере его запроса. То есть Where()
не оценивает и сразу возвращает последовательность всех объектов-кандидатов, она предоставляет их по одному, поскольку они повторяются.
Так как FirstOrDefault()
останавливается после первого элемента, это приведет к тому, что Where()
также остановит итерацию.
Подумайте о FirstOrDefault()
как о прекращении выполнения Where()
, как если бы он выполнил break
. Это не так просто, конечно, но по существу, так как FirstOrDefault()
останавливает итерацию после того, как находит элемент, Where()
не нужно продолжать дальше.
Конечно, это в простом случае применения FirstOrDefault()
в предложении Where()
, если у вас есть другие предложения, в которых подразумевается необходимость рассмотрения всех элементов, это может иметь эффект, но это будет true как при использовании Where().FirstOrDefault()' combo or just
FirstOrDefault() 'с предикатом.