Разбиение в TPL с использованием Partitioner
Я вижу небольшую разницу в производительности между следующими двумя функционально похожими кодами, я надеюсь, что кто-то сможет помочь мне понять, почему есть разница.
//Case 1 Faster
Parallel.ForEach(data, x => func(x))
//Case 2 Slower
Parallel.ForEach(Partitioner.Create(data), x => func(x))
данные имеют тип List
Насколько я понимаю, разделение по умолчанию в первом случае также будет похоже на Partitioner.Create(data), поэтому не должно быть никакой разницы в производительности.
Есть ли способ выяснить, как происходит разбиение во время выполнения?
1 ответ
Я отвечаю на свой вопрос, на случай, если кто-то задумается об этом.
Я написал новый класс MyList, унаследованный от IList и реализующий все методы как обертки вокруг экземпляра списка вместе с дополнительным Console.WriteLine для отладки.
Интересно, что в первом случае, даже если я передаю ему экземпляр IEnumerable, он, похоже, выясняет, является ли он списком внизу и вызывает ли он функции индексатора List. В то время как во втором случае он вызывает GetEnumerator, который, я полагаю, медленнее из-за вызовов функций и синхронизации, требуемой для перечислимых элементов. То же самое происходит в первом случае, если я передаю data.Select(x=>x) вместо data.
Я предполагаю, что реализация параллельной программы пытается выяснить, является ли IEnumerable списком, и использует его, если может.