Как запустить два ядра процессора для одновременного выполнения инструкций? - PullRequest
3 голосов
/ 08 июля 2019

Например, в X86 два ядра ЦП работают под управлением разных программных потоков.
На данный момент эти 2 потока должны одновременно работать на ядрах процессора.
Есть ли способ синхронизации этих двух ядер / потоков ЦП или что-то вроде этого, чтобы заставить их запускаться одновременно ( почти ) (на уровне инструкций)?

Ответы [ 2 ]

5 голосов
/ 08 июля 2019

Используйте совместно используемую переменную для установления крайнего срока на основе rdtsc между двумя потоками.Например, установите крайний срок, скажем, текущего rdtsc значения плюс 10000.

Затем оба потока вращаются на rdtsc, ожидая, пока разрыв между текущим значением rdtsc ипорог меньше порогового значения T (Т = 100 должно быть в порядке).Наконец, используйте окончательное значение промежутка (то есть, крайний срок rdtsc значение минус последнее прочитанное значение rdtsc), чтобы перейти к последовательности зависимых инструкций добавления, чтобы число инструкций добавления было равно разрыву.

Этот последний шаг компенсирует тот факт, что каждый чип, как правило, не будет "в фазе" по отношению к их rdtsc циклу вращения.Например, предполагая 30-тактную параллельную пропускную способность для показаний rdtsc, один чип может получить показания 890, 920, 950 и т. Д., В то время как другой может прочитать 880, 910, 940, поэтому будет 10 или 20ошибка цикла, если используется только rdtsc.Используя компенсацию добавления слайдов, если крайний срок был 1000, и с пороговым значением 100, первый поток будет запускаться при rdtsc == 920 и выполнять 80 добавлений, тогда как второй будет запускаться при rdtsc == 910 и выполнять 90 добавлений.В принципе, оба ядра будут примерно синхронизированы.

Некоторые примечания:

  • Выше предполагается, что частота процессора равна номинальной rdtsc частоте - если это не такПри вычислении места перехода в слайд добавления необходимо применять коэффициент компенсации, основанный на номинальном значении истинной частоты.
  • Не ожидайте, что ваши процессоры будут долго синхронизироваться: что-то вроде прерывания, переменной задержкитакие операции, как пропадание кэша или множество других вещей, могут привести к потере синхронизации.
  • Вы хотите, чтобы весь код полезной нагрузки и слайд добавления были горячими в кэш-памяти каждого ядра, иначе ониочень вероятно, чтобы выйти из синхронизации сразу.Вы можете разогреть icache, выполнив один или несколько фиктивных прогонов этого кода перед синхронизацией.
  • Вы хотите, чтобы T был достаточно большим, чтобы разрыв всегда был положительным, поэтому несколько больше, чем обратный.rdtsc задержка, но не такая большая, чтобы увеличить вероятность таких событий, как прерывания во время добавления слайда.
  • Вы можете проверить эффективность "синхронизации", введя rdtsc или rdtscp в различных точках кода «полезной нагрузки» после синхронизации и наблюдения, насколько близко записанные значения находятся между потоками.

Совершенно другой вариант будет использовать Intel TSX: транзакционныйрасширения.Организовать для двух потоков, которые хотят координировать, чтобы одновременно прочитать общую линию внутри транзакционной области, а затем вращаться, и иметь третий поток для записи в общую линию.Это приведет к прерыванию в обоих ожидающих потоках.В зависимости от топологии между ядрами два ожидающих потока могут получить аннулирование и, следовательно, последующее прерывание TSX почти одновременно.Вызовите код, который вы хотите запустить, «синхронно» из обработчика прерывания.

3 голосов
/ 08 июля 2019

В зависимости от вашего определения «(почти) того же времени», это очень сложная проблема микроархитектуры.

Даже определение «Выполнить» не достаточно конкретно, если вы заботитесь о времени до цикла. Вы имеете в виду проблему из внешнего интерфейса в неработающий сервер? Выполнить? (отправить в исполнительный блок? или успешно завершить выполнение без необходимости повторного воспроизведения?) Или удалиться?

Я бы предпочел пойти с Выполнить 1 , потому что именно тогда команда, подобная rdtsc, производит выборку счетчика меток времени. Это тот, который вы можете записать, а затем сравнить позже.

сноска 1: на правильном пути, не в тени ошибочных предположений, если только вы не согласны с казнями, которые не доходят до выхода на пенсию.

Но если два ядра имеют разные состояния ROB / RS при выполнении команды, о которой вы заботитесь, они не будут продолжать работу в режиме блокировки. (Крайне мало процессоров x86-64 в порядке, как некоторые предшествующие Silvermont Atoms, и ранние Xeon Phi: Knight's Corner. Современные процессоры x86-64 вышли из строя и находятся за пределами маломощного Silvermont -семейно агрессивно так с большим ROB + планировщиком.)


x86 трюки asm:

Я не использовал его, но x86 asm monitor / mwait, чтобы оба процессора контролировали и ждали записи в заданную ячейку памяти, могли бы работать. Я не знаю, насколько синхронизирован пробуждение. Я предполагаю, что чем менее глубокий сон, тем меньше переменная задержка.

Ранний выход из прерывания, предшествующего записи, всегда возможен. Если вы не отключите прерывания, вы не сможете сделать это в 100% случаев; надеюсь, вам просто нужно, чтобы это произошло с некоторым разумным шансом на успех, и иметь возможность по факту определить, достигли ли вы этого.

(На самых последних процессорах Intel с низким энергопотреблением (Tremont) доступны версии для использования в пространстве пользователя: umonitor / umwait. Но в ядре вы, вероятно, можете просто использовать monitor / mwait)

Если доступно umonitor / umwait, это означает, что у вас есть функция CPU WAITPKG, которая также включает tpause: например, pause, но дождитесь Метка времени TSC.

В современных процессорах x86 TSC синхронизируется между всеми ядрами аппаратно, поэтому использование одного и того же времени активации для нескольких ядер делает это тривиальным.

В противном случае вы могли бы подождать до крайнего срока rdtsc и, вероятно, в течение ~ 25 циклов в худшем случае на Скайлэйке.

rdtsc имеет пропускную способность по одному на каждые 25 циклов на Skylake (https://agner.org/optimize/), так что вы ожидаете, что каждый поток будет в среднем на 12,5 цикла позже, выходя из цикла ожидания вращения, + -12,5. Я предполагаю ветвь Стоимость -mispredict для обоих потоков одинакова. Это тактовые циклы ядра, а не опорные циклы, которые считает rdtsc. RDTSC обычно тикает близко к максимальному не турбо такту. См. Как получить количество тактов ЦП в x86_64 из C ++? для получения дополнительной информации о RDTSC от C.

См. Сколько задержки генерируется этим ассемблерным кодом в linux для функции asm, которая вращается на rdtsc в ожидании крайнего срока. Вы можете написать это в C достаточно легко .


Синхронизация после начального запуска:

На многоядерном Xeon, где каждое ядро ​​может изменять частоту независимо , вам нужно будет зафиксировать частоту процессора на что-то, вероятно, максимальный нетурбо режим будет хорошим выбором. В противном случае с ядрами на разных тактовых частотах они, очевидно, сразу же не синхронизируются.

На настольном компьютере вы можете сделать это в любом случае, в случае, если приостановка часов для изменения частоты процессора приводит к сбоям.


Любая разница в ошибочных прогнозах веток, кэш-памяти или даже в разных начальных состояниях ROB / RS может привести к значительной рассинхронизации.

Что еще более важно, прерывания огромны и занимают очень длительное время по сравнению с выполнением еще 1 инструкции в уже выполняющейся задаче. И это может даже привести к тому, что планировщик переключит контекст в другой поток. Или миграция ЦП для задачи, которая, очевидно, стоит много циклов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...