gpt4 book ai didi

assembly - 编写跨步 x86 基准测试

转载 作者:行者123 更新时间:2023-12-03 17:11:30 25 4
gpt4 key购买 nike

我想编写一个加载基准测试,它以编译时已知的步幅跨过给定的内存区域,并在该区域的末尾(2 的幂)使用尽可能少的非加载指令进行包装有可能。

例如,给定步长 4099,rdi 中的迭代计数以及 rsi 中指向内存区域的指针,“有效”的是:

%define STRIDE 4099
%define SIZE 128 * 1024
%define MASK (SIZE - 1)
xor ecx, ecx

.top:
mov al, [rsi + rcx]
add ecx, STRIDE
and ecx, MASK
dec rdi
jnz .top

问题是有4个非加载指令只是为了支持单个加载,处理步幅加法、屏蔽和循环终止检查。此外,还有一个通过 ecx 承载的 2 周期依赖链。

我们可以将其展开一点,以将循环终止检查成本降低到接近于零,并打破依赖链(此处展开 4 倍):

.top:

lea edx, [ecx + STRIDE * 0]
and edx, MASK
movzx eax, BYTE [rsi + rdx]

lea edx, [ecx + STRIDE * 1]
and edx, MASK
movzx eax, BYTE [rsi + rdx]

lea edx, [ecx + STRIDE * 2]
and edx, MASK
movzx eax, BYTE [rsi + rdx]

lea edx, [ecx + STRIDE * 3]
and edx, MASK
movzx eax, BYTE [rsi + rdx]

add ecx, STRIDE * 4

dec rdi
jnz .top

但是,这对于处理步幅包装的 addand 操作没有帮助。例如,上述基准报告 L1 包含区域的 0.875 个周期/负载,但我们知道正确的答案是 0.5(每个周期两个负载)。 0.875 来自 15 个总 uops/4 uops 周期,即我们受 4 宽 uop 吞吐量最大宽度的约束,而不是负载吞吐量。

关于如何有效展开循环以消除步幅计算的大部分成本的好方法有什么想法吗?

最佳答案

对于“绝对最大的疯狂”;您可以要求操作系统在许多虚拟地址处映射相同的页面(例如,相同的 16 MiB RAM 出现在虚拟地址 0x100000000、0x11000000、0x12000000、0x13000000...),以避免需要关心换行;并且您可以使用自生成代码来避免其他一切。基本上,生成如下指令的代码:

    movzx eax, BYTE [address1]
movzx ebx, BYTE [address2]
movzx ecx, BYTE [address3]
movzx edx, BYTE [address4]
movzx esi, BYTE [address5]
movzx edi, BYTE [address6]
movzx ebp, BYTE [address7]
movzx eax, BYTE [address8]
movzx ebx, BYTE [address9]
movzx ecx, BYTE [address10]
movzx edx, BYTE [address11]
...
movzx edx, BYTE [address998]
movzx esi, BYTE [address999]
ret

当然,所有使用的地址都将由生成指令的代码计算得出。

注意:根据具体的 CPU,使用循环可能比完全展开更快(指令获取和解码成本以及循环开销之间存在折衷)。对于较新的 Intel CPU,有一种称为循环流检测器的东西,旨在避免读取和解码小于特定大小的循环(该大小取决于 CPU 型号);我假设生成一个适合该大小的循环是最佳的。

关于assembly - 编写跨步 x86 基准测试,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47957099/

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