У Linq есть штраф за запуск? - PullRequest
0 голосов
/ 05 ноября 2018

Мне нужно либо использовать запросы Linq, либо перебирать данные более 100 000 в приложении. Скорость будет иметь важное значение. Я написал тест и не получил ожидаемых результатов. По сути, я передаю одни и те же данные двум разным функциям в цикле 100 раз. Функции аналогичны, за исключением того, что в одном я использую несколько запросов Linq, а в другом я итерирую данные вручную для построения информации. Код выглядит так:

Версия Linq:

            //Get max and min of each
        double maxX = (from node in pointCloud
                       select node.Node.Value.X).Max();
        double maxY = (from node in pointCloud
                       select node.Node.Value.Y).Max();
        double maxZ = (from node in pointCloud
                       select node.Node.Value.Z).Max();
        double minX = (from node in pointCloud
                       select node.Node.Value.X).Min();
        double minY = (from node in pointCloud
                       select node.Node.Value.Y).Min();
        double minZ = (from node in pointCloud
                       select node.Node.Value.Z).Min();
        //Extract all the x, y and z values into arrays
        double[] x = (from node in pointCloud
                      select node.Node.Value.X).ToArray();
        double[] y = (from node in pointCloud
                      select node.Node.Value.Y).ToArray();
        double[] z = (from node in pointCloud
                      select node.Node.Value.Z).ToArray();

VS:

Ручная версия:

        //Get max and min of each
        double maxX = double.MinValue;
        double maxY = double.MinValue;
        double maxZ = double.MinValue;
        double minX = double.MaxValue;
        double minY = double.MaxValue;
        double minZ = double.MaxValue;

        List<double> x = new List<double>();
        List<double> y = new List<double>();
        List<double> z = new List<double>();

        foreach (NodeDistance<KDTreeNode<g.Point3d>> node in pointCloud)
        {
            maxX = msf.Max(maxX, node.Node.Value.X);
            maxY = msf.Max(maxY, node.Node.Value.Y);
            maxZ = msf.Max(maxZ, node.Node.Value.Z);
            minX = msf.Min(minX, node.Node.Value.X);
            minY = msf.Min(minY, node.Node.Value.Y);
            minZ = msf.Min(minZ, node.Node.Value.Z);
            x.Add(node.Node.Value.X);
            y.Add(node.Node.Value.Y);
            z.Add(node.Node.Value.Z);
        }

Вот загадка. Когда запускается версия Linq, это занимает намного больше времени в первый раз. Я запускаю секундомер перед тем, как начать цикл, а затем записываю прошедшее время после каждого запуска функции. Вот первые 5 раз, когда запускается функция Linq:

00:00:00.0425169 (after 1st run)
00:00:00.0433850
00:00:00.0437312
00:00:00.0440666
00:00:00.0443969
....
00:00:00.1352192 (Total time for all 100 executions)

Когда я запускаю версию с ручной итерацией, первые пять раз выглядят так:

00:00:00.0124269 (after 1st run)
00:00:00.0138497
00:00:00.0152502
00:00:00.0166348
00:00:00.0180180
....
00:00:00.1060389 (Total time for all 100 executions)

У меня будет 30 или 40 разных запросов Linq, которые будут выполняться в разное время. Данные не все поставлены в очередь, и один и тот же запрос выполняется снова и снова. Если я запускаю запрос Linq, а затем выполняю другие действия, и каждый раз, когда я запускаю запрос, это занимает по существу 0,04 секунды, тогда приложение будет работать очень медленно. Если при первом запуске ЛЮБОГО запроса Linq требуется 0,04 секунды, а затем это время больше не повторяется для этого приложения, тогда будет лучше использовать Linq.

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

Ответы [ 2 ]

0 голосов
/ 05 ноября 2018

Вы писали:

Данные не все поставлены в очередь, и один и тот же запрос выполняется снова и снова.

Если вы выполните запрос LINQ несколько раз с одним и тем же вводом, разумнее сохранить результаты.

var cityGroups = persons.GroupBy (person => person.City);

Если вы посмотрите на источник ссылок в Enumerable.GroupBy , вы увидите, что при запросе первого элемента перечисляется полная исходная последовательность для помещения элементов в таблицу поиска. Поэтому, если вы выполните повторное перечисление, таблица подстановок будет создана снова:

Поэтому, если вам нужно перечислить несколько раз, разумно выполнить запрос и сохранить результаты таким образом, чтобы повысить эффективность при повторном перечислении:

var result = cityGroups.ToList();
foreach (var cityGroup in cityGroups)
{
    DoSomething(cityGroup};
}
foreach (var cityGroup in cityGroups)
{
     DoSomethinElse(cityGroup);
}

Даже если вы не просто перечислите, а добавите несколько операторов LINQ после него, возможно, было бы целесообразно сохранить промежуточные результаты:

var newYorkers = cityGroups.Where(cityGroup => cityGroup.Key == "New York").ToList();
var ghostTowns = cityGroups.Where(cityGroup => !cityGroup.Any()).ToList();

Таблица поиска будет создана дважды. Если вы выполняете ToList () перед расширением результата, таблица поиска будет создана только один раз

var result = cityGroups.ToList();
var newYorkers = result.Where(cityGroup => cityGroup.Key == "New York").ToList();
var ghostTowns = result.Where(cityGroup => !cityGroup.Any()).ToList();

Мой совет: если вы будете выполнять один и тот же запрос снова и снова, создайте ToList (или To-независимо от того, что вам нужно).

0 голосов
/ 05 ноября 2018

Я не могу вспомнить ни одного случая, когда "прямой C #" не был бы по крайней мере столь же быстрым, как LINQ. Определенно есть возможности, когда LINQ может быть заметно медленнее .

Так что, если "скорость" имеет решающее значение ... тогда вам лучше не использовать выражения LINQ.

ВАЖНЫЕ ТОЧКИ:

  1. Обязательно отметьте

    <= Я не уверен, что приведенный выше пример действительно является верным тестом </p>

  2. Обязательно сравните с большими наборами данных

    <= Я столкнулся с очень невинным на вид выражением LINQ пару лет назад (написанным кем-то другим), которое выглядело нормально ... и хорошо работало для нескольких элементов ... но стало экспоненциально медленнее и в конечном итоге взорвалось переполнение стека (без каламбура) ... с тысячами предметов ... </p>

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...