Есть ли проблемы с этим foreach? (Ошибка, производительность, ...) - PullRequest
0 голосов
/ 08 апреля 2020

Я нашел код ниже в старом проекте, и я чувствую, что это может быть не очень хорошая производительность, но я не уверен в этом. Кто-нибудь может объяснить это для меня? Большое спасибо.

Я отредактировал код ниже. Я имею в виду, что у него есть 2 объекта.

var obj = ...; // Object; 
var obj.ListObject_1  = ...; //List<Object>;

var obj2 = ...; // Object; 
var obj2.ListObject_2 = ...; //List<Object>;

foreach (var value in obj != null ? obj.ListObject_1.OrderBy(x => x.displayOrderField).ToArray() : obj2.ListObject_2.OrderBy(x => x.displayOrderField).ToArray()) {

}

Ответы [ 2 ]

2 голосов
/ 08 апреля 2020

Я чувствую, что это может быть не очень хорошая производительность, но я не уверен в этом

Вы можете улучшить производительность, убрав вызов ToArray() - вы по сути повторяя коллекцию один раз в вашем вызове ToArray(), а затем повторяя ее снова в вашем foreach l oop.

С точки зрения производительности троичный оператор в порядке, он оценит условие obj != null, а затем, в зависимости от значения, будет оцениваться один из других операндов - он не оценивает оба obj.ListObject_1.OrderBy(x => x.displayOrderField).ToArray() и obj.ListObject_1.OrderBy(x => x.displayOrderField).ToArray().

Редактировать: завершить переписать после @ Luân Đình опечатки ключа исправления в OP

0 голосов
/ 10 апреля 2020

Говоря о производительности, недостаточно просто «говорить», нужно измерять вещи. Так что давайте просто сделаем это.

Я сделал довольно простой тест цикла с использованием различных конструкций и поиска суммы всех чисел внутри. Обратите внимание, что «дополнительная работа» (поиск суммы сбора) не влияет на показатель, поскольку в каждом тесте выполняется одно и то же.


[SimpleJob(RuntimeMoniker.CoreRt31)] // .net core 3.1
public class StackOverflowPerformance
{
    private const int Size = 1_000_000; // 8MB cache, 1 mil ints * 4 byte each = 4MB
                                        // hopefully this will fit into the processor cache
    private List<int> _sut = new List<int>(Size); // assuming you work with List

    [GlobalSetup]
    public void Setup()
    {
        // achieve worst case scenario for our OrderBy
        for (int i = Size; i > 0; --i)
        {
            _sut.Add(i);
        }
    }

    [Benchmark]
    public int ForeachToArray()
    {
        var result = 0;
        foreach(var value in _sut.OrderBy(x => x).ToArray()) // change the underlying structure type
        {
            result += value;
        }

        return result;
    }

    [Benchmark]
    public int ForeachToList()
    {
        var result = 0;
        foreach (var value in _sut.OrderBy(x => x).ToList()) // preserve the underlying structure type
        {
            result += value;
        }

        return result;
    }

    [Benchmark]
    public int Foreach()
    {
        var result = 0;
        foreach (var value in _sut.OrderBy(x => x)) // OrderBy returns IEnumerable<int> this will
                                                    // most likely introduce virtual calls

        {
            result += value;
        }

        return result;
    }

    [Benchmark(Baseline = true)]
    public int For()
    {
        var result = 0;
        _sut = _sut.OrderBy(x => x).ToList(); // preserve the underlying type of the structure
        for (int i = 0; i < _sut.Count; i++)
        {
            result += _sut[i];
        }

        return result;
    }

}

И вот результаты : benchmarks


Поэтому я предлагаю следующее:

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

Действительно хороший глубокий анализ производительности l oop в C#

Следующая вещь, как говорили другие, не звонить ToArray() или ToList() если вам не нужно использовать конкретный тип для чего-то связанного с ним, например, вы можете вызывать Find только для List<T>, потому что это приведет к дополнительным накладным расходам, таким как повторение цикла по l oop еще раз.

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