gpt4 book ai didi

c++ - 有没有办法利用所有 XMM 寄存器?

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:29:49 24 4
gpt4 key购买 nike

这是一个代码片段,用于计算从中获取的 float 组中的值的平方根 http://felix.abecassis.me/2011/09/cpp-getting-started-with-sse/

void sse(float* a, int N)
{
// We assume N % 4 == 0.
int nb_iters = N / 4;
__m128* ptr = (__m128*)a;


for (int i = 0; i < nb_iters; ++i, ++ptr, a += 4){
_mm_store_ps(a, _mm_sqrt_ps(*ptr));
}

}

当我分解这段代码时,我看到只使用了一个 xmm (xmm0)。我假设展开循环会给编译器一个可以使用更多 xmm 的提示。我将代码修改为

void sse3(float* a, int N)
{
__m128* ptr = (__m128*)a;

for (int i = 0; i < N; i+=32){

_mm_store_ps(a + i, _mm_sqrt_ps(*ptr));
ptr++;
_mm_store_ps(a + i + 4, _mm_sqrt_ps(*ptr));
ptr++;
_mm_store_ps(a + i + 8, _mm_sqrt_ps(*ptr));
ptr++;
_mm_store_ps(a + i + 12, _mm_sqrt_ps(*ptr));
ptr++;
_mm_store_ps(a + i + 16, _mm_sqrt_ps(*ptr));
ptr++;
_mm_store_ps(a + i + 20, _mm_sqrt_ps(*ptr));
ptr++;
_mm_store_ps(a + i + 24, _mm_sqrt_ps(*ptr));
ptr++;
_mm_store_ps(a + i + 28, _mm_sqrt_ps(*ptr));
ptr++;
}
}

在这种情况下,N 应大于 32。但是我仍然看不到一个 xmm。为什么编译器不能分配一个以上的 xmm?

我的理解是 xmm0、xmm1、xmm2 ... xmm7 上的计算是独立的,可以在现代超标量架构上并行进行。在 4 路超标量机器上,第二个代码片段应该给我 4 的理论加速(例如,如果分配了 4 个不同的 xmm)。

PS:第二个代码片段似乎快了一点(一致)。

sse3: 18809 microseconds
sse: 20543 microseconds

更新

按照建议使用 -O3 标志

这是 Ben Voigt 回答的反汇编 -请注意,我将函数的名称更改为 sse4。

147:ssetest.cpp   **** void sse4(float* a, int N)
148:ssetest.cpp **** {
2076 .loc 8 148 0
2077 .cfi_startproc
2078 .LVL173:
2079 .LBB5900:
2080 .LBB5901:
149:ssetest.cpp **** __m128 b, c, d, e;
150:ssetest.cpp ****
151:ssetest.cpp **** for (int i = 0; i < N; i += 16) {
2081 .loc 8 151 0
2082 0320 85F6 testl %esi, %esi # N
2083 0322 7E4C jle .L106 #,
147:ssetest.cpp **** void sse4(float* a, int N)
2084 .loc 8 147 0
2085 0324 8D56FF leal -1(%rsi), %edx #, tmp104
2086 .LBE5901:
2087 .LBE5900:
2088 0327 31C0 xorl %eax, %eax # ivtmp.1046
2089 .LBB5925:
2090 .LBB5924:
2091 0329 C1EA04 shrl $4, %edx #,
2092 032c 4883C201 addq $1, %rdx #, D.189746
2093 0330 48C1E206 salq $6, %rdx #, D.189746
2094 .LVL174:
2095 .p2align 4,,10
2096 0334 0F1F4000 .p2align 3
2097 .L108:
2098 .LBB5902:
2099 .LBB5903:
899:/usr/lib/gcc/x86_64-linux-gnu/4.6/include/xmmintrin.h **** return (__m128) *(__v4sf *)__P;
2100 .loc 9 899 0 discriminator 2
2101 0338 0F285407 movaps 16(%rdi,%rax), %xmm2 # MEM[base: a_7(D), index: ivtmp.1046_85, offset: 16B], c
2101 10
2102 .LVL175:
2103 .LBE5903:
2104 .LBE5902:
2105 .LBB5904:
2106 .LBB5905:
182:/usr/lib/gcc/x86_64-linux-gnu/4.6/include/xmmintrin.h **** return (__m128) __builtin_ia32_sqrtps ((__v4sf)__A);
2107 .loc 9 182 0 discriminator 2
2108 033d 0F511C07 sqrtps (%rdi,%rax), %xmm3 # MEM[base: a_7(D), index: ivtmp.1046_85, offset: 0B], tmp107
2109 .LBE5905:
2110 .LBE5904:
2111 .LBB5906:
2112 .LBB5907:
899:/usr/lib/gcc/x86_64-linux-gnu/4.6/include/xmmintrin.h **** return (__m128) *(__v4sf *)__P;
2113 .loc 9 899 0 discriminator 2
2114 0341 0F284C07 movaps 32(%rdi,%rax), %xmm1 # MEM[base: a_7(D), index: ivtmp.1046_85, offset: 32B], d
2114 20
2115 .LVL176:
2116 .LBE5907:
2117 .LBE5906:
2118 .LBB5908:
2119 .LBB5909:
182:/usr/lib/gcc/x86_64-linux-gnu/4.6/include/xmmintrin.h **** return (__m128) __builtin_ia32_sqrtps ((__v4sf)__A);
2120 .loc 9 182 0 discriminator 2
2121 0346 0F51D2 sqrtps %xmm2, %xmm2 # c, tmp109
2122 .LBE5909:
2123 .LBE5908:
2124 .LBB5910:
2125 .LBB5911:
899:/usr/lib/gcc/x86_64-linux-gnu/4.6/include/xmmintrin.h **** return (__m128) *(__v4sf *)__P;
2126 .loc 9 899 0 discriminator 2
2127 0349 0F284407 movaps 48(%rdi,%rax), %xmm0 # MEM[base: a_7(D), index: ivtmp.1046_85, offset: 48B], e
2127 30
2128 .LVL177:
2129 .LBE5911:
2130 .LBE5910:
2131 .LBB5912:
2132 .LBB5913:
182:/usr/lib/gcc/x86_64-linux-gnu/4.6/include/xmmintrin.h **** return (__m128) __builtin_ia32_sqrtps ((__v4sf)__A);
2133 .loc 9 182 0 discriminator 2
2134 034e 0F51C9 sqrtps %xmm1, %xmm1 # d, tmp111
2135 .LBE5913:
2136 .LBE5912:
2137 .LBB5914:
2138 .LBB5915:
2139 .loc 9 948 0 discriminator 2
2140 0351 0F291C07 movaps %xmm3, (%rdi,%rax) # tmp107, MEM[base: a_7(D), index: ivtmp.1046_85, offset: 0B]
2141 .LVL178:
2142 .LBE5915:
2143 .LBE5914:
2144 .LBB5916:
2145 .LBB5917:
182:/usr/lib/gcc/x86_64-linux-gnu/4.6/include/xmmintrin.h **** return (__m128) __builtin_ia32_sqrtps ((__v4sf)__A);
2146 .loc 9 182 0 discriminator 2
2147 0355 0F51C0 sqrtps %xmm0, %xmm0 # e, tmp113
2148 .LBE5917:
2149 .LBE5916:
2150 .LBB5918:
2151 .LBB5919:
2152 .loc 9 948 0 discriminator 2
2153 0358 0F295407 movaps %xmm2, 16(%rdi,%rax) # tmp109, MEM[base: a_7(D), index: ivtmp.1046_85, offset: 16B]
2153 10
2154 .LVL179:
2155 .LBE5919:
2156 .LBE5918:
2157 .LBB5920:
2158 .LBB5921:
2159 035d 0F294C07 movaps %xmm1, 32(%rdi,%rax) # tmp111, MEM[base: a_7(D), index: ivtmp.1046_85, offset: 32B]
2159 20
2160 .LVL180:
2161 .LBE5921:
2162 .LBE5920:
2163 .LBB5922:
2164 .LBB5923:
2165 0362 0F294407 movaps %xmm0, 48(%rdi,%rax) # tmp113, MEM[base: a_7(D), index: ivtmp.1046_85, offset: 48B]
2165 30
2166 0367 4883C040 addq $64, %rax #, ivtmp.1046
2167 .LVL181:
2168 .LBE5923:
2169 .LBE5922:
2170 .loc 8 151 0 discriminator 2
2171 036b 4839D0 cmpq %rdx, %rax # D.189746, ivtmp.1046
2172 036e 75C8 jne .L108 #,
2173 .LVL182:
2174 .L106:
2175 0370 F3 rep
2176 0371 C3 ret
2177 .LBE5924:
2178 .LBE5925:
2179 .cfi_endproc
2180 .LFE7998:
2182 0372 66666666 .p2align 4,,15
2182 662E0F1F
2182 84000000
2182 0000
2183 .globl _Z6normalPfi
2185 _Z6normalPfi:
2186 .LFB7999:
152:ssetest.cpp **** b = _mm_load_ps(a + i);
153:ssetest.cpp **** c = _mm_load_ps(a + i + 4);
154:ssetest.cpp **** d = _mm_load_ps(a + i + 8);
155:ssetest.cpp **** e = _mm_load_ps(a + i + 12);
156:ssetest.cpp **** _mm_store_ps(a + i, _mm_sqrt_ps(b));
157:ssetest.cpp **** _mm_store_ps(a + i + 4, _mm_sqrt_ps(c));
158:ssetest.cpp **** _mm_store_ps(a + i + 8, _mm_sqrt_ps(d));
159:ssetest.cpp **** _mm_store_ps(a + i + 12, _mm_sqrt_ps(e));
160:ssetest.cpp **** }
161:ssetest.cpp **** }

奇怪的是,sse 和 sse4 的性能几乎相同,sse3 性能最差(尽管循环的一部分被展开)。

最佳答案

怎么样:

void sse3(float* a, int N)
{
__m128 b, c, d, e;

for (int i = 0; i < N; i += 16) {
b = _mm_load_ps(a + i);
c = _mm_load_ps(a + i + 4);
d = _mm_load_ps(a + i + 8);
e = _mm_load_ps(a + i + 12);
_mm_store_ps(a + i, _mm_sqrt_ps(b));
_mm_store_ps(a + i + 4, _mm_sqrt_ps(c));
_mm_store_ps(a + i + 8, _mm_sqrt_ps(d));
_mm_store_ps(a + i + 12, _mm_sqrt_ps(e));
}
}

注意:使用相同的 XMM 寄存器进行多次计算是可以扼杀流水线的一件事,但这不是唯一的事情。只有在其他资源充足的情况下,不同寄存器的操作才能独立进行。没有一个完整的 SIMD 计算单元专用于每个寄存器,正如您的评论表明您相信的那样。

关于c++ - 有没有办法利用所有 XMM 寄存器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20159206/

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