求幂的 SIMD 代码
Posted
技术标签:
【中文标题】求幂的 SIMD 代码【英文标题】:SIMD code for exponentiation 【发布时间】:2010-10-25 07:21:00 【问题描述】:我正在使用 SIMD 来计算快速求幂结果。我将时间与非 simd 代码进行比较。取幂是使用平方和乘法算法实现的。
普通(非simd)版本代码:
b = 1;
for (i=WPE-1; i>=0; --i)
ew = e[i];
for(j=0; j<BPW; ++j)
b = (b * b) % p;
if (ew & 0x80000000U) b = (b * a) % p;
ew <<= 1;
SIMD 版本:
B.data[0] = B.data[1] = B.data[2] = B.data[3] = 1U;
P.data[0] = P.data[1] = P.data[2] = P.data[3] = p;
for (i=WPE-1; i>=0; --i)
EW.data[0] = e1[i]; EW.data[1] = e2[i]; EW.data[2] = e3[i]; EW.data[3] = e4[i];
for (j=0; j<BPW;++j)
B.v *= B.v; B.v -= (B.v / P.v) * P.v;
EWV.v = _mm_srli_epi32(EW.v,31);
M.data[0] = (EWV.data[0]) ? a1 : 1U;
M.data[1] = (EWV.data[1]) ? a2 : 1U;
M.data[2] = (EWV.data[2]) ? a3 : 1U;
M.data[3] = (EWV.data[3]) ? a4 : 1U;
B.v *= M.v; B.v -= (B.v / P.v) * P.v;
EW.v = _mm_slli_epi32(EW.v,1);
问题是虽然计算正确,但 simd 版本比非 simd 版本花费更多时间。
请帮我调试原因。也欢迎任何有关 SIMD 编码的建议。
感谢和问候, 阿努普。
【问题讨论】:
【参考方案1】:for 循环中的所有函数都应该是 SIMD 函数,而不仅仅是两个。为您的 2 个函数设置参数所花费的时间不如原始示例(最有可能由编译器优化)
【讨论】:
+1:在标量代码和 SIMD 代码之间移动代价高昂 - 任何给定循环的 SIMD 优化都需要“全有或全无” 你的意思是我需要替换SIMD对应的赋值、乘法、除法运算吗?我正在使用 SSE2。我看到对于上面的例子,没有任何乘法函数可以一次性计算 4 个 32 位数字的乘积。这同样适用于除法。那该怎么办呢? @anup 我看到您正在将一些数据从 e1,e2,e3,e4 数组复制到 EW.data 数组。那很不好。然后,您正在对该数据进行一些操作。从 SSE2 函数中,您只是在使用 shift。如果 SSE2 没有您需要的功能,那么您就不能使用它。或者你必须做一些聪明的事情 好吧,我是 SIMD 的新手,因此我对它以及如何进行个别操作了解不多。你能解释一下为什么这些作业不好吗?【参考方案2】:32 位 int 数据的 SIMD 循环通常如下所示:
for (i = 0; i < N; i += 4)
// load input vector(s) with data at array index i..i+3
__m128 va = _mm_load_si128(&A[i]);
__m128 vb = _mm_load_si128(&B[i]);
// process vectors using SIMD instructions (i.e. no scalar code)
__m128 vc = _mm_add_epi32(va, vb);
// store result vector(s) at array index i..i+3
_mm_store_si128(&C[i], vc);
如果您发现需要在循环内的标量代码和 SIMD 代码之间移动,那么您可能不会从 SIMD 优化中获得任何收益。
SIMD 编程的大部分技能都来自于寻找方法,使您的算法能够使用给定 SIMD 架构提供的有限数量的受支持指令和数据类型。您通常需要利用数据集的先验知识来获得最佳性能,例如如果您确定您的 32 位整数值实际上具有适合 16 位的范围,那么这将使您的算法的乘法部分更容易实现。
【讨论】:
以上是关于求幂的 SIMD 代码的主要内容,如果未能解决你的问题,请参考以下文章