Ответ Бореалида включает в себя тест и выяснение , которое невозможно превзойти по совету.
Но, возможно, есть еще кое-что для тестирования, чем вы думаете: вы хотите, чтобы ваши потоки по возможности избегали конфликта данных. Если данные полностью доступны только для чтения, вы можете увидеть лучшую производительность, если ваши потоки обращаются к «похожим» данным - обязательно просматривая данные небольшими блоками за раз, поэтому каждый поток получает доступ к данным из одни и те же страницы снова и снова . Если данные полностью только для чтения, то нет проблем, если каждое ядро получит свою собственную копию строк кэша. (Хотя это может не максимально использовать кеш каждого ядра.)
Если данные каким-либо образом изменены, то вы увидите значительное улучшение производительности, если будете на большом расстоянии от отделять друг от друга. Большинство кэшей хранят данные вдоль строк кэша , и вы отчаянно хотите, чтобы каждая строка кэша не отскакивала между процессорами для обеспечения хорошей производительности. В этом случае вы можете захотеть, чтобы разные потоки работали с данными, которые на самом деле далеко друг от друга, чтобы никогда не сталкиваться друг с другом.
Итак: если вы обновляете данные во время работы с ними, я бы рекомендовал иметь N или 2 * N потоков выполнения (для N ядер), начиная их с SIZE / N * M в качестве отправной точки, для потоки от 0 до M. (0, 1000, 2000, 3000, для четырех потоков и 4000 объектов данных.) Это даст вам наилучшую возможность подачи различных строк кэша к каждому ядру и позволит обновлениям проходить без отскока строк кэша:
+--------------+---------------+--------------+---------------+--- ...
| first thread | second thread | third thread | fourth thread | first ...
+--------------+---------------+--------------+---------------+--- ...
Если вы не обновляете данные во время работы с ними, вы можете запустить N или 2 * N потоков выполнения (для N ядер), начиная с 0, 1, 2, 3 и т. Д. И перемещая каждый элемент вперед на N или 2 * N элементов с каждой итерацией. Это позволит кеш-системе извлекать каждую страницу из памяти по одному разу, заполнять кеш ЦП практически идентичными данными и, как мы надеемся, заполнять каждое ядро свежими данными.
+-----------------------------------------------------+
| 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 ... |
+-----------------------------------------------------+
Я также рекомендую использовать sched_setaffinity(2)
непосредственно в вашем коде для принудительного различных потоков к их собственным процессорам. По моему опыту, Linux стремится сохранить каждый поток на своем исходном процессоре настолько, чтобы не переносить задачи на другие ядра, которые в противном случае простаивают.