用 sse 执行内在函数
Posted
技术标签:
【中文标题】用 sse 执行内在函数【英文标题】:performance of intrinsic functions with sse 【发布时间】:2011-03-11 18:25:16 【问题描述】:我目前正在开始使用 SSE。
我之前关于 SSE 的问题 (Mutiplying vector by constant using SSE) 的答案让我想到了测试使用像 _mm_mul_ps()
这样的内在函数与像 *
这样只使用“普通运算符”(不确定最好的术语是什么)之间的区别的想法。
所以我写了两个测试用例,它们只是计算结果的方式不同: 方法一:
int main(void)
float4 a, b, c;
a.v = _mm_set_ps(1.0f, 2.0f, 3.0f, 4.0f);
b.v = _mm_set_ps(-1.0f, -2.0f, -3.0f, -4.0f);
printf("method 1\n");
c.v = a.v + b.v; // <---
print_vector(a);
print_vector(b);
printf("1.a) Computed output 1: ");
print_vector(c);
exit(EXIT_SUCCESS);
方法二:
int main(void)
float4 a, b, c;
a.v = _mm_set_ps(1.0f, 2.0f, 3.0f, 4.0f);
b.v = _mm_set_ps(-1.0f, -2.0f, -3.0f, -4.0f);
printf("\nmethod 2\n");
c.v = _mm_add_ps(a.v, b.v); // <---
print_vector(a);
print_vector(b);
printf("1.b) Computed output 2: ");
print_vector(c);
exit(EXIT_SUCCESS);
两个测试用例共享以下内容:
typedef union float4
__m128 v;
float x,y,z,w;
float4;
void print_vector (float4 v)
printf("%f,%f,%f,%f\n", v.x, v.y, v.z, v.w);
所以要比较我编译的两种情况生成的代码:gcc -ggdb -msse -c t_vectorExtensions_method1.c
导致(仅显示添加两个向量的部分 - 不同): 方法一:
c.v = a.v + b.v;
a1: 0f 57 c9 xorps %xmm1,%xmm1
a4: 0f 12 4d d0 movlps -0x30(%rbp),%xmm1
a8: 0f 16 4d d8 movhps -0x28(%rbp),%xmm1
ac: 0f 57 c0 xorps %xmm0,%xmm0
af: 0f 12 45 c0 movlps -0x40(%rbp),%xmm0
b3: 0f 16 45 c8 movhps -0x38(%rbp),%xmm0
b7: 0f 58 c1 addps %xmm1,%xmm0
ba: 0f 13 45 b0 movlps %xmm0,-0x50(%rbp)
be: 0f 17 45 b8 movhps %xmm0,-0x48(%rbp)
方法二:
c.v = _mm_add_ps(a.v, b.v);
a1: 0f 57 c0 xorps %xmm0,%xmm0
a4: 0f 12 45 a0 movlps -0x60(%rbp),%xmm0
a8: 0f 16 45 a8 movhps -0x58(%rbp),%xmm0
ac: 0f 57 c9 xorps %xmm1,%xmm1
af: 0f 12 4d b0 movlps -0x50(%rbp),%xmm1
b3: 0f 16 4d b8 movhps -0x48(%rbp),%xmm1
b7: 0f 13 4d f0 movlps %xmm1,-0x10(%rbp)
bb: 0f 17 4d f8 movhps %xmm1,-0x8(%rbp)
bf: 0f 13 45 e0 movlps %xmm0,-0x20(%rbp)
c3: 0f 17 45 e8 movhps %xmm0,-0x18(%rbp)
/* Perform the respective operation on the four SPFP values in A and B. */
extern __inline __m128 __attribute__((__gnu_inline__, __always_inline__, __artificial__))
_mm_add_ps (__m128 __A, __m128 __B)
return (__m128) __builtin_ia32_addps ((__v4sf)__A, (__v4sf)__B);
c7: 0f 57 c0 xorps %xmm0,%xmm0
ca: 0f 12 45 e0 movlps -0x20(%rbp),%xmm0
ce: 0f 16 45 e8 movhps -0x18(%rbp),%xmm0
d2: 0f 57 c9 xorps %xmm1,%xmm1
d5: 0f 12 4d f0 movlps -0x10(%rbp),%xmm1
d9: 0f 16 4d f8 movhps -0x8(%rbp),%xmm1
dd: 0f 58 c1 addps %xmm1,%xmm0
e0: 0f 13 45 90 movlps %xmm0,-0x70(%rbp)
e4: 0f 17 45 98 movhps %xmm0,-0x68(%rbp)
显然,使用内部_mm_add_ps()
时生成的代码要大得多。为什么是这样?它不应该产生更好的代码吗?
【问题讨论】:
使用gcc -ggdb -msse -O3 -c t_vectorExtensions_method1.c
编译后,两种情况都会生成完全相同的输出。所以使用内在函数没有任何好处。总是这样吗?
【参考方案1】:
真正重要的是addps
。在一个更现实的用例中,比如说,在循环中添加两个大的浮点向量,循环体将只包含addps
、两个加载和一个存储,以及一些用于地址算术的标量整数指令.在现代超标量 CPU 上,许多这些指令将并行执行。
还请注意,您在禁用优化的情况下进行编译,因此您不会获得特别高效的代码。试试gcc -O3 -msse3 ...
。
【讨论】:
用-O3
编译了两个案例,用于 sse、sse2 和 sse3。所有六种情况都生成完全相同的机器代码。 (因为这是一个简单的添加,这并不让我感到惊讶)。但是:当似乎没有区别时,使用内部函数有意义吗?使用它们会使代码变得难以阅读。
@emanuel:您无法将一个非常简单的测试用例外推到一般情况下。大多数现代编译器在自动矢量化代码(尤其是 ICC)方面可以做得很好,这就是您所看到的,但大多数编译器都适用于复杂代码或边缘情况。 Imo,坚持内在函数,让您的代码保持清晰,并且不会让您依赖编译器来做正确的事情
@Emanuel:例如能力使用+
等添加向量类型是特定于 gcc 的,并且只是可能的 SSE 操作的一小部分的有用简写。如果您要进行任何实质性的 SIMD 编程,您确实需要熟悉内在函数。以上是关于用 sse 执行内在函数的主要内容,如果未能解决你的问题,请参考以下文章