计算机系统模拟器:熟悉的全平台模拟器和未知的顺时针和痕迹

在关于计算机系统模拟器的文章的第二部分中,我将继续以简单的介绍形式谈论计算机模拟器,即普通用户最常遇到的全平台模拟,以及时钟时钟-时钟模型和迹线,这在开发者圈子里比较常见。

计算机系统模拟器:熟悉的全平台模拟器和未知的顺时针和痕迹

В 第一部分 我讨论了模拟器的一般含义以及模拟的级别。 现在,基于这些知识,我建议更深入地讨论全平台仿真、如何收集跟踪、稍后如何处理它们,以及逐个时钟微架构仿真。

全平台模拟器,还是“孤军奋战不是勇士”

如果你想研究一个特定设备(例如网卡)的操作,或者为这个设备编写固件或驱动程序,那么可以单独模拟这样一个设备。 然而,与基础设施的其余部分隔离地使用它并不是很方便。 要运行相应的驱动程序,您将需要中央处理器、内存、数据总线的访问权限等。 此外,驱动程序需要操作系统 (OS) 和网络堆栈才能运行。 此外,可能需要单独的数据包生成器和响应服务器。

全平台模拟器创建一个运行完整软件堆栈的环境,其中包括从 BIOS 和引导加载程序到操作系统本身及其各种子系统的所有内容,例如相同的网络堆栈、驱动程序和用户级应用程序。 为此,它实现了大多数计算机设备的软件模型:处理器和内存、磁盘、输入/输出设备(键盘、鼠标、显示器)以及相同的网卡。

下面是 Intel x58 芯片组的框图。 该芯片组上的全平台计算机模拟器需要实现大多数列出的设备,包括 IOH(输入/输出集线器)和 ICH(输入/输出控制器集线器)内的设备,这些设备在框图中没有详细描述。 尽管实践表明,我们要运行的软件不使用的设备并不多。 不需要创建此类设备的模型。

计算机系统模拟器:熟悉的全平台模拟器和未知的顺时针和痕迹

大多数情况下,全平台模拟器是在处理器指令级(ISA,见下文)实现的。 上一篇文章)。 这使您可以相对快速且廉价地创建模拟器本身。 ISA 级别也很好,因为它或多或少保持恒定,这与 API/ABI 级别不同,后者更频繁地变化。 此外,指令级的实现允许您运行所谓的未修改的二进制软件,即运行已编译的代码而不进行任何更改,就像在真实硬件上使用一样。 换句话说,您可以制作硬盘驱动器的副本(“转储”),将其指定为全平台模拟器中模型的映像,然后瞧! – 操作系统和其他程序加载到模拟器中,无需任何其他操作。

模拟器性能

计算机系统模拟器:熟悉的全平台模拟器和未知的顺时针和痕迹

正如上面提到的,模拟整个系统(即所有设备)的过程是一项相当缓慢的工作。 如果您还在非常详细的级别(例如微架构或逻辑级别)实现所有这些,那么执行将变得非常慢。 但指令级别是一个合适的选择,它允许操作系统和程序以足以让用户舒适地与之交互的速度执行。

在这里讨论模拟器性能的话题是合适的。 通常以 IPS(每秒指令数)来衡量,更准确地说以 MIPS(百万 IPS)为单位,即模拟器在一秒钟内执行的处理器指令数。 同时,仿真的速度还取决于仿真本身运行的系统的性能。 因此,谈论模拟器相对于原始系统的“减速”可能更正确。

市场上最常见的全平台模拟器,例如QEMU、VirtualBox或VmWare Workstation,都具有良好的性能。 用户甚至可能没有注意到模拟器中正在进行的工作。 这要归功于处理器中实现的特殊虚拟化功能、二进制翻译算法和其他有趣的东西。 这是一篇单独文章的主题,但简而言之,虚拟化是现代处理器的一项硬件功能,它允许模拟器不模拟指令,而是将它们直接发送到真实处理器执行,当然,如果架构模拟器和处理器是相似的。 二进制翻译是将客户机器代码翻译成主机代码并随后在真实处理器上执行。 因此,模拟仅稍微慢一些,为 5-10 倍,甚至常常以与真实系统相同的速度运行。 虽然这受到很多因素的影响。 例如,如果我们要模拟一个有几十个处理器的系统,那么速度会立即下降这几十倍。 另一方面,最新版本的 Simics 等模拟器支持多处理器主机硬件,并有效地将模拟内核并行到真实处理器的内核上。

如果我们谈论微架构模拟的速度,那么它通常是几个数量级,比在没有模拟的情况下在普通计算机上执行慢大约 1000-10000 倍。 并且逻辑元素级别的实现要慢几个数量级。 因此,在这个级别使用FPGA作为仿真器,可以显着提高性能。

下图显示了模拟速度对模型细节的近似依赖性。

计算机系统模拟器:熟悉的全平台模拟器和未知的顺时针和痕迹

逐节拍模拟

尽管执行速度较低,但微架构模拟器还是很常见。 为了准确地模拟每条指令的执行时间,需要对处理器的内部模块进行模拟。 这里可能会产生误解 - 毕竟,看起来为什么不简单地对每条指令的执行时间进行编程。 但这样的模拟器将非常不准确,因为同一指令的执行时间可能因调用而异。

最简单的例子是存储器访问指令。 如果请求的内存位置在高速缓存中可用,则执行时间将是最短的。 如果该信息不在高速缓存中(“高速缓存未命中”),那么这将大大增加指令的执行时间。 因此,需要缓存模型来进行准确的模拟。 然而,问题并不限于缓存模型。 当数据不在高速缓存中时,处理器不会简单地等待从内存中检索数据。 相反,它将开始执行下一条指令,选择那些不依赖于从内存读取结果的指令。 这就是所谓的“乱序”执行(OOO,乱序执行),是最小化处理器空闲时间所必需的。 对相应的处理器模块进行建模将有助于在计算指令的执行时间时考虑所有这些因素。 在这些指令中,在等待从存储器读取结果的同时执行,可能会发生条件跳转操作。 如果此时条件的结果未知,则处理器不会停止执行,而是进行“猜测”,执行适当的分支并继续从转换点主动执行指令。 这样的块称为分支预测器,也必须在微架构模拟器中实现。

下图显示了处理器的主要模块,没有必要了解它,它只是为了显示微架构实现的复杂性。

计算机系统模拟器:熟悉的全平台模拟器和未知的顺时针和痕迹

真实处理器中所有这些模块的操作都是通过特殊时钟信号同步的,模型中也是如此。 这种微架构模拟器称为周期精确的。 其主要目的是准确预测正在开发的处理器的性能和/或计算特定程序的执行时间,例如基准测试。 如果这些值低于要求,则需要修改算法和处理器模块或优化程序。

如上所示,逐时钟仿真速度非常慢,因此仅在研究程序运行的某些时刻时使用,需要了解程序执行的真实速度并评估其设备的未来性能。原型正在被模拟。

在这种情况下,使用功能模拟器来模拟程序的剩余运行时间。 这种组合使用在现实中是如何发生的? 首先,启动功能模拟器,在其上加载操作系统和运行所研究程序所需的一切。 毕竟,我们对操作系统本身不感兴趣,也不对启动程序的初始阶段、其配置等感兴趣。 但是,我们也不能跳过这些部分并立即从中间开始执行程序。 因此,所有这些初步步骤都在功能模拟器上运行。 当程序执行到我们感兴趣的时刻后,有两种选择。 您可以将该模型替换为逐周期模型并继续执行。 使用可执行代码(即常规编译的程序文件)的仿真模式称为执行驱动仿真。 这是最常见的模拟选项。 另一种方法也是可能的——跟踪驱动模拟。

基于轨迹的模拟

它由两个步骤组成。 使用功能模拟器或在真实系统上,收集程序操作日志并将其写入文件。 该日志称为跟踪。 根据所检查的内容,跟踪可能包括可执行指令、内存地址、端口号和中断信息。

下一步是“播放”跟踪,即逐个时钟模拟器读取跟踪并执行其中写入的所有指令。 最后,我们得到了这段程序的执行时间,以及该进程的各种特征,例如缓存中的命中百分比。

使用痕迹的一个重要特征是确定性,也就是说,通过以上述方式运行模拟,我们一遍又一遍地重现相同的操作序列。 这样,通过更改模型参数(缓存、缓冲区和队列大小)并使用不同的内部算法或对其进行调整,就可以研究特定参数如何影响系统性能以及哪个选项提供最佳结果。 所有这些都可以在创建实际的硬件原型之前通过原型设备模型来完成。

这种方法的复杂性在于需要首先运行应用程序并收集跟踪,以及跟踪文件的巨大尺寸。 其优点包括仅模拟感兴趣的设备或平台的一部分就足够了,而通过执行进行模拟通常需要完整的模型。

因此,在本文中,我们研究了全平台仿真的特点,讨论了不同级别的实现速度、逐周期仿真和跟踪。 在下一篇文章中,我将描述使用模拟器的主要场景,无论是出于个人目的还是从大公司的开发角度来看。

来源: habr.com

添加评论