Pull to refresh
0

Worst case execution time на x86

Reading time 3 min
Views 7.8K
В прошлом посте я описал, как и зачем измеряется interrupt latency на платформе Atom.

Сегодня расскажу о том, почему один и тот же код с одними и теми же входными данными может исполняться разное время. Для некоторых realtime приложений это очень нежелательный эффект, с которым приходится бороться.

Сразу на всякий случай скажу, что термин "Worst case execution time" в заголовке я применил, конечно же, не разбираясь в сути явления. С x86 он никак не связан.

Пусть у нас есть, например, прерывание, которое срабатывает, когда по DMA некая железка подогнала новые данные в память. В ISR мы гоняем их через FIR фильтр, и отдаем, например, другой железке. Только не надо спрашивать, зачем в этой задаче x86. Это просто пример, и пусть в этом примере на машине одновременно исполняется масса другого нужного кода.

В этом случае получается, что каждое прерывание исполняет строго одну и ту же последовательность инструкций. Каждый раз она должна исполняться за одно и то же время. Так? Не так!

Есть две главные западни.
1. Кэш. Коэффициенты для фильтра берутся из памяти. Они могут быть в L1D кэше, L2 кэше, L3 кэше (если он есть), или в памяти. Заранее не скажешь. Если бы в примере использовалась виртуальная память, еще мог бы произойти (или не произойти) DTLB miss. Ну и наконец сам код ISR может лежать в L1I cache, L2, и т.д. Основной источник непредсказуемости здесь — тот факт, что выполнявшийся ранее код мог забить кэш своими данными. А еще кэш может делиться между настоящими или HT ядрами.
2. Гипертрединг. В зависимости от того, какие инструкции исполняются во втором потоке, первый поток замедляется по-разному. (в 1-3 раз, в среднем в полтора раза на Атоме) Механизм замедления немного различается в Core с OOO-конвейером и в Atom с In-Order конвейером. У Атома гипертреды в среднем сильнее могут влиять на производительность друг друга, т.к. им приходится делить более ограниченные ресурсы, вроде доступа к кэшу.

Что можно сделать?
1. Кэш. Во-первых, использовать s/w префетч. В начале кода ISR, прежде всего заказать коэффициенты фильтра в кэш.
Если речь об общем L3 кэше в новых Core, есть еще один выход. Можно использовать cache coloring для того, чтобы выделить область памяти в кэше, которую будет сложнее вытеснить процессам, исполняющимся на других ядрах. Если пропатчить работу с виртуальной памятью в ОС, можно выдавать приложениям куски виртуальной памяти, которые могут попадать только в определенные области общего last level cache. Можно разделить L3 кэш на Core на 32 области, и при выделении памяти на разных ядрах выбирать непересекающиеся участки. Я видел, как эта техника помогает изменить время выполнения определенного кода с 400-900us до 500-600us.
К сожалению, этот вид магии неприменим к достаточно длинным областям физической памяти.

2. Гипертрединг. Отключить! Можно в начале работы критичного с точки зрения предсказуемости производительноси кода послать IPI к второму потоку. И написать его обработчик, который при приеме такого IPI будет ждать, например, на mwait. Так можно продолжать использовать прирост производительности, которую дает гипертрединг, но почти без нежелательного побочного эффекта.

Существование обоих описанных выше явлений, на самом деле, помогает увеличивать общую производительность. Просто для некоторых realtime приложений, возможные небольшие (100-500us), но непредсказуемые задержки очень нежелательны. Поэтому в новых архитектурах будут появляться фичи, помогающие получить более предсказуемую производительность. Например, возможно, появится поддержка более тонкого программного управления кэшированием.
Tags:
Hubs:
+20
Comments 12
Comments Comments 12

Articles

Information

Website
www.intel.ru
Registered
Founded
Employees
5,001–10,000 employees
Location
США
Representative
Анастасия Казантаева