如何将此代码重写为 sse 内在函数
Posted
技术标签:
【中文标题】如何将此代码重写为 sse 内在函数【英文标题】:How to rewrite this code to sse intrinsics 【发布时间】:2014-12-06 15:58:22 【问题描述】:我是 sse 内在函数的新手,希望在使用这个 9 时提供一些提示帮助,因为这对我来说还很模糊)
我得到了这样的代码
for(int k=0; k<=n-4; k+=4)
int xc0 = 512 + ((idx + k*iddx)>>6);
int yc0 = 512 + ((idy + k*iddy)>>6);
int xc1 = 512 + ((idx + (k+1)*iddx)>>6);
int yc1 = 512 + ((idy + (k+1)*iddy)>>6);
int xc2 = 512 + ((idx + (k+2)*iddx)>>6);
int yc2 = 512 + ((idy + (k+2)*iddy)>>6);
int xc3 = 512 + ((idx + (k+3)*iddx)>>6);
int yc3 = 512 + ((idy + (k+3)*iddy)>>6);
unsigned color0 = working_buffer[yc0*working_buffer_size_x + xc0];
unsigned color1 = working_buffer[yc1*working_buffer_size_x + xc1];
unsigned color2 = working_buffer[yc2*working_buffer_size_x + xc2];
unsigned color3 = working_buffer[yc3*working_buffer_size_x + xc3];
int adr = base_adr + k;
frame_bitmap[adr] = color0;
frame_bitmap[adr+1]= color1;
frame_bitmap[adr+2]= color2;
frame_bitmap[adr+3]= color3;
这里的所有内容都是 int/unsigned,这是循环的关键部分,不确定整数 sse 是否有助于提高速度,但想知道它是否可以工作?有人可以帮忙吗?
(我使用mingw32)
【问题讨论】:
您能否对working_buffer
的实际访问模式进行去混淆处理?所以,简单的索引数学。这有点难以解码。我仍然不确定这是“奇怪的聚集”还是可以实际使用的模式。
看起来像“收集”类型的操作,所以你至少需要 AVX2。
working_buffer 是一个无符号颜色的纹理[][] - 数据有 1024 x 1024 虽然可悲的是 working_buffer 低维比 1024 大一点 - 虽然如果非常需要我可以重写一些代码来实现它只是未签名的 texture_bitmap[1024][1024]
xc yc 是纹理中的索引(应仅在 0 到 1023 、 0 到 1023 的纹理区域上运行)这是展开循环,因为展开它即使在标量代码中也可以使其更快;我不希望 sse 有明显的加速,但想习惯如何做这些事情,这对我来说很模糊(尤其是整数 intrisinc)
模式本身是针对给定的像素 i, j,转换为纹理坐标,读取纹理颜色并分配给 frame_bitmap - 尽管整个算法由瓦片 (12x12) 划分,这是瓦片中的 x 线(n 现在是 12,这是一次 4 个像素)
【参考方案1】:
我的sse有点生疏了,但是你应该做的是:
xmm0: [k, k+1, k+2, k+3] //xc0, xc1,....
xmm1: [k, k+1, k+2, k+3] //yc0, yc1,....
//initialize before the loop
xmm2: [512, 512, 512, 512]
xmm3: [idx, idx, idx, idx]
xmm4: [iddx, iddx, iddx, iddx]
xmm5: [idy, idy, idy, idy]
xmm6: [iddy, iddy, iddy, iddy]
xmm7: [working_buffer_size_x, working_buffer_size_x, working_buffer_size_x, working_buffer_size_x]
计算:
xmm0 * xmm4
xmm0 + xmm3
xmm0 >> 6
xmm0 + xmm2
xmm0: [xc0, xc1, xc2, xc3]
///////////////////////////////
xmm1 * xmm6
xmm1 + xmm5
xmm1 >> 6
xmm1 + xmm2
xmm1: [yc0, yc1, yc2, yc3]
xmm1 * xmm7
xmm1 + xmm0
现在xmm1
是:
xmm1: [yc0*working_buffer_size_x + xc0, yc1*working_buffer_size_x + xc1, yc2*working_buffer_size_x + xc2, yc3*working_buffer_size_x + xc3]
您在每个循环(working_buffer、frame_bitmap 数组)中读取和写入内存,这些操作比计算本身慢得多,因此速度提升不会像您预期的那么多。
编辑
您需要 working_buffer 和 frame_bitmap 数组进行对齐和SSE4.1:
#include <emmintrin.h>
#include <smmintrin.h> //SSE4.1
int a[4] __attribute__((aligned(16)));
__m128i xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7;
xmm2 = _mm_set1_epi32(512);
xmm3 = _mm_set1_epi32(idx);
xmm4 = _mm_set1_epi32(iddx);
xmm5 = _mm_set1_epi32(idy);
xmm6 = _mm_set1_epi32(iddy);
xmm7 = _mm_set1_epi32(working_buffer_size_x);
for(k = 0; k <= n - 4; k +=4)
xmm0 = _mm_set_epi32(k + 3, k + 2, k + 1, k);
xmm1 = _mm_set_epi32(k + 3, k + 2, k + 1, k);
//xmm0 * xmm4
xmm0 = _mm_mullo_epi32(xmm0, xmm4);
//xmm0 + xmm3
xmm0 = _mm_add_epi32(xmm0, xmm3);
//xmm0 >> 6
xmm0 = _mm_srai_epi32(xmm0, 6);
//xmm0 + xmm2
xmm0 = _mm_add_epi32(xmm0, xmm2);
//xmm1 * xmm6
xmm1 = _mm_mullo_epi32(xmm1, xmm6);
//xmm1 + xmm5
xmm1 = _mm_add_epi32(xmm1, xmm5);
//xmm1 >> 6
xmm1 = _mm_srai_epi32(xmm1, 6);
//xmm1 + xmm2
xmm1 = _mm_add_epi32(xmm1, xmm2);
//xmm1 * xmm7
xmm1 = _mm_mullo_epi32(xmm1, xmm7);
//xmm1 + xmm0
xmm1 = _mm_add_epi32(xmm1, xmm0);
//a[0] = yc0*working_buffer_size_x + xc0
//a[1] = yc1*working_buffer_size_x + xc1
//a[2] = yc2*working_buffer_size_x + xc2
//a[3] = yc3*working_buffer_size_x + xc3
_mm_store_si128((__m128i *)&a[0], xmm1);
unsigned color0 = working_buffer[ a[0] ];
unsigned color1 = working_buffer[ a[1] ];
unsigned color2 = working_buffer[ a[2] ];
unsigned color3 = working_buffer[ a[3] ];
int adr = base_adr + k;
frame_bitmap[adr] = color0;
frame_bitmap[adr+1]= color1;
frame_bitmap[adr+2]= color2;
frame_bitmap[adr+3]= color3;
您可以通过使用直接操作内存的程序集避免_mm_store_si128((__m128i *)&a[0], xmm1);
或int adr = base_adr + k;
来进一步优化它。
【讨论】:
好吧,我知道这里的速度改进可能并不大(如果有的话,在除法或 sqrt 的情况下会更多)但无论如何我想测试一下,如果一切正常跨度> ps 这是工作的一部分,也许有人可以继续做下去?我知道的助记符就像 __m128 a128 = _mm_load_ps(a); __m128 b128 = _mm_load_ps(b); __m128 out128 = _mm_div_ps(a128, b128); _mm_store_ps(out, out128); @user2214913 正如我所说,我确实在几个月前写过内在函数,所以我不记得它们了。但是你的例子很简单。我会看看我能做什么。 非常感谢 - 我会花一些时间来检查它以上是关于如何将此代码重写为 sse 内在函数的主要内容,如果未能解决你的问题,请参考以下文章