Worst case execution time на x86

    В прошлом посте я описал, как и зачем измеряется 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), но непредсказуемые задержки очень нежелательны. Поэтому в новых архитектурах будут появляться фичи, помогающие получить более предсказуемую производительность. Например, возможно, появится поддержка более тонкого программного управления кэшированием.
    Intel
    202,00
    Компания
    Поделиться публикацией

    Комментарии 12

      +2
      Спасибо за статью, тема важная. Только написано, даже для людей в теме, заумно :) Чувствуется, что Вы хорошо понимаете то, о чем говорите, но текст нуждается в шлифовке. Обычно пишут много воды, а у Вас, наоборот, слишком кратко. И перебор с англицизмами.

      Прошу прощения за критику, я из лучших побуждений :)
        0
        Отчего же, правильная критика. Единственно, насчет англицизмов — сложно поддержать баланс между НГМД и FDD :). Возможно, я считаю неправильно, что Last Level Cache звучит привычнее, чем «кэш последнего уровня»?
          +2
          Недаром, видимо, в Южной Корее официальное общение в научных кругах на английском :) Они берегут свой разговорный язык от подобных вторжений.
          +2
          В следующий раз напишу, как именно работает cache coloring (раскраска кэша?), с картинками. Надеюсь, кому-нибудь окажется полезно.
            0
            По идее, здесь можно расставить ссылки на вики по всем упомянутым терминам, чтобы не писать отдельные статьи.
              0
              На тему «utilizing cache coloring for cache partitioning» вроде нет ничего в вики? То, что есть в BSD и Виндах — это средство улучшения общей производительности работы с кэшем.
          0
          По моему тут у вас архитектурная проблема. ISR должна исполнится максимально быстро, потому никакого FIR фильтра в ней не должно быть. Она должна например принять даные и отсигналить, а уже приложение профильтрирует и передаст драйверу другого устройства.
            0
            Я в курсе. Конечно, это надо делать в DPC.

            Я упростил пример, чтобы сконцентрироваться на том, что даже если исполняется код на самом верхнем IRQL, и некому его прерывать, то исполняться он все равно будет за разное время.

            Кстати, если бы я использовал, например, Twincat под Windows XP, то FIR работал бы все равно фактически на 28 IRQL, а не на 2, как рекомендуете вы и Microsoft.
              0
              Я вообще никак не рекомендую.
              Я под M8C, i8051, ARM библиотеки пишу. Самое большое преступление это делать какую-то работу в обработчике прерываний, в обработчике принял данные, передал кому нужно и вернул управление. Иначе пользователь вместо реакции на нажатие кнопки получит что система не реагирует и нажмёт второй раз например.
                0
                Это действительно достаточно большое преступление в 99% случаев, когда пишется ISR. Еще бОльшее преступление — отдавать эту работу в DPC в остальных 1% случаях. Как и у остальных правил, бывают исключения.

                Я специально указал в примере, что данные получены по DMA от одной железки, и результат работы FIR фильтра над ними обязательно должен быть отдан другой железке за минимальное и предсказуемое время. Как бы Вы еще поступлил на x86 под Windows или Linux?
                  0
                  А зачем тут вообще ОС?
                  Из пушки по насекомым, нет?
                    0
                    «Это просто пример, и пусть в этом примере на машине одновременно исполняется масса другого нужного кода.» Например, GUI под Windows. Клиентам очень нравится.

                    Когда есть ОС, разработка сложных и больших вещей проще. А если было бы нужно просто гонять данные с минимальной обработкой, то ОС, конечно же, не нужна.

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое