Добавление клонов в список происходит медленно - PullRequest
0 голосов
/ 26 сентября 2018

Я пытаюсь ускорить цикл, который клонирует 2_500_000 объектов.Сам клон берет 800 мс для всего цикла, но когда я добавляю их в список, это занимает 3 секунды ..

List<T> list = new List<T>();

Stopwatch sw = new Stopwatch();
sw.Start();

foreach(T entity in listSource)
{
    T entityCloned = GetEntityClone(entity); // Taking 800ms totally

    if (entityCloned != null)
        list.Add(entityCloned);
}

sw.Stop();

Можетпожалуйста, помогите мне выяснить, почему эти Add s занимают так много времени?

Ответы [ 3 ]

0 голосов
/ 26 сентября 2018

Я сэкономил некоторое время (около 33%), используя массив вместо списка:

Определение класса MyObject:

public class MyObject
{
    public int Id { get; set; }
    public bool Flag { get; set; }

    public static MyObject GetEntityClone(MyObject obj)
    {
        if (obj == null) return null;

        var newObj = new MyObject()
        {
            Id = obj.Id,
            Flag = obj.Flag
        };

        return newObj;
    }
}

Код:

var sourceList = new List<MyObject>();

// let's mock the source data, every 27th element will be null
for (int i = 0; i < 2500000; ++i)
{
    if (i % 27 != 0)
        sourceList.Add(new MyObject { Id = i, Flag = (i % 2 == 0) });
}

var destArray = new MyObject[2500000];

Stopwatch sw = new Stopwatch();
sw.Start();
Console.WriteLine(sw.ElapsedMilliseconds);

var currentElement = 0;
for (int i = 0; i < sourceList.Count; ++i)
{
    MyObject entityCloned = MyObject.GetEntityClone(sourceList[i]);

    if (entityCloned != null)
        destArray[currentElement++] = entityCloned;
}

var result = new MyObject[currentElement];
Array.Copy(destArray, 0, result, 0, currentElement);
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
0 голосов
/ 26 сентября 2018

Попробуйте выполнить параллельную работу:

ConcurrentBag<T> list = new ConcurrentBag<T>();
Parallel.ForEach(listSource, entity =>
{
    T entityCloned = GetEntityClone(entity); //Taking 800ms totally
    if (entityCloned != null)
        list.Add(entityCloned);

});

var listVersion = list.ToList();
0 голосов
/ 26 сентября 2018

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

Но вы потенциально можете улучшить скорость.

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

После выделения емкости сложность добавления в список должно составлять O(1), повторных выражений (которые являются O(n) сложностью см. Этот ответ ) не потребуется.Маловероятно, что добавление к списку в этом случае станет узким местом.

Вы также можете удалить нулевые значения из первоначального списка, из которого вы копируете заранее, чтобы исключить необходимость в операторе if, который должен оцениваться каждый раз.Использование linq :

var noNulls = listSource.where(o => o != null)

...