gpt4 book ai didi

c++ - VM解释器-权衡性能的优点和缺点:较大的指令集/调度循环

转载 作者:行者123 更新时间:2023-12-01 18:15:44 24 4
gpt4 key购买 nike

我正在开发一个简单的VM,并且正处于十字路口。

我的最初目标是使用字节长指令,因此使用了一个小循环和快速计算出的goto调度。

但是,事实证明,离现实再远了-256位还远远不足以覆盖有符号和无符号的8、16、32和64位整数,浮点数和 double 数,指针操作以及寻址的不同组合。一种选择是不实现字节和短裤,但目标是使VM支持完整的C子集以及 vector 运算,因为无论如何实现,它们几乎处处都是。

因此,我切换到16位指令,因此现在我还能够添加可移植的SIMD内部函数和更多已编译的通用例程,这些例程无需进行解释即可真正节省性能。全局地址也有缓存,最初被编译为基本指针偏移量,第一次编译地址时,它会简单地覆盖偏移量和指令,以便下一次直接跳转时,将付出代价,并且会增加指令集中每次使用全局指令时。

因为我还没有处于分析阶段,所以我处于两难境地,额外的指令是否值得拥有更大的灵活性,是否会有更多的指令,从而没有来回复制指令来弥补调度循环的增加?请记住,这些说明只是一些汇编说明,例如:

    .globl  __Z20assign_i8u_reg8_imm8v
.def __Z20assign_i8u_reg8_imm8v; .scl 2; .type 32; .endef
__Z20assign_i8u_reg8_imm8v:
LFB13:
.cfi_startproc
movl _ip, %eax
movb 3(%eax), %cl
movzbl 2(%eax), %eax
movl _sp, %edx
movb %cl, (%edx,%eax)
addl $4, _ip
ret
.cfi_endproc
LFE13:
.p2align 2,,3
.globl __Z18assign_i8u_reg_regv
.def __Z18assign_i8u_reg_regv; .scl 2; .type 32; .endef
__Z18assign_i8u_reg_regv:
LFB14:
.cfi_startproc
movl _ip, %edx
movl _sp, %eax
movzbl 3(%edx), %ecx
movb (%ecx,%eax), %cl
movzbl 2(%edx), %edx
movb %cl, (%eax,%edx)
addl $4, _ip
ret
.cfi_endproc
LFE14:
.p2align 2,,3
.globl __Z24assign_i8u_reg_globCachev
.def __Z24assign_i8u_reg_globCachev; .scl 2; .type 32; .endef
__Z24assign_i8u_reg_globCachev:
LFB15:
.cfi_startproc
movl _ip, %eax
movl _sp, %edx
movl 4(%eax), %ecx
addl %edx, %ecx
movl %ecx, 4(%eax)
movb (%ecx), %cl
movzwl 2(%eax), %eax
movb %cl, (%eax,%edx)
addl $8, _ip
ret
.cfi_endproc
LFE15:
.p2align 2,,3
.globl __Z19assign_i8u_reg_globv
.def __Z19assign_i8u_reg_globv; .scl 2; .type 32; .endef
__Z19assign_i8u_reg_globv:
LFB16:
.cfi_startproc
movl _ip, %eax
movl 4(%eax), %edx
movb (%edx), %cl
movzwl 2(%eax), %eax
movl _sp, %edx
movb %cl, (%edx,%eax)
addl $8, _ip
ret
.cfi_endproc

本示例包含以下说明:
  • 从立即数中分配无符号字节以注册
  • 从寄存器分配无符号字节到寄存器
  • 从全局偏移量分配无符号字节到寄存器,然后缓存并更改为直接指令
  • 从全局偏移量分配无符号字节到寄存器(现在缓存的先前版本)
  • ...等等...

  • 自然地,当我为它生成一个编译器时,我将能够测试生产代码中的指令流,并优化指令在内存中的排列,以将经常使用的指令打包在一起,并获得更多的缓存命中率。

    我只是很难判断这样的策略是否是个好主意,但这种膨胀会弥补灵活性,但是性能又如何呢?更多的编译例程会弥补更大的调度循环吗?值得缓存全局地址吗?

    我还想让一个组装得体的人对GCC生成的代码的质量发表意见-是否存在明显的效率低下和优化空间?为了使情况更清楚,有一个 sp指针,该指针指向实现寄存器的堆栈(没有其他堆栈), ip在逻辑上是当前指令指针,而 gp是全局指针(未引用,作为抵消)。

    编辑:此外,这是我正在执行指令的基本格式:
    INSTRUCTION assign_i8u_reg16_glob() { // assign unsigned byte to reg from global offset
    FETCH(globallAddressCache);
    REG(quint8, i.d16_1) = GLOB(quint8);
    INC(globallAddressCache);
    }

    FETCH返回对该结构的引用,该指令基于操作码使用

    REG从偏移量返回对寄存器值T的引用

    GLOB从缓存的全局偏移量(有效的绝对地址)中恢复对全局值的引用

    INC只是将指令指针增加指令的大小。

    有人可能会建议不要使用宏,但是使用模板则可读性差得多。这样,代码就很明显了。

    编辑:我想向问题添加几点:
  • 我可以使用“仅寄存器操作”解决方案,该解决方案只能在寄存器和“内存”之间移动数据-是全局变量还是堆。在这种情况下,每个“全局”访问和堆访问都必须复制值,修改或使用它,然后将其移回以进行更新。这样,我的调度循环更短了,但是每条针对非寄存器数据的指令都有一些额外的指令。因此,难题是直接跳转时间更长的本地代码要多出几倍,而调度循环更短的解释性指令要多出几倍。简短的调度循环会给我足够的性能来弥补额外的和昂贵的内存操作吗?也许较短和较长的调度循环之间的增量不足以产生真正的变化?就缓存命中而言,就汇编跳转的成本而言。
  • 我可以去进行额外的解码,而仅使用8位宽的指令,但是,这可能会增加另一个跳转-跳转到处理该指令的任何地方,然后浪费时间跳转到处理特定寻址方案的情况下或解码操作等等复杂的执行方法。在第一种情况下,调度循环仍在增长,并增加了另一个跳跃。第二种选择-寄存器操作可用于对寻址进行解码,但是将需要更复杂的指令以及更多的编译时间来寻址任何内容。我不太确定如何用较短的调度循环将其叠加,再次不确定我的“较短和较长的调度循环”与汇编指令,所需的内存和速度方面的长短关系如何。他们的执行。
  • 我可以使用“许多指令”解决方案-调度循环大了好几倍,但是它仍然使用预先计算的直接跳转。复杂的寻址是针对每个指令的特定且已优化的,并编译为本地的,因此“仅寄存器”方法所需的额外内存操作将被编译并主要在寄存器上执行,这对性能有好处。通常,这种想法不仅增加了指令集,还增加了可以预先编译并在单个“指令”中完成的工作量。 loner指令集还意味着更长的调度循环,更长的跳转(尽管可以优化以使其最小化),更少的高速缓存命中,但是问题是如何产生的?考虑到每条“指令”只是几条汇编指令,大约7-8k条指令的汇编代码片段被认为是正常的,还是太多?考虑到平均指令大小在2-3b左右变化,因此该内存不应超过20k,足以完全适合大多数L1缓存。但这不是具体的数学运算,只是我在谷歌搜索时遇到的东西,所以也许我的“计算”不正确了?或者,也许这种方式行不通?我在缓存机制方面经验不足。

  • 对我而言,正如我目前对参数的重视程度一样,“许多指令”方法似乎具有最佳性能的最大机会,当然,前提是我关于在L1缓存中拟合“扩展派发循环”的理论仍然成立。因此,这里就是您的专业知识和经验发挥作用的地方。既然上下文已经缩小并且提出了一些支持想法,也许可能会更容易给出一个更具体的答案,即通过减少较慢的,解释的代码的数量,较大的指令集的好处是否优先于本地代码的大小增加? 。

    我的乐器尺寸数据是基于 stats的。

    最佳答案

    您可能需要考虑分离VM ISA及其实现。

    例如,在VM中,我写了一条“直接加载值”指令。指令流中的下一个值没有被解码为指令,而是作为值加载到寄存器中。您可以考虑使用此宏指令或两个单独的值。

    我实现的另一条指令是“加载常数值”,它从内存中加载了一个常数(使用常数表的基地址和偏移量)。因此,指令流中的常见模式是load value direct (index); load constant value。您的VM实现可能会识别此模式,并使用一个优化的实现来处理该对。

    显然,如果您有足够的位,则可以使用其中一些来标识寄存器。对于8位,可能需要为所有操作使用单个寄存器。但是同样,您可以添加另一条指令with register X来修改下一个操作。在您的C++代码中,该指令只会设置其他指令使用的currentRegister指针。

    关于c++ - VM解释器-权衡性能的优点和缺点:较大的指令集/调度循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18181763/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com