2013-02-15 2 views
27

Очень короткий вопрос. У меня есть случайный сортированный массив больших строк (100K + entries), где я хочу найти первое заполнение нужной строки. У меня есть два решения.For vs. Linq - Производительность против будущего

Из того, что я могу догадаться, что «цикл цикла» в настоящее время дает немного лучшую производительность (но этот марж всегда может измениться), но я также считаю, что версия linq более читаема. На балансе, какой метод обычно считается лучшей лучшей практикой кодирования и почему?

string matchString = "dsf897sdf78"; 
int matchIndex = -1; 
for(int i=0; i<array.length; i++) 
{ 
    if(array[i]==matchString) 
    { 
     matchIndex = i; 
     break; 
    } 
} 

или

int matchIndex = array.Select((r, i) => new { value = r, index = i }) 
         .Where(t => t.value == matchString) 
         .Select(s => s.index).First(); 
+0

Связанный: [для vs. foreach против LINQ] (http://programmers.stackexchange.com/questions/178218/for-vs-foreach-vs-linq) – sloth

+3

Я бы даже не использовал LINQ в этом case, так как вам действительно нужно бороться, чтобы найти индекс - я бы использовал 'Array.IndexOf' :) – Rawling

+0

Я использую LINQ для больших данных (100k + записей, ~ 40 столбцов) без каких-либо проблем с производительностью. – Teejay

ответ

41

Лучше всего зависит от того, что вам нужно:

  1. скорость развития и ремонтопригодность: LINQ
  2. Производительность (в соответствии с инструментами профилирования): ручной код

LINQ действительно замедляет работу со всей косвенностью. Не беспокойтесь об этом, так как 99% вашего кода не влияет на производительность конечного пользователя.

Я начал с C++ и действительно научился оптимизировать кусок кода. LINQ не подходит для максимальной загрузки вашего процессора. Поэтому, если вы измеряете запрос LINQ, чтобы быть проблемой, просто выровняйте его. Но только тогда.

Для вашего образца кода я бы оценил 3-кратное замедление. Распределения (и последующие GC!) ​​И пропуски через лямбды действительно болят.

+0

Согласен. Linq поставляется с небольшой стоимостью, но во многих случаях это небрежно. На самом деле, из того, что я помню, большая часть кода, стоящего за StackOverflow, использует Linq –

+0

+1 и хочу добавить, что только 20% кода работает 80% времени, поэтому только узкие места должны быть оптимизированы, если есть проблемы с производительностью –

+1

+ 1 99,9% прямое хорошее объяснение ':)' – spajce

2

Ну, вы сами ответили на свой вопрос.

Пойдите с петлей For, если вы хотите получить наилучшую производительность, или зайдите с Linq, если хотите читать.

Также возможно иметь возможность использовать Parallel.Foreach(), который будет полезен из линейных лямбда-выражений (так, что ближе к Linq), и это намного читаемо, а затем делает паралификацию «вручную».

+1

Я всегда задавался вопросом, почему LINQ и лямбда-выражения автоматически считаются более читаемыми. Иногда простой foreach или для более читабельны, чем LINQ IMO –

+0

@LeeDale, конечно. И я хотел бы добавить, что мой ответ касался макета Linq в стиле Fluent, как в вопросе, а не декларативного стиля. – dutzu

1

Я не думаю, что это считается лучшей практикой, которую некоторые предпочитают смотреть на LINQ, а некоторые нет.

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

Также вы подумали о том, как использовать PLINQ или сделать цикл параллельным?

+0

+1 для PLINQ ': D' – spajce

2

Всегда существует дилемма между производительностью и ремонтопригодностью. И обычно (если нет конкретных требований к производительности), то должна поддерживаться ремонтопригодность. Только если у вас проблемы с производительностью, тогда вы должны профилировать приложение, найти источник проблем и улучшить его производительность (за счет сокращения ремонтопригодности в то же время, да, это мир, в котором мы живем).

О вашем образце. Linq - это не очень хорошее решение, потому что оно не добавляет совместимость соответствия вашему коду. На самом деле для меня проецирование, фильтрация и проецирование снова выглядят еще хуже, чем простой цикл. Здесь вам нужен простой массив.IndexOf, который становится более сопровождаемым, чем петли, и имеют почти такую ​​же производительность:

Array.IndexOf(array, matchString) 
24

Слегка лучшую производительность? Цикл даст ЗНАЧИТЕЛЬНО лучшую производительность!

Рассмотрите приведенный ниже код. В моей системе для RELEASE (не отладки) сборки, это дает:

Found via loop at index 999999 in 00:00:00.2782047 
Found via linq at index 999999 in 00:00:02.5864703 
Loop was 9.29700432810805 times faster than linq. 

Код намеренно настроен так, что элемент можно найти прямо в конце. Если бы это было в самом начале, все было бы совсем по-другому.

using System; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Linq; 

namespace Demo 
{ 
    public static class Program 
    { 
     private static void Main(string[] args) 
     { 
      string[] a = new string[1000000]; 

      for (int i = 0; i < a.Length; ++i) 
      { 
       a[i] = "Won't be found"; 
      } 

      string matchString = "Will be found"; 

      a[a.Length - 1] = "Will be found"; 

      const int COUNT = 100; 

      var sw = Stopwatch.StartNew(); 
      int matchIndex = -1; 

      for (int outer = 0; outer < COUNT; ++outer) 
      { 
       for (int i = 0; i < a.Length; i++) 
       { 
        if (a[i] == matchString) 
        { 
         matchIndex = i; 
         break; 
        } 
       } 
      } 

      sw.Stop(); 
      Console.WriteLine("Found via loop at index " + matchIndex + " in " + sw.Elapsed); 
      double loopTime = sw.Elapsed.TotalSeconds; 

      sw.Restart(); 

      for (int outer = 0; outer < COUNT; ++outer) 
      { 
       matchIndex = a.Select((r, i) => new { value = r, index = i }) 
          .Where(t => t.value == matchString) 
          .Select(s => s.index).First(); 
      } 

      sw.Stop(); 
      Console.WriteLine("Found via linq at index " + matchIndex + " in " + sw.Elapsed); 
      double linqTime = sw.Elapsed.TotalSeconds; 

      Console.WriteLine("Loop was {0} times faster than linq.", linqTime/loopTime); 
     } 
    } 
} 
+4

+1 для усилий': D' – spajce

+2

Проблема - новый оператор, который замедляет запрос linq. Если массив можно преобразовать в список, а linq можно комбинировать с FindIndex, и на этот раз цикл for будет только примерно в 1,5 раза быстрее. 'matchIndex = a.ToList(). FindIndex (x => x.Equals (matchString));' – JohnCambell

+0

, изменяя ваш запрос на что-то более близкое к регулярному циклу, резко уменьшает разницу: 'string tst = a.First (s => matchIndex ++! = - 2 && s == matchString);' – jmoreno

3

LINQ, согласно декларативной парадигме, выражает логику вычисления без описания его потока управления. Запрос ориентирован на цели, самоопределяется и, таким образом, легко анализируется и понимается. Является также кратким. Более того, используя LINQ, одно зависит от абстракции структуры данных. Это связано с высокой скоростью и возможностью повторного использования.

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

Смежные вопросы