Полноплатформенный симулятор, потактовая модель и трассы

Технические статьи

Полноплатформенный симулятор, потактовая модель и трассы

6 июля, 2020

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

Полноплатформенный симулятор

Если инженеру требуется исследовать работу одного конкретного устройства, – например, сетевой карты, – или написать для этого устройства прошивку или драйвер, то такое устройство можно смоделировать отдельно. Однако, использовать его в отрыве от остальной инфраструктуры не очень целесообразно: неизбежно потребуются дополнительные усилия на подготовку входных данных и преобразование выходных. В случае устройства (например, модели сетевой карты) просто запустить соответствующий драйвер нельзя, так как он выполняется на центральном процессоре, затрачивает память, требует доступ к шине для передачи данных и прочее. Кроме того, сам по себе драйвер работает в некотором окружении и для его нормальной работы необходимы операционная система (ОС) и сетевой стек. В дополнение к этому может потребоваться отдельный генератор пакетов и сервер приема ответов.

Полноплатформенный симулятор (full-platform simulator) создает окружение для запуска немодифицированного полного софтверного стека, который включает в себя все, начиная с BIOS и загрузчика, и заканчивая самой ОС и различными ее подсистемами (например, сетевой стек, драйвера, приложения пользовательского уровня). Для этого полноплатформенный симулятор включает в себя большинство устройств компьютера: прежде всего, процессор и память, диск, устройства ввода-вывода, дисплей, а также сетевое устройство.

Ниже, для примера, приведена блок-диаграмма чипсета x58 от компании Intel. Если требуется разработать полноплатформенный симулятор компьютера на этом чипсете, то необходима реализация большинства перечисленных устройств, в том числе и тех, что находятся внутри IOH (Input/Output Hub) и ICH (Input/Output Controller Hub), не нарисованных детально на блок-диаграмме.

Simulators chart

Чаще всего полноплатформенные симуляторы реализуются на уровне инструкций процессора (ISA, см. предыдущую статью). Это позволяет относительно быстро и недорого создать сам симулятор, иначе на создание детализированных моделей всех устройств уйдет вечность.

Уровень ISA также хорош тем, что остается более или менее постоянным, в отличие от, например, уровня API/ABI, который меняется чаще. К тому же, реализация на уровне инструкций позволяет запускать так называемое немодифицированное бинарное ПО. Другими словами, можно сделать слепок («дамп») жесткого диска, указать его в качестве образа для модели в полноплатформенном симуляторе и – вуаля! – ОС и остальные программы загружаются в симуляторе без всяких дополнительных действий.

Производительность симуляторов

Как было упомянуто чуть выше, сам процесс симуляции целиком всей системы, каждого из ее устройств, довольно небыстрое мероприятие. Поэтому реализация на совсем детальном уровне будет выполняться очень медленно. Уровень инструкций, в свою очередь, позволяет ОС и программам выполняться на скоростях, достаточных для комфортного взаимодействия пользователя с ними.

Когда мы говорим о производительности, то под единицей изменения подразумеваем IPS (instructions per second), точнее, MIPS (millions IPS), то есть количество инструкций процессора, выполняемых симулятором за одну секунду. В то же время, скорость симуляции зависит и от производительности системы, на которой работает сама симуляция. Поэтому более правильно говорить о «замедлении» (slowdown) симулятора по сравнению с оригинальной системой.

Наиболее распространенные полноплатформенные симуляторы, которые доступны сегодня на рынке, чрезвычайно быстрые. Пользователь может даже не заметить, что работа идет в симуляторе. Это происходит благодаря реализованной в  процессорах специальной возможности виртуализации, а также алгоритмам так называемой бинарной трансляции. В результате симуляция медленнее реальной системы всего лишь в 5-10 раз, а часто и вообще работает с той же скоростью. Однако, на скорость исполнения симуляции влияет очень много факторов. Например, если мы хотим симулировать систему с несколькими десятками процессоров, то скорость тут же упадет в эти несколько десятков раз. Эта ситуация разрешается путем использования современных платформ  вроде Simics, с которым мы работаем в “Аурига”, последние версии которых поддерживают многопроцессорное хостовое «железо» и эффективно распараллеливают симулируемые ядра на ядра реального процессора.

Когда мы переходим к разговору о скорости микроархитектурной симуляции, она обычно на несколько порядков (примерно в 1000-10000 раз) медленнее реальной системы, а реализации на уровне логических элементов еще на несколько порядков медленнее. Поэтому в качестве эмулятора на этом уровне используют FPGA, что позволяет существенно увеличить производительность.

График ниже показывает примерную зависимость скорости симуляции от детализации модели.

Simulators graph

Потактовая симуляция

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

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

Простейший пример – инструкция доступа в память. Если запрашиваемая ячейка памяти доступна в кэше, то время выполнения будет минимально. Если в кэше данной информации нет («промах кэша», cache miss), то это сильно увеличит время выполнения инструкции. Таким образом, для точной симуляции необходима модель кэша. Однако моделью кэша дело не ограничивается. Процессор, например, не будет просто ждать получения данных из памяти при ее отсутствии в кэше. Вместо этого он начнет выполнять следующие инструкции, выбирая такие, которые не зависят от результата чтения из памяти. Это так называемое выполнение «не по порядку» (OOO, out of order execution), оно необходимо для максимального снижения времени простоя процессора. Учесть все это при расчете времени выполнения инструкций поможет моделирование соответствующих блоков процессора.

Среди инструкций, выполняемых пока ожидается результат чтения из памяти, может также встретиться операция условного перехода. Если результат выполнения условия неизвестен на данный момент, то опять-таки процессор не останавливает выполнение, а делает «предположение», выполняет соответствующий переход и продолжает превентивно выполнять инструкции с места перехода. Такой блок, называемый branch predictor, также должен быть реализован в микроархитектурном симуляторе.

Ниже приведена схема основных блоков процессора для демонстрации сложности микроархитектурной реализации.

Simulators chart

Работа всех этих блоков в реальном процессоре синхронизуется специальными сигналами тактовой частоты.  Аналогично это происходит и в модели. Такой микроархитектурный симулятор называют потактовым (cycle accurate). Основное его назначение – точно спрогнозировать производительность разрабатываемого процессора и рассчитать время выполнения определенной программы, например, какого-либо бенчмарка. Если значения будут ниже необходимых, то потребуется дорабатывать алгоритмы и блоки процессора.

Потактовая симуляция очень медленная, поэтому ее используют только при исследовании определенных моментов работы программы когда необходимо узнать реальную скорость выполнения программ и оценить будущую производительность устройства, прототип которого моделируется. При этом, для симуляции остального времени работы программы используется функциональный симулятор. Как такое комбинированное использование происходит в реальности?

Вначале запускается функциональный симулятор, на котором загружается ОС и все необходимое для запуска исследуемой программы. Ведь нас не интересует ни сама ОС, ни начальные стадии запуска программы, ни ее конфигурирование, однако и пропустить эти части и сразу перейти к выполнению программы мы тоже не можем. Поэтому все эти предварительные этапы прогоняются на функциональном симуляторе. После исполнения программы до интересующего нас момента, возможны два варианта. В первом подходе можно заменить модель на потактовую и продолжить исполнение. Режим симуляции, при котором используется исполняемый код (т.е. обычные скомпилированные файлы программ), называют симуляцией по исполнению (execution driven simulation). Это самый распространенный вариант симуляции. Он используется и в функциональных и потактовых симуляторах. Возможен также и второй подход – симуляция на основе трасс (trace driven simulation).

Симуляция на основе трасс

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

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

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

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

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

Алексей Лесных, Менеджер проектов, Аурига

Новости

Auriga получила высокую оценку за интерфейс медицинского приложения

Auriga получила высокую оценку за интерфейс медицинского приложения

Начиная с 14 сентября 2019 года, мир отмечает Всемирный день безопасности пациентов. Эта дата, принятая ВОЗ, должна повысить информированность и стимулировать объединенные усилия, направленные на

Auriga и OR.NET будут разрабатывать стандарты совместимости медоборудования

Компания Auriga стала членом ассоциации OR.NET – некоммерческой организации, которая занимается разработкой международных стандартов в сфере совместимости медицинского оборудования. Вступление в ассоциацию, которая ставит своей...

Auriga в условиях пандемии: работаем для вас

Уважаемые коллеги, заказчики и партнеры, Наша жизнь никогда еще не была так непредсказуема, как в эти месяцы, в условиях пандемии коронавируса. В это непростое для...

Признанный лидер услуг по разработке ПО:
управление командами и проектами;
разработка новых продуктов, сопровождение, тестирование