Как стать автором
Обновить

Симуляторы компьютерных систем: всем знакомый полноплатформенный симулятор и никому неизвестные потактовый и трассы

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

image

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

Полноплатформенный симулятор (full platform simulator), или “Один в поле — не воин”


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

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

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

image

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

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



image

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

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

Наиболее распространенные на рынке полноплатформенные симуляторы, те же QEMU, VirtualBox или VmWare Workstation, имеют неплохую производительность. Для пользователя может быть даже не заметно, что работа идет в симуляторе. Так происходит благодаря реализованной в процессорах специальной возможности виртуализации, алгоритмам бинарной трансляции и другим интересным вещам. Это все тема для отдельной статьи, но если совсем коротко, то виртуализация – это аппаратная возможность современных процессоров, позволяющая симуляторам не симулировать инструкции, а отдавать на исполнение напрямую в реальный процессор, если, конечно, архитектуры симулятора и процессора похожи. Бинарная трансляция – это перевод гостевого машинного кода в хостовый и последующее исполнение на реальном процессоре. В результате симуляция лишь ненамного медленнее, раз в 5-10, а часто вообще работает с той же скоростью, что и реальная система. Хотя на это влияет очень много факторов. Например, если мы хотим симулировать систему с несколькими десятками процессоров, то скорость тут же упадет в эти несколько десятков раз. С другой стороны, симуляторы типа Simics в последних версиях поддерживают многопроцессорное хостовое «железо» и эффективно распараллеливают симулируемые ядра на ядра реального процессора.

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

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

image

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


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

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

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

image

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

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

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

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


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

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

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

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

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

Публикации

Информация

Сайт
hr.auriga.ru
Дата регистрации
Дата основания
Численность
501–1 000 человек
Местоположение
Россия