gpt4 book ai didi

c# - 了解 C# SIMD 输出

转载 作者:行者123 更新时间:2023-12-04 07:45:29 29 4
gpt4 key购买 nike

我有以下代码段,它总结了数组的所有元素(大小是硬编码的,是 32 ):

static unsafe int F(int* a) 
{
Vector256<int> ymm0 = Avx2.LoadVector256(a + 0);
Vector256<int> ymm1 = Avx2.LoadVector256(a + 8);
Vector256<int> ymm2 = Avx2.LoadVector256(a + 16);
Vector256<int> ymm3 = Avx2.LoadVector256(a + 24);

ymm0 = Avx2.Add(ymm0, ymm1);
ymm2 = Avx2.Add(ymm2, ymm3);

ymm0 = Avx2.Add(ymm0, ymm2);

const int s = 256 / 32;
int* t = stackalloc int[s];

Avx2.Store(t, ymm0);

int r = 0;
for (int i = 0; i < s; ++i)
r += t[i];

return r;
}
这会生成以下 ASM :
Program.F(Int32*)
L0000: sub rsp, 0x28
L0004: vzeroupper ; Question #1
L0007: vxorps xmm4, xmm4, xmm4
L000b: vmovdqa [rsp], xmm4 ; Question #2
L0010: vmovdqa [rsp+0x10], xmm4 ; Question #2
L0016: xor eax, eax ; Question #3
L0018: mov [rsp+0x20], rax
L001d: mov rax, 0x7d847bd1f9ce ; Question #4
L0027: mov [rsp+0x20], rax
L002c: vmovdqu ymm0, [rcx]
L0030: vmovdqu ymm1, [rcx+0x20]
L0035: vmovdqu ymm2, [rcx+0x40]
L003a: vmovdqu ymm3, [rcx+0x60]
L003f: vpaddd ymm0, ymm0, ymm1
L0043: vpaddd ymm2, ymm2, ymm3
L0047: vpaddd ymm0, ymm0, ymm2
L004b: lea rax, [rsp] ; Question #5
L004f: vmovdqu [rax], ymm0
L0053: xor edx, edx ; Question #5
L0055: xor ecx, ecx ; Question #5
L0057: movsxd r8, ecx
L005a: add edx, [rax+r8*4]
L005e: inc ecx
L0060: cmp ecx, 8
L0063: jl short L0057
L0065: mov eax, edx
L0067: mov rcx, 0x7d847bd1f9ce ; Question #4
L0071: cmp [rsp+0x20], rcx
L0076: je short L007d
L0078: call 0x00007ffc9de2d430 ; Question #6
L007d: nop
L007e: vzeroupper
L0081: add rsp, 0x28
L0085: ret
问题
  • 为什么我们需要VZEROUPPER一开始。没有它不是很好吗?
  • VMOVDQA做什么的一开始就做。或者更确切地说,他们为什么在那里?
  • 清零 EAX登记?为什么?可能与下一行有关 MOV [RSP+0x20], RAX ,但还是看不懂。
  • 这个神秘的值 ( 0x7d847bd1f9ce ) 有什么作用?
  • 中间也有几行我不明白为什么需要它们(请参阅代码中的“问题 #5”注释)。
  • 我假设这一行( L0078: call 0x00007ffc9de2d430 )引发异常。我的代码中是否有函数或其他东西可以抛出异常?

  • 我知道有很多问题,但我无法将它们分开,因为我认为它们彼此相关。清晰明了:我只是想了解生成的 ASM这里。我不是这方面的专业人士。
    笔记
  • 如果您想知道什么 GCC (O2)生成,这里是结果:

  • int32_t
    f(int32_t *a) {
    __m256i ymm0;
    __m256i ymm1;
    __m256i ymm2;
    __m256i ymm3;

    ymm0 = _mm256_load_si256((__m256i*)(a + 0));
    ymm1 = _mm256_load_si256((__m256i*)(a + 8));
    ymm2 = _mm256_load_si256((__m256i*)(a + 16));
    ymm3 = _mm256_load_si256((__m256i*)(a + 24));

    ymm0 = _mm256_add_epi32(ymm0, ymm1);
    ymm2 = _mm256_add_epi32(ymm2, ymm3);

    ymm0 = _mm256_add_epi32(ymm0, ymm2);

    int32_t t[8];
    _mm256_store_si256((__m256i*)t, ymm0);

    int32_t r;
    r = 0;
    for (int i = 0; i < 8; ++i)
    r += t[i];

    return r;
    }
    和生成的 ASM :
    f:
    push rbp
    xor r8d, r8d
    mov rbp, rsp
    and rsp, -32
    lea rax, [rsp-32]
    mov rdx, rsp
    vmovdqa ymm1, YMMWORD PTR [rdi+96]
    vpaddd ymm0, ymm1, YMMWORD PTR [rdi+64]
    vpaddd ymm0, ymm0, YMMWORD PTR [rdi+32]
    vpaddd ymm0, ymm0, YMMWORD PTR [rdi]
    vmovdqa YMMWORD PTR [rsp-32], ymm0
    .L2:
    add r8d, DWORD PTR [rax]
    add rax, 4
    cmp rax, rdx
    jne .L2
    mov eax, r8d
    vzeroupper
    leave
    ret
    我认为它在这里优化了(可能很重)我的代码,但无论如何。

    最佳答案

    vzeroupper 可以帮助性能。L0007直通 L0018行将局部变量使用的存储空间归零。0x7d847bd1f9ce value 似乎与检测堆栈溢出有关。它设置一个检查值,当函数完成时,它会查看该值是否已更改。如果有,则调用诊断功能。
    函数体从 L002c 开始.首先它初始化你的本地 ymm变量,然后进行加法。leaL004bt的分配.下一条指令( L004f )是 Avx2.Store(t, ymm0);陈述。L0053直通 L0063是for循环。 rax已经具有 t 的值, ecx持有 i , 和 edx持有 r .
    来自 L0065最后,我们有 return 语句和函数结语。 Epilog 检查堆栈是否已被破坏,进行一些清理,然后返回给调用者。

    关于c# - 了解 C# SIMD 输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67218961/

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