Я пытаюсь понять на высоком уровне, как отдельные потоки работают на нескольких ядрах. Ниже мое лучшее понимание. Я не верю, что это правильно, хотя.
Основываясь на моем чтении Hyper-threading , кажется, что ОС организует инструкции всех потоков таким образом, что они не ожидают друг друга. Затем интерфейс ЦП дополнительно организует эти инструкции, распределяя один поток по каждому ядру, и распределяет независимые инструкции из каждого потока по всем открытым циклам.
Таким образом, если существует только один поток, то ОС не будет выполнять какую-либо оптимизацию. Тем не менее, внешний интерфейс ЦП будет распределять независимые наборы команд между каждым ядром.
Согласно https://stackoverflow.com/a/15936270 , конкретный язык программирования может создавать больше или меньше потоков, но это не имеет значения при определении того, что делать с этими потоками. ОС и процессор справляются с этим, так что это происходит независимо от используемого языка программирования.
Просто для пояснения, я спрашиваю об одном потоке, запущенном на нескольких ядрах, а не о запуске нескольких потоков на одном ядре.
Что не так с моим резюме? Где и как разделены инструкции потока между несколькими ядрами? Имеет ли значение язык программирования? Я знаю, что это широкая тема; Я надеюсь на понимание на высоком уровне.
источник
Ответы:
Операционная система предлагает интервалы времени ЦП для потоков, которые могут работать.
Если имеется только одно ядро, то операционная система планирует наиболее подходящий поток для запуска на этом ядре в течение отрезка времени. После того, как отрезок времени завершен, или когда работающий поток блокируется на IO, или когда процессор прерывается внешними событиями, операционная система переоценивает, какой поток будет запущен следующим (и она может снова выбрать тот же поток или другой).
Право на запуск состоит из вариаций честности, приоритета и готовности, и с помощью этого метода различные потоки получают временные интервалы, некоторые больше, чем другие.
Если имеется несколько ядер, N, то операционная система планирует запуск наиболее подходящих N потоков на ядрах.
Процессор Affinity является соображением эффективности. Каждый раз, когда процессор запускает другой поток, чем раньше, он имеет тенденцию немного замедляться, потому что его кэш теплый для предыдущего потока, но холодный для нового. Таким образом, запуск одного и того же потока на одном и том же процессоре в течение нескольких временных интервалов является преимуществом эффективности.
Однако операционная система может свободно предлагать временные интервалы одного потока на разных процессорах, и она может вращаться через все процессоры на разных временных интервалах. Однако, как говорит @ gnasher729 , он не может запускать один поток одновременно на нескольких процессорах.
Гиперпоточность - это аппаратный метод, с помощью которого одно расширенное ядро ЦП может поддерживать выполнение двух или более разных потоков одновременно. (Такой ЦП может предлагать дополнительные потоки при более низкой стоимости в кремниевой области, чем дополнительные полные ядра.) Это расширенное ядро ЦП должно поддерживать дополнительное состояние для других потоков, например значения регистров ЦП, а также имеет состояние и поведение координации, которые обеспечивает совместное использование функциональных блоков в этом процессоре без объединения потоков.
Гиперпоточность, хотя и технически сложная с точки зрения аппаратного обеспечения, с точки зрения программиста, модель выполнения - это просто модель дополнительных ядер ЦП, а не что-то более сложное. Таким образом, операционная система видит дополнительные ядра ЦП, хотя есть некоторые новые проблемы сродства процессоров, поскольку несколько потоков с многопоточностью совместно используют архитектуру кэша одного ядра ЦП.
Мы можем наивно думать, что два потока, работающие на ядре с многопоточностью, работают в два раза быстрее, чем каждый со своим собственным полным ядром. Но это не обязательно так, потому что выполнение одного потока полно свободных циклов, и некоторое количество из них может использоваться другим потоком с гиперпоточностью. Кроме того, даже во время циклов без провисания один поток может использовать другие функциональные блоки, чем другой, поэтому может происходить одновременное выполнение. Усовершенствованный ЦП для гиперпоточности может иметь несколько более интенсивно используемых функциональных блоков, специально предназначенных для этого.
источник
Нет такой вещи, как один поток, работающий на нескольких ядрах одновременно.
Однако это не означает, что инструкции из одного потока не могут выполняться параллельно. Существуют механизмы, называемые конвейерной обработкой команд и выполнением вне порядка, которые позволяют это. Каждое ядро имеет много избыточных ресурсов, которые не используются простыми инструкциями, поэтому несколько таких инструкций можно запускать вместе (при условии, что следующее не зависит от предыдущего результата). Тем не менее, это все еще происходит внутри одного ядра.
Гиперпоточность является своего рода крайним вариантом этой идеи, когда одно ядро не только выполняет инструкции из одного потока параллельно, но и смешивает инструкции из двух разных потоков для дальнейшей оптимизации использования ресурсов.
Связанные записи в Википедии: конвейеризация инструкций , выполнение не по порядку .
источник
a[i] = b[i] + c[i]
цикле, каждая итерация независима, поэтому загрузка, добавление и сохранение из разных итераций могут выполняться сразу. Это должно сохранить иллюзию того, что инструкции выполняются в программном порядке, но, например, хранилище, которое отсутствует в кеше, не задерживает поток (пока не закончится место в буфере хранилища).итоги: поиск и использование параллелизма (на уровне команд) в однопоточной программе выполняется исключительно аппаратно, ядром процессора, на котором он работает. И только через окно из пары сотен инструкций, а не масштабного переупорядочения.
Однопоточные программы не получают никаких преимуществ от многоядерных процессоров, за исключением того, что другие вещи могут работать на других ядрах вместо того, чтобы отнимать время от однопоточной задачи.
ОС НЕ просматривает потоки команд потоков. Это только планирует потоки к ядрам.
Фактически, каждое ядро выполняет функцию планировщика ОС, когда ему нужно выяснить, что делать дальше. Планирование является распределенным алгоритмом. Чтобы лучше понять многоядерные машины, представьте, что каждое ядро работает отдельно. Как и многопоточная программа, ядро написано так, что его код на одном ядре может безопасно взаимодействовать с его кодом на других ядрах для обновления общих структур данных (например, списка потоков, готовых к запуску).
В любом случае, ОС помогает многопоточным процессам использовать параллелизм на уровне потоков, который должен быть явно показан при написании многопоточной программы вручную . (Или компилятором с автоматическим распараллеливанием с OpenMP или чем-то еще).
Ядро ЦП выполняет только один поток инструкций, если оно не остановлено (спит до следующего прерывания, например, прерывание по таймеру). Часто это поток, но это также может быть обработчик прерываний ядра или другой код ядра, если ядро решило сделать что-то иное, чем просто возврат к предыдущему потоку после обработки и прерывания или системного вызова.
В HyperThreading или других конструкциях SMT физическое ядро ЦП действует как несколько «логических» ядер. Единственное различие с точки зрения ОС между четырехъядерным процессором с гиперпоточностью (4c8t) и обычным 8-ядерным компьютером (8c8t) заключается в том, что ОС с поддержкой HT будет пытаться планировать потоки для разделения физических ядер, чтобы они не конкурировать друг с другом. Операционная система, которая не знала о гиперпоточности, просто увидела бы 8 ядер (если вы не отключите HT в BIOS, она обнаружит только 4).
Термин « внешний интерфейс» относится к части ядра ЦП, которая выбирает машинный код, декодирует инструкции и выдает их в неупорядоченную часть ядра . Каждое ядро имеет свой собственный интерфейс и является частью ядра в целом. Инструкции, которые он извлекает, - это то, что процессор в настоящее время работает.
Внутри неупорядоченной части ядра инструкции (или мопы) отправляются в порты выполнения, когда их входные операнды готовы и имеется свободный порт выполнения. Это не должно происходить в программном порядке, поэтому ЦП ООО может использовать параллелизм на уровне команд в одном потоке .
Если вы замените «ядро» на «исполнительный блок» в своей идее, вы почти правы. Да, ЦП распределяет независимые инструкции / мопы параллельно исполняющим блокам. (Но есть перепутывание терминологии, так как вы сказали «front-end», когда на самом деле это планировщик команд CPU, также называемый Reservation Station, который выбирает инструкции, готовые к выполнению).
Внеочередное выполнение может найти ILP только на очень локальном уровне, только до пары сотен инструкций, а не между двумя независимыми циклами (если они не короткие).
Например, асм эквивалент этого
будет работать примерно так же быстро, как и тот же цикл, увеличивая только один счетчик на Intel Haswell.
i++
зависит только от предыдущего значенияi
, аj++
зависит только от предыдущего значенияj
, поэтому две цепочки зависимостей могут работать параллельно, не разрушая иллюзию того, что все выполняется в программном порядке.На x86 цикл будет выглядеть примерно так:
Haswell имеет 4 целочисленных исполнительных порта, и все они имеют сумматоры, поэтому он может поддерживать пропускную способность до 4
inc
инструкций за такт, если они все независимы. (С задержкой = 1, поэтому вам нужно всего 4 регистра, чтобы максимизировать пропускную способность, сохраняя 4inc
команды в полете. Сравните это с вектором FP-MUL или FMA: задержка = 5 пропускной способности = 0,5 требует 10 векторных аккумуляторов для поддержания 10 FMA в полете чтобы максимизировать пропускную способность. И каждый вектор может быть 256b, держа 8 плавающих одинарной точности).Взятая ветвь также является узким местом: цикл всегда занимает по крайней мере один полный такт на одну итерацию, потому что пропускная способность принятой ветви ограничена 1 на такт. Я мог бы поместить еще одну инструкцию в цикл без снижения производительности, если только он не читает и не записывает,
eax
илиedx
в этом случае это удлиняет эту цепочку зависимостей. Помещение в цикл еще 2 инструкций (или одной сложной многопользовательской инструкции) создаст узкое место на внешнем интерфейсе, так как он может выдавать только 4 мопа за такт в ядро из строя. (См. Этот раздел вопросов и ответов, где приведены подробности о том, что происходит с циклами, которые не кратны четырем мопам: буферы цикла и кеш мопов делают вещи интересными.)В более сложных случаях, чтобы найти параллелизм, нужно взглянуть на большее окно инструкций . (например, может быть, есть последовательность из 10 инструкций, которые все зависят друг от друга, затем некоторые независимые).
Емкость буфера переупорядочения является одним из факторов, ограничивающих размер окна не по порядку. На Intel Haswell это 192 моп. (И вы даже можете измерить это экспериментально , наряду с емкостью переименования регистров (размером файла регистра).) Ядра с низким энергопотреблением, такие как ARM, имеют гораздо меньшие размеры ROB, если они вообще выполняют не по порядку.
Также обратите внимание, что процессоры должны быть конвейерными, а также не в порядке. Поэтому он должен извлекать и декодировать инструкции задолго до того, как они выполняются, предпочтительно с достаточной пропускной способностью для повторного заполнения буферов после пропуска каких-либо циклов выборки. Ветви хитрые, потому что мы не знаем, откуда их взять, если не знаем, куда пошла ветка. Вот почему прогнозирование ветвей так важно. (И почему современные процессоры используют спекулятивное выполнение: они угадывают, каким образом пойдет ветвь, и начнут извлекать / декодировать / выполнять этот поток команд. При обнаружении неправильного прогноза они возвращаются к последнему известному исправному состоянию и выполняют оттуда.)
Если вы хотите узнать больше о внутренних процессорах ЦП, есть некоторые ссылки в вики-теге Stackoverflow x86 , в том числе на руководство по микроархиву Agner Fog , а также на подробные рецензии Дэвида Кантера с диаграммами процессоров Intel и AMD. Из его записи о микроархитектуре Intel Haswell это окончательная схема всего конвейера ядра Haswell (а не всего чипа).
Это блок-схема одного ядра процессора . Четырехъядерный процессор имеет 4 из них на чипе, каждый со своими кэшами L1 / L2 (совместно использующими кэш L3, контроллеры памяти и PCIe-соединения с системными устройствами).
Я знаю, что это чрезвычайно сложно. Статья Кантера также показывает некоторые части этого, чтобы рассказать о внешнем интерфейсе, например, отдельно от исполнительных блоков или кэшей.
источник
inc
инструкций в одном такте с его 4 целочисленными исполнительными блоками ALU.