Разбиение в 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 списком, и использует его, если может.

Другие вопросы по тегам