C 32x32乘法和加法优化
我正在优化一个应用程序。我发现我需要优化一个内部循环以提高性能。 该滤波器是一个16位阵列C 32x32乘法和加法优化,c,optimization,loops,C,Optimization,Loops,我正在优化一个应用程序。我发现我需要优化一个内部循环以提高性能。 该滤波器是一个16位阵列 for (i = 0; i < iLen; i++) { iPredErr = (I32)*rgiResidue; rgiFilter = rgiFilterBuf; rgiPrevVal = rgiPrevValRdBuf + iRecent; rgiUpdate = rgiUpdateRdBuf + iRecent; iPred = iScalingOf
for (i = 0; i < iLen; i++) {
iPredErr = (I32)*rgiResidue;
rgiFilter = rgiFilterBuf;
rgiPrevVal = rgiPrevValRdBuf + iRecent;
rgiUpdate = rgiUpdateRdBuf + iRecent;
iPred = iScalingOffset;
for (j = 0; j < iOrder_Div_8; j++) {
iPred += (I32) rgiFilter[0] * rgiPrevVal[0];
rgiFilter[0] += rgiUpdate[0];
iPred += (I32) rgiFilter[1] * rgiPrevVal[1];
rgiFilter[1] += rgiUpdate[1];
iPred += (I32) rgiFilter[2] * rgiPrevVal[2];
rgiFilter[2] += rgiUpdate[2];
iPred += (I32) rgiFilter[3] * rgiPrevVal[3];
rgiFilter[3] += rgiUpdate[3];
iPred += (I32) rgiFilter[4] * rgiPrevVal[4];
rgiFilter[4] += rgiUpdate[4];
iPred += (I32) rgiFilter[5] * rgiPrevVal[5];
rgiFilter[5] += rgiUpdate[5];
iPred += (I32) rgiFilter[6] * rgiPrevVal[6];
rgiFilter[6] += rgiUpdate[6];
iPred += (I32) rgiFilter[7] * rgiPrevVal[7];
rgiFilter[7] += rgiUpdate[7];
rgiFilter += 8;
rgiPrevVal += 8;
rgiUpdate += 8;
}
(i=0;i{
iPredErr=(I32)*残基;
rgiFilter=rgiFilterBuf;
rgiproval=rgiprovalrdbuf+iRecent;
rgiUpdate=rgiupdaterbuf+iRecent;
iPred=iScalingOffset;
对于(j=0;j
ode在这里我不认为您可以在C语言中对其进行太多优化。您的编译器可能有生成SIMD代码的选项,但如果性能至关重要,您可能需要编写自己的SIMD汇编代码……您唯一的赌注是一次执行多个操作,这意味着以下三个选项之一:
SSE指令(SIMD)。您可以使用一条指令处理多个内存位置
多线程(MIMD)。如果您有一个以上的cpu内核,这一点最为有效。将阵列拆分为多个大小相似的独立条带(依赖关系会大大增加此选项的复杂性,如果您需要大量锁,则比按顺序计算所有内容要慢)。请注意,阵列必须足够大,以抵消额外的上下文切换和同步开销(非常小,但不可忽略)。最适合4核或更多核
如果你的阵型真的很大,你可以通过两者的结合获得很多
确保iPred保存在寄存器中(在每次+=操作之前不从内存读取,在每次+=操作之后不写回内存)
优化一级缓存的内存布局。确保3个阵列不会争夺相同的缓存项。这取决于CPU体系结构,一点也不简单
如果rgiFilterBuf
、rgiprovalrdbuf
和rgipupdaterbuf
是不带别名的函数参数,请使用restrict
限定符声明它们。这将允许编译器更积极地进行优化
正如其他一些人所评论的,您的内部循环看起来很适合向量处理指令(如SSE,如果您在x86上)。请检查编译器的内部函数。循环展开和向量化应由编译器完成
请参见您可以用很少的SSE2内部函数替换内部循环
请参见[u mm_madd_epi16][1]以替换八个
iPred += (I32) rgiFilter[] * rgiPrevVal[];
以及(二)或(三)以取代八项建议
rgiFilter[] += rgiUpdate[];
你应该看到一个很好的加速
这些内部函数特定于Microsoft和Intel编译器。
我确信GCC存在等价物,我只是没有使用它们
编辑:根据下面的评论,我将更改以下内容
如果您有混合类型,编译器并不总是足够聪明来解决它。
我建议以下几点,让它更明显,并给它一个更好的机会
在自动矢量化
将rgiFilter[]声明为的I32位
此函数的目的。您
我会付一份
将iPred也更改为iPred[]作为I32
在内部(甚至外部)循环之外进行iPred[]求和
四人一组打包类似说明
iPred[0]+=rgiFilter[0]*RGIProval[0]
iPred[1]+=rgiFilter[1]*RGIProval[1]
iPred[2]+=rgiFilter[2]*RGIProval[2]
iPred[3]+=rgiFilter[3]*RGIProval[3]
rgiFilter[0]+=rgiFilter[0]
rgiFilter[1]+=rgiFilter[1]
rgiFilter[2]+=rgiFilter更新[2]
rgiFilter[3]+=rgiFilter更新[3]
这对于英特尔编译器来说应该足够了,可以通过确保数据在内存中呈线性排列,这样就不会出现缓存未命中来解决问题。不过,这似乎不是问题
如果无法执行这些操作(如果编译器失败,请查看程序集),请尝试将较小的for循环分成几个不同的for循环(每个for循环对应一个0..8)。编译器往往能够对执行较少操作的循环进行更好的优化(除非在这种情况下,它可能能够进行矢量化/SSE)
对于32/64位体系结构来说,16位整数的使用成本更高(除非它们有特定的16位寄存器)。在执行循环之前,请尝试将其转换为32位(大多数64位体系结构也有32位寄存器)。非常好的代码
在每一步中,你基本上要做三件事,一个乘法和两个加法
另外,我有时发现,如果我将这些活动划分为不同的循环,我会得到更快的代码,比如
- 一个循环执行乘法并保存到临时数组
- 在
iPred
中对该数组求和的一个循环
- 将
rgiUpdate
添加到rgiFilter
的一个循环
展开时,循环开销可以忽略不计,但如果
for (i = 0; i < iLen; i++) {
for (i = iLen-1; i <= 0; i--) {
for (p = rgiFilter; p <= rgiFilter+8; ) {
iPred += (I32) (*p) + *rgiPreval++;
*p++ += *rgiUpdate++;
....
}
for (p = rgiFilter; p <= rgiFilter+8; ) {
I16 x = *p;
I16 y = *(p+1); // Hope that the compiler can combine these loads
iPred += (I32) x + *rgiPreval++;
iPred += (I32) y + *rgiPreval++;
*p++ += *rgiUpdate++;
*p++ += *rgiUpdate++; // Hope that the complier can combine these stores
....
}
__builtin_prefetch (const void * addr)
__builtin_prefetch (const void * addr, int rw)
__builtin_prefetch (const void * addr, int rw, int locality)