SSE _mm_dp_ps 尺寸结果
SSE _mm_dp_ps size result
我开始用SSE做操作了。我想用 _mm_dp_ps
生成两个点积并将第一个结果保存在 aux_sse
中,第二个结果保存在 aux_sse
中。 B 是值为 1 的 8 元素向量。
因为每对只需要两个浮点数,所以我完成了以下代码:
printf("A \n");
for(i = 0; i < M; i++){
for(j = 0; j < ele; j++){
A[i*ele+j] = i*ele+j;
printf(" %f ", A[i*ele+j]);
}
printf("\n");
}
float aux[ele*M];
float aux2[ele*M];
__m128 *A_sse = (__m128*) A;
__m128 *B_sse = (__m128*) B;
__m128 *aux_sse = (__m128*) aux;
__m128 *aux2_sse = (__m128*) aux2;
for(int i = 0; i < M; i++)
{
*aux_sse = _mm_dp_ps (*A_sse, *B_sse, 0xFF);
printf("%f \n", aux[i]);
B_sse ++;
A_sse++;
*aux2_sse = _mm_dp_ps (*A_sse, *B_sse, 0xFF);
printf("%f \n", aux2[i]);
B_sse --;
A_sse ++;
aux_sse+= sizeof(char);
aux2_sse+= sizeof(char);
}
我得到以下错误输出:
A
0.000000 1.000000 2.000000 3.000000 4.000000 5.000000 6.000000 7.000000
8.000000 9.000000 10.000000 11.000000 12.000000 13.000000 14.000000 15.000000
6.000000
22.000000
6.000000
22.000000
根据this:
Conditionally multiply the packed single-precision (32-bit) floating-point elements in a and b using the high 4 bits in imm8, sum the four products, and conditionally store the sum in dst using the low 4 bits of imm8.
我了解到在 imm8 中我们在元素中指定我们希望保存结果。
据我了解,即使结果在输出向量的4个元素中,如果我只用aux_sse+= sizeof(char)
增加一个元素,结果应该被覆盖并且会得到想要的结果出去。但是,我发现情况并非如此。
如果我在打印 aux 和 aux2 的结果时进行以下修改,则输出是正确的。
printf("%f \n", aux[i*4]);
printf("%f \n", aux2[i*4]);
输出:
6.000000
22.000000
38.000000
54.000000
我正在使用 gcc 编译器。
有谁知道问题出在哪里?任何答案都会有所帮助。
编辑:
我需要aux和aux2的元素对应每次迭代:
aux[i] = dot_product 在迭代 i
中执行
aux_sse+= sizeof(char);
是一种荒谬的写法 aux_sse+=1
,即前进 16 个字节,也就是 4 个浮点数,即 sizeof(__m128) == sizeof(*aux_sse) == 16
.
因此,如果您还通过浮点数索引访问数组,是的,如果您仅将 i
每个 4 个浮点数的向量递增 1,则必须将其缩放 4。
通常情况下,使用 _mm_store_ps(&aux[i], v);
而不是跟踪 __m128*
变量来访问相同的数组会更容易。并且 i+=4
所以 i
实际上是索引你拥有的 4 元素组的开始,而不是需要缩放它。这使得编写像 i < M-3
.
这样的循环边界变得更容易
另请注意,如果要对数组进行 alignment-required 访问,则应使用 alignas(16) float aux[ele*M];
。 GCC 会注意到您在做什么,并会在看到数组的使用方式时为您对齐数组,但一般不要指望它。
或者您是否只想存储一个 float
结果,而不是为每组 4 个输入存储 4 个相同的 dot-products?在那种情况下,您应该提取低标量元素,例如_mm_store_ss (&aux[i], v)
。或者 _mm_cvtss_f32(v)
将向量的低元素作为标量 float
.
如果需要,您可以手动执行四个 4 元素点积,生成 1 个包含 4 个结果的向量。 _mm_mul_ps
然后可能是 2x _mm_hadd_ps
(SSE3) 水平减少,一种转置和添加。 (由@mainactual 建议)
dpps
在 Skylake 和类似的 Intel CPU (https://uops.info) 上是 4 微指令,所以如果你有多个点积要做,那就不太好了。
为了避免 SSE3,您可以使用 _mm_shuffle_ps
(shufps) 从 2 个向量中选取元素,或者某些 _mm_unpacklo_ps
/ unpackhi 可能有用,或者 pd
将元素对保持在一起的版本。
我开始用SSE做操作了。我想用 _mm_dp_ps
生成两个点积并将第一个结果保存在 aux_sse
中,第二个结果保存在 aux_sse
中。 B 是值为 1 的 8 元素向量。
因为每对只需要两个浮点数,所以我完成了以下代码:
printf("A \n");
for(i = 0; i < M; i++){
for(j = 0; j < ele; j++){
A[i*ele+j] = i*ele+j;
printf(" %f ", A[i*ele+j]);
}
printf("\n");
}
float aux[ele*M];
float aux2[ele*M];
__m128 *A_sse = (__m128*) A;
__m128 *B_sse = (__m128*) B;
__m128 *aux_sse = (__m128*) aux;
__m128 *aux2_sse = (__m128*) aux2;
for(int i = 0; i < M; i++)
{
*aux_sse = _mm_dp_ps (*A_sse, *B_sse, 0xFF);
printf("%f \n", aux[i]);
B_sse ++;
A_sse++;
*aux2_sse = _mm_dp_ps (*A_sse, *B_sse, 0xFF);
printf("%f \n", aux2[i]);
B_sse --;
A_sse ++;
aux_sse+= sizeof(char);
aux2_sse+= sizeof(char);
}
我得到以下错误输出:
A
0.000000 1.000000 2.000000 3.000000 4.000000 5.000000 6.000000 7.000000
8.000000 9.000000 10.000000 11.000000 12.000000 13.000000 14.000000 15.000000
6.000000
22.000000
6.000000
22.000000
根据this:
Conditionally multiply the packed single-precision (32-bit) floating-point elements in a and b using the high 4 bits in imm8, sum the four products, and conditionally store the sum in dst using the low 4 bits of imm8.
我了解到在 imm8 中我们在元素中指定我们希望保存结果。
据我了解,即使结果在输出向量的4个元素中,如果我只用aux_sse+= sizeof(char)
增加一个元素,结果应该被覆盖并且会得到想要的结果出去。但是,我发现情况并非如此。
如果我在打印 aux 和 aux2 的结果时进行以下修改,则输出是正确的。
printf("%f \n", aux[i*4]);
printf("%f \n", aux2[i*4]);
输出:
6.000000
22.000000
38.000000
54.000000
我正在使用 gcc 编译器。 有谁知道问题出在哪里?任何答案都会有所帮助。
编辑:
我需要aux和aux2的元素对应每次迭代:
aux[i] = dot_product 在迭代 i
中执行aux_sse+= sizeof(char);
是一种荒谬的写法 aux_sse+=1
,即前进 16 个字节,也就是 4 个浮点数,即 sizeof(__m128) == sizeof(*aux_sse) == 16
.
因此,如果您还通过浮点数索引访问数组,是的,如果您仅将 i
每个 4 个浮点数的向量递增 1,则必须将其缩放 4。
通常情况下,使用 _mm_store_ps(&aux[i], v);
而不是跟踪 __m128*
变量来访问相同的数组会更容易。并且 i+=4
所以 i
实际上是索引你拥有的 4 元素组的开始,而不是需要缩放它。这使得编写像 i < M-3
.
另请注意,如果要对数组进行 alignment-required 访问,则应使用 alignas(16) float aux[ele*M];
。 GCC 会注意到您在做什么,并会在看到数组的使用方式时为您对齐数组,但一般不要指望它。
或者您是否只想存储一个 float
结果,而不是为每组 4 个输入存储 4 个相同的 dot-products?在那种情况下,您应该提取低标量元素,例如_mm_store_ss (&aux[i], v)
。或者 _mm_cvtss_f32(v)
将向量的低元素作为标量 float
.
如果需要,您可以手动执行四个 4 元素点积,生成 1 个包含 4 个结果的向量。 _mm_mul_ps
然后可能是 2x _mm_hadd_ps
(SSE3) 水平减少,一种转置和添加。 (由@mainactual 建议)
dpps
在 Skylake 和类似的 Intel CPU (https://uops.info) 上是 4 微指令,所以如果你有多个点积要做,那就不太好了。
为了避免 SSE3,您可以使用 _mm_shuffle_ps
(shufps) 从 2 个向量中选取元素,或者某些 _mm_unpacklo_ps
/ unpackhi 可能有用,或者 pd
将元素对保持在一起的版本。