2016-12-19 1 views
4

Я написал метод для разбивки списка элементов на несколько списков с использованием System.Linq. Когда я запускаю этот метод для 50000 простых целых чисел, он принимает около 59.862 секунд.с выпуском System.Linq при разбиении списка на несколько списков

Stopwatch watchresult0 = new Stopwatch(); 
watchresult0.Start(); 
var result0 = SubDivideListLinq(Enumerable.Range(0, 50000), 100).ToList(); 
watchresult0.Stop(); 
long elapsedresult0 = watchresult0.ElapsedMilliseconds; 

Так что я пытался форсировать его, и написал его с простой итерации цикла по каждому пункту в моем списке, и это нужно только 4 миллисекунды:

Stopwatch watchresult1 = new Stopwatch(); 
watchresult1.Start(); 
var result1 = SubDivideList(Enumerable.Range(0, 50000), 100).ToList(); 
watchresult1.Stop(); 
long elapsedresult1 = watchresult1.ElapsedMilliseconds; 

Это мой Subdivide-метод с помощью Linq:

private static IEnumerable<List<T>> SubDivideListLinq<T>(IEnumerable<T> enumerable, int count) 
{ 
    while (enumerable.Any()) 
    { 
     yield return enumerable.Take(count).ToList(); 
     enumerable = enumerable.Skip(count); 
    } 
} 

И это мой Subdivide-метод с foreach цикла по каждому пункту:

private static IEnumerable<List<T>> SubDivideList<T>(IEnumerable<T> enumerable, int count) 
{ 
    List<T> allItems = enumerable.ToList(); 

    List<T> items = new List<T>(count); 
    foreach (T item in allItems) 
    { 
     items.Add(item); 

     if (items.Count != count) continue; 
     yield return items; 
     items = new List<T>(count); 
    } 

    if (items.Any()) 
     yield return items; 
} 

у вас есть идеи, почему моя собственная реализация намного быстрее, чем разделение на Linq? Или я делаю что-то неправильно?

И: Как вы можете видеть, я знаю, как разделять списки, так что это не дублированный связанный с этим вопрос. Я хотел знать о производительности между linq и моей реализацией. Не как разделить-списки

+2

Версия LINQ будет выполнять запрос дважды на итерацию. Он также будет иметь пустые циклы, которые всегда должны найти последнюю позицию, тогда как ваш оптимизированный метод может продолжить обработку. Он также будет инициализировать список с правильным размером, тогда как LINQ должен каждый раз изменять размер внутреннего массива. –

+1

также не использовать 'while (перечисляемый.Any())'. с некоторыми итераторами вы можете пропустить значения. вы должны либо безопасно получить все значения, например, используя 'foreach', либо использовать' MoveNext' и 'Current' –

+0

, но' enumerable.Any() 'возвращает true/false, когда' перечисляемый' присваивается элементам 'Skip'ped , Зачем мне скучать по ценностям здесь? –

ответ

0

Если вы после читаемости и производительности, вы можете использовать этот алгоритм. с точки зрения скорости этот действительно близок к вашей версии, отличной от linq. в то же время его гораздо читабельнее.

private static IEnumerable<List<T>> SubDivideListLinq<T>(IEnumerable<T> enumerable, int count) 
{ 
    int index = 0; 
    return enumerable.GroupBy(l => index++/count).Select(l => l.ToList()); 
} 

И его альтернатива:

private static IEnumerable<List<T>> SubDivideListLinq<T>(IEnumerable<T> enumerable, int count) 
{ 
    int index = 0; 
    return from l in enumerable 
     group l by index++/count 
     into l select l.ToList(); 
} 

Другой вариант:

private static IEnumerable<List<T>> SubDivideListLinq<T>(IEnumerable<T> enumerable, int count) 
{ 
    int index = 0; 
    return enumerable.GroupBy(l => index++/count, 
          item => item, 
          (key,result) => result.ToList()); 
} 

В моем компьютере я получаю linq 0.006 sec против non-linq 0.002 sec который является совершенно справедливым и приемлемым для использования LINQ.

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

+1

Да, вы совершенно правы, и я люблю использовать linq. разница в 0,004 секунды в порядке. разница в ~ 1 минута - нет, вот почему я начал вопрос :) это выглядит гораздо более читаемым и довольно приятным. С вашими комментариями выше и вашими ответами, я собираюсь принять ваш ответ и использовать его. –

+0

Теперь вы оба разрушили исходное сообщение. Я участвовал в его повторном открытии как дублирующем, так как речь шла о конкретных показателях выполнения. Этот ответ и прием возвращают его к дублированию, в котором содержится много ответов ** на то, как ** выполнять разделение, включая аналогичные (или точно такие же). Если вы считаете, что этот ответ добавляет что-то к теме, пойдите и опубликуйте ее под вопросом, который был идентифицирован как дубликат. Он определенно не отвечает ** этот пост ** вопрос. –

1

Если кто-то здесь, с тем же вопросом:

Итак, наконец я сделал некоторые дополнительные исследования и обнаружили, что многократное перечисление с System.Linq является причиной исполнения:

Когда я перечисляя его в массив, чтобы избежать многократного перечисления, производительность становится намного лучше (14 мс/50k элементы):

T[] allItems = enumerable as T[] ?? enumerable.ToArray(); 
while (allItems.Any()) 
{ 
    yield return allItems.Take(count); 
    allItems = allItems.Skip(count).ToArray(); 
} 

Тем не менее, я не буду использовать Linq подход, так как это помедленнее. Вместо этого я написал расширение-метод подразделяют свои списки, и это занимает 3 мс для 50k пунктов:

public static class EnumerableExtensions 
{ 
    public static IEnumerable<List<T>> Subdivide<T>(this IEnumerable<T> enumerable, int count) 
    { 

     List<T> items = new List<T>(count); 
     int index = 0; 
     foreach (T item in enumerable) 
     { 
      items.Add(item); 
      index++; 
      if (index != count) continue; 
      yield return items; 
      items = new List<T>(count); 
      index = 0; 
     } 
     if (index != 0 && items.Any()) 
      yield return items; 
    } 
} 

Как @AndreasNiedermair уже писал, это также содержится в MoreLinq -Library, называется Batch. (Но я не буду добавлять библиотеку сейчас только для этого метода)

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