Подтвердить что ты не робот

Порядок методов расширения 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: Может кто-нибудь объяснить, почему я ошибаюсь?

4b9b3361

Ответ 1

Я бы предположил, что будет медленнее добавлять Where, так как он должен найти все соответствующие элементы, а затем взять первый и предшествующий FirstOrDefault, может дать первый найденный элемент. Может ли кто-нибудь объяснить, почему я ошибаюсь?

Вы ошибаетесь, потому что ваше первое утверждение просто неверно. Where не требуется находить все совпадающие элементы перед получением первого совпадающего элемента. Where выбирает соответствующие элементы "по запросу"; если вы только запрашиваете первый, он выбирает только первый. Если вы запрашиваете только первые два, он извлекает только первые два.

Джон Скит отлично играет на сцене. Представьте, что у вас три человека. У первого человека есть перетасованная карта. У второго человека есть футболка с надписью "где карта красная". Третий человек вторит второму человеку и говорит: "Дайте мне первую карту". Второй человек повторяет первого человека снова и снова, пока первый человек не раздаст красную карточку, которую второй человек передает третьему лицу. У второго человека нет причин продолжать выталкивать первого человека; задача выполнена!

Теперь, если футболка второго лица говорит "порядок по рангу по возрастанию", тогда у нас совсем другая ситуация. Теперь второй человек действительно должен получить каждую карту от первого лица, чтобы найти самую низкую карту в колоде, прежде чем передать первую карту третьему лицу.

Теперь это даст вам необходимую интуицию, чтобы рассказать, когда заказ имеет значение по соображениям производительности. Чистый результат "дать мне красные карточки, а затем отсортировать их" точно так же, как "сортировать все карты, а затем дать мне красные", но первый намного быстрее, потому что вам не нужно тратить время на сортировку черные карты, которые вы собираетесь сбросить.

Ответ 2

Метод Where() использует отложенное выполнение и будет предоставлять следующий соответствующий элемент по мере его запроса. То есть Where() не оценивает и сразу возвращает последовательность всех объектов-кандидатов, она предоставляет их по одному, поскольку они повторяются.

Так как FirstOrDefault() останавливается после первого элемента, это приведет к тому, что Where() также остановит итерацию.

Подумайте о FirstOrDefault() как о прекращении выполнения Where(), как если бы он выполнил break. Это не так просто, конечно, но по существу, так как FirstOrDefault() останавливает итерацию после того, как находит элемент, Where() не нужно продолжать дальше.

Конечно, это в простом случае применения FirstOrDefault() в предложении Where(), если у вас есть другие предложения, в которых подразумевается необходимость рассмотрения всех элементов, это может иметь эффект, но это будет true как при использовании Where().FirstOrDefault()' combo or just FirstOrDefault() 'с предикатом.