Выполнил студенты группы ИВТ-32 _________/Саранский П. С. /
Проверил доцент кафедры _________________/Караваева О. В./
Киров 2013
Этот мини-отчёт является доказательной иллюстрацией к моим словам о том, что выполнение одновременно более n высоко оптимизированных слабо зависимых друг от друга потоков на N-ядерном процессоре (с общей памятью и, разумеется, общими для всех ядер кэшами, шиной памяти и другими объектами, как и единым для всех планировщиком) губительно сказывается на суммарной производительности. Вероятно, из-за замены элементов общего кэша (промахов и вытеснений), частых переключений, неэффективностью предвыборок памяти по слишком большому количеству разных страниц (с меньшим полезным размером предвыборки, которую можно обработать за небольшой квант, отведённый на вычисление задачи при n>>N), а может чего-то ещё.
Испытания проводились не на моей машине, так как на ней слишком большое количество постоянно работающих фоновых задач, которые будут вызывать лишние переключения задач на ядре и вытеснения кэша. Взятый здесь на достаточно чистой машине процессор хоть и чуть староват, но поддерживает почти все современные технологии и обладает идентичной вычислительной производительностью с современными архитектурами ядра (Ivy Bridge, Haswell). HyperThreading отсутствует, всё распределение потоков между ядрами осуществляется только программным планировщиком.
Так же по экспериментальным данным можно видеть сильно отличающуюся повторяемость результатов когда n>N и очень хорошую повторяемость при n<=N. Это связано, скорее всего, с тем, что потоки всё-таки связаны по данным, поэтому при квазислучайном выборе планировщиком потока, которому будет дозволено выполнение на ядре, некоторые другие потоки будут простаивать из-за нехватки данных. Вероятно, это является ещё одной (довольно веской) причиной падения суммарной полезной производительности при n>N.
Приоритет вычислительных потоков во всех тестах установлен на 24 (Realtime), в этом можно убедиться и с помощью сторонних средств, однако в случае приоритета 24 это не представляется возможным из-за катастрофического снижения отзывчивости системы при n>=N. Однако при малом количестве потоков это отлично видно J
Рис. 1. Приоритет потока равен 24.
Рис. 2. Отличная повторяемость результата (гигафлопсы) при n<N.
Рис. 3. При увеличении размера задачи полезная производительность немного возрастает (в связи с меньшей тратой времени на неоптимизированную генерацию начальных данных).
Далее иллюстрации идут без номеров и пояснений, где это не требуется.
Сравнение полезной производительности при разных объёмах задачи. Пояснения к этой ситуации даны несколько иллюстраций назад. Отчасти, на мой взгляд, это связано ещё и с тем, что при частом освобождении процессора для фоновых задач, они накапливаются и успевают неплохо повыполняться в небольших перерывах между небольшими задачами. При крупном размере задачи и её длительном выполнении, система почти не даёт квантов времени фоновым задачам и они, вот неожиданность, работают меньше! ;) Шутки шутками, а дело обстоит ещё в I/O и некоторых блокировках. Поэтому, на мой взгляд, если почти не давать фоновым задачам выполняться, то полезная однозадачная (точнее, фактически, одна задача на ядро в течение долгого времени) эффективность выполнения будет несколько больше, и это связано не только с контекст-свитчами и вытеснением кэша, а и с некоторыми не-вычислительными ресурсами.
Повторяемость результата, а точнее, разброс, упала довольно низко. Максимальный разброс значений производительности на десяти измерениях составляет 23.5% от значения максимальной производительности.
Вероятно, если значение производительности вычисляется не самим вычисляющим потоком, а каким-то другим (или вовсе другой задачей, например, оболочкой), то при огромной очереди (64:priority=24 + все фоновые задачи) может иметь место серьёзная погрешность в измерении времени. (Таймер? Не слышал! До него ещё нужно достучаться, получив квант времени).
Опять большой разброс.
Полезная производительность упала больше, чем в два раза (при увеличении количества потоков в 16 раз по сравнению с количеством физических ядер). К слову, доступ на тестовой машине к памяти был одноканальный, частота памяти 1600MHz, CL9. Одноканальность тоже внесла свою лепту.
Вывод
который требовалось показать.
Эмпирическое правило о том, что количество потоков n могло бы для хорошей работы составлять 10*N, где N – количество исполнительных узлов (ядер), верно в случае большого количества неравномерно связанных между собой не очень высоко нагруженных задач и подзадач.
В случае же хорошей оптимизации кода каждой подзадачи, слабых зависимостях одной подзадачи от другой и отсутствии проблем в параллельном доступе к памяти, желательно уравнивание количества потоков и физических узлов исполнения (ядер, например, мультипроцессоров, узлов NUMA или ещё чего-то, на чём это исполняется).