当前位置: 首页 > 知识库问答 >
问题:

在 x86 上失败的存储到负载转发的成本是多少?

夹谷志
2023-03-14

在最近的x86体系结构上,失败的存储到加载转发的成本是多少?

特别是,由于加载与先前的存储部分重叠,或者由于先前的加载或存储跨越了导致转发失败的某些对齐边界而失败的存储到加载转发。

当然存在延迟成本:它有多大?是否还存在吞吐量成本,例如,失败的存储到加载转发是否使用了其他加载和存储甚至其他非内存操作不可用的额外资源?

当存储的所有部分都来自存储缓冲区时,与存储缓冲区和L1混合的情况相比,是否存在差异?

共有2个答案

薛高澹
2023-03-14

在英特尔桑迪桥系列中,商店转发摊位不能与其他商店转发摊位一起进行管道传输。即使在独立地址上,它们也会在吞吐量方面发生冲突。请参阅商店转发示例,常春藤桥的一个更有趣的实验,以及Alex对咖啡湖(Skylake衍生物)的答案。

但是失败的(慢速路径)存储转发似乎不会阻碍成功的存储转发。在Skylake (i7-6700k)上测试时,我做了一个测试循环,其中包括两个依赖链:

  • 存储/更宽的重新加载导致存储转发失败。仅凭这一点,每次迭代就有 15 个周期,该延迟瓶颈。
  • 3x 正常存储/重新加载:每次迭代约 13.425 个周期。
  • 两个依赖关系链并行:每次迭代15或16个周期,具体取决于事物的位置。
;; nasm -felf64 testloop.asm
;; ld -o testloop testloop.o
;; taskset -c 3 perf stat -etask-clock:u,context-switches:u,cpu-migrations:u,page-faults:u,cycles:u,branches:u,instructions:u,uops_issued.any:u,uops_executed.thread:u,idq.dsb_uops:u -r1 ./testloop

default rel
%ifdef __YASM_VER__
    CPU Conroe AMD
    CPU Skylake AMD
%else
%use smartalign
alignmode p6, 64
%endif

global _start
_start:

    lea        rdi, [buf]
    mov     ebp, 100000000

align 64
.loop:
    mov [rdi+64], ecx
;    mov  rcx, [rdi+64]   ; reload here: 16c.  Or 16.8 if we *also* reload after the %rep block
%rep 3
    mov [rdi], eax
    mov eax, [rdi]
%endrep
    mov  rcx, [rdi+64]     ; reload here: 15c
    dec ebp
    jnz .loop
.end:

;;NASM-only, not YASM:   %if __BITS__ == 32
%ifidn __OUTPUT_FORMAT__, elf32
    mov eax,1
    xor ebx,ebx
    int 0x80     ; sys_exit(0) 32-bit ABI
%else
    xor edi,edi
    mov eax,231   ; __NR_exit_group  from /usr/include/asm/unistd_64.h
    syscall       ; sys_exit_group(0)
%endif

section .bss
align 4096
buf:    resb 4096

性能结果:

$ t=testloop; asm-link -dn "$t".asm && taskset -c 3 perf stat --all-user -etask-clock,context-switches,cpu-migrations,page-faults,cycles,instructions,uops_issued.any,uops_executed.thread,ld_blocks.store_forward,resource_stalls.sb -r2 ./"$t"
+ nasm -felf64 -Worphan-labels testloop.asm
+ ld -o testloop testloop.o

testloop:     file format elf64-x86-64


Disassembly of section .text:

0000000000401000 <_start>:
  401000:       48 8d 3d f9 0f 00 00    lea    rdi,[rip+0xff9]        # 402000 <__bss_start>
  401007:       bd 00 e1 f5 05          mov    ebp,0x5f5e100
  40100c:       0f 1f 84 00 00 00 00 00         nop    DWORD PTR [rax+rax*1+0x0]
  401014:       0f 1f 84 00 00 00 00 00         nop    DWORD PTR [rax+rax*1+0x0]
  40101c:       0f 1f 84 00 00 00 00 00         nop    DWORD PTR [rax+rax*1+0x0]
  401024:       0f 1f 84 00 00 00 00 00         nop    DWORD PTR [rax+rax*1+0x0]
  40102c:       0f 1f 84 00 00 00 00 00         nop    DWORD PTR [rax+rax*1+0x0]
  401034:       0f 1f 84 00 00 00 00 00         nop    DWORD PTR [rax+rax*1+0x0]
  40103c:       0f 1f 40 00             nop    DWORD PTR [rax+0x0]

0000000000401040 <_start.loop>:
  401040:       89 4f 40                mov    DWORD PTR [rdi+0x40],ecx
  401043:       89 07                   mov    DWORD PTR [rdi],eax
  401045:       8b 07                   mov    eax,DWORD PTR [rdi]
  401047:       89 07                   mov    DWORD PTR [rdi],eax
  401049:       8b 07                   mov    eax,DWORD PTR [rdi]
  40104b:       89 07                   mov    DWORD PTR [rdi],eax
  40104d:       8b 07                   mov    eax,DWORD PTR [rdi]
  40104f:       48 8b 4f 40             mov    rcx,QWORD PTR [rdi+0x40]
  401053:       ff cd                   dec    ebp
  401055:       75 e9                   jne    401040 <_start.loop>

0000000000401057 <_start.end>:
  401057:       31 ff                   xor    edi,edi
  401059:       b8 e7 00 00 00          mov    eax,0xe7
  40105e:       0f 05                   syscall

Performance counter stats for './testloop' (two runs):

            385.85 msec task-clock                #    0.999 CPUs utilized            ( +-  0.02% )
                 0      context-switches          #    0.000 /sec
                 0      cpu-migrations            #    0.000 /sec
                 2      page-faults               #    5.183 /sec
     1,503,701,305      cycles                    #    3.897 GHz                      ( +-  0.01% )
     1,000,000,130      instructions              #    0.67  instructions per cycle           ( +-  0.00% )
       900,084,383      uops_issued.any           #    2.333 G/sec                    ( +-  0.00% )
     1,300,091,135      uops_executed.thread      #    3.369 G/sec                    ( +-  0.00% )
        99,933,928      ld_blocks.store_forward   #  258.998 M/sec                    ( +-  0.02% )
       443,686,304      resource_stalls.sb        #    1.150 G/sec                    ( +-  4.87% )

          0.386139 +- 0.000119 seconds time elapsed  ( +-  0.03% )
李洋
2023-03-14

这并不是一个完整的答案,但仍然证明了惩罚是可见的。

MSVC 2022 基准测试,编译器与 /标准:c 最新

#include <chrono>
#include <iostream>

struct alignas(16) S
{
    char* a;
    int* b;
};

extern "C" void init_fused_copy_unfused(int n, S & s2, S & s1);
extern "C" void init_fused_copy_fused(int n, S & s2, S & s1);
extern "C" void init_unfused_copy_unfused(int n, S & s2, S & s1);
extern "C" void init_unfused_copy_fused(int n, S & s2, S & s1);

int main()
{
    using namespace std::chrono;

    S s1, s2;
    constexpr int N = 1'000'000'000;
    auto t1 = system_clock::now();
    init_fused_copy_fused(N, s2, s1);
    auto t2 = system_clock::now();
    init_fused_copy_unfused(N, s2, s1);
    auto t3 = system_clock::now();
    init_unfused_copy_fused(N, s2, s1);
    auto t4 = system_clock::now();
    init_unfused_copy_unfused(N, s2, s1);
    auto t5 = system_clock::now();

    std::cout
     << "init fused copy fused     " << duration_cast<duration<double>>(t2 - t1) << "\n"
     << "init fused copy unfused   " << duration_cast<duration<double>>(t3 - t2) << "\n"
     << "init unfused copy fused   " << duration_cast<duration<double>>(t4 - t3) << "\n"
     << "init unfused copy unfused " << duration_cast<duration<double>>(t5 - t4) << "\n";
}
.code

c     db 0
i     dd 0

s     dq byte ptr [c], dword ptr [i]

ALIGN 16
init_fused_copy_fused PROC
    movups      xmm0,xmmword ptr [s]
    movups      xmmword ptr [r8],xmm0

    movups      xmm1,xmmword ptr [r8]
    movups      xmmword ptr [rdx], xmm1

    dec ecx
    jnz init_fused_copy_fused
    ret
init_fused_copy_fused ENDP


ALIGN 16
init_unfused_copy_fused PROC

    lea         rax, byte ptr [c]
    mov         qword ptr[r8], rax
    lea         rax, dword ptr [i]
    mov         qword ptr[r8 + 8], rax

    movups      xmm1,xmmword ptr [r8]
    movups      xmmword ptr [rdx], xmm1

    dec ecx
    jnz init_unfused_copy_fused
    ret
init_unfused_copy_fused ENDP

ALIGN 16
init_fused_copy_unfused PROC
    movups      xmm0,xmmword ptr [s]
    movups      xmmword ptr [r8],xmm0

    mov         rax, qword ptr[r8]
    mov         qword ptr[rdx], rax
    mov         rax, qword ptr[r8 + 8]
    mov         qword ptr[rdx +8], rax

    dec ecx
    jnz init_fused_copy_unfused
    ret
init_fused_copy_unfused ENDP


ALIGN 16
init_unfused_copy_unfused PROC

    lea         rax, byte ptr [c]
    mov         qword ptr[r8], rax
    lea         rax, dword ptr [i]
    mov         qword ptr[r8 + 8], rax

    mov         rax, qword ptr[r8]
    mov         qword ptr[rdx], rax
    mov         rax, qword ptr[r8 + 8]
    mov         qword ptr[rdx +8], rax

    dec ecx
    jnz init_unfused_copy_unfused
    ret
init_unfused_copy_unfused ENDP

END
init fused copy fused     0.664739s
init fused copy unfused   0.935631s
init unfused copy fused   4.34326s
init unfused copy unfused 1.02741s

CPU:Intel(R)Core(TM)i7-8750H CPU@2.20GHz 2.21 GHz

我对结果的解释如下:

  • 使用fused init,转发永远不会失败。融合和未融合拷贝之间的差异在基准误差范围内。
  • 使用unfused init,融合副本会导致转发失败,从而导致显著的性能差异
 类似资料:
  • 最后一个2字节加载从紧邻的前一个存储区中获取第二个字节,但从前一个存储区中获取第一个字节。这个加载可以被存储转发吗,还是需要等到前面的两个存储都提交到L1? 请注意,通过这里的存储转发,我包含了任何机制,这些机制可以满足来自仍然在存储缓冲区中的存储的读取,而不是等待它们提交到L1,即使这是一个比“从单个存储转发”的最佳情况更慢的路径。

  • 我正在使用react-native-firebase与我们的Firebase帐户一起进行身份验证、firestore和存储。尝试将照片上载到存储区失败,出现未知错误。以下是尝试的代码: 在iOS模拟器中测试,并使用调试器检测错误,我只是返回了这个错误: https://github.com/invertase/react-native-firebase/issues/1177 https://gi

  • 我在尝试将文件上载到blob存储时遇到此错误。在本地主机上运行和在Azure函数中运行时都会出现错误。 我的连接字符串如下所示:DefaultEndpoint sProtocol=https; AcCountName=xxx; AcCountKey=xxx; Endpoint Suffix=core.windows.net 身份验证信息的格式不正确。检查授权标头的值。时间:2021 10月14日1

  • 我有一个git存储库,它完全存在于我的PC中的本地。我想在github.com上创建它的远程克隆。怎么做呢?

  • 我记得在我的体系结构类中,假设一级缓存命中率为1个周期(即与寄存器访问时间相同),但在现代x86处理器上,这是真的吗? L1缓存命中需要多少个周期?它与注册访问相比如何?

  • > 出错原因:任务“:app:mergereleaseResources”执行失败。 [drawable-HDPI-v4/node_modules_reactnavigation_stack_src_views_assets_backicon]C:\users\victo\documents\git\melhor\app-user\android\app\src\srp\src\src\src\s