我有以下查找和插值代码进行优化。(128大小的浮动表)它将在windows上与英特尔编译器一起使用,在OSX上使用GCC,在neon OSX上使用GCC。
for(unsigned int i = 0 ; i < 4 ; i++)
{
const int iIdx = (int)m_fIndex[i];
const float frac = m_fIndex - iIdx;
m_fResult[i] = sftable[iIdx].val + sftable[iIdx].val2 * frac;
}我使用sse/neon对所有内容进行了矢量化。(宏转换为sse/neon指令)
VEC_INT iIdx = VEC_FLOAT2INT(m_fIndex);
VEC_FLOAT frac = VEC_SUB(m_fIndex ,VEC_INT2FLOAT(iIdx);
m_fResult[0] = sftable[iIdx[0]].val2;
m_fResult[1] = sftable[iIdx[1]].val2;
m_fResult[2] = sftable[iIdx[2]].val2;
m_fResult[3] = sftable[iIdx[3]].val2;
m_fResult=VEC_MUL( m_fResult,frac);
frac[0] = sftable[iIdx[0]].val1;
frac[1] = sftable[iIdx[1]].val1;
frac[2] = sftable[iIdx[2]].val1;
frac[3] = sftable[iIdx[3]].val1;
m_fResult=VEC_ADD( m_fResult,frac);我认为表访问和移动到对齐内存才是真正的瓶颈。我不擅长汇编,但有很多unpcklp和mov:
10026751 mov eax,dword ptr [esp+4270h]
10026758 movaps xmm3,xmmword ptr [eax+16640h]
1002675F cvttps2dq xmm5,xmm3
10026763 cvtdq2ps xmm4,xmm5
10026766 movd edx,xmm5
1002676A movdqa xmm6,xmm5
1002676E movdqa xmm1,xmm5
10026772 psrldq xmm6,4
10026777 movdqa xmm2,xmm5
1002677B movd ebx,xmm6
1002677F subps xmm3,xmm4
10026782 psrldq xmm1,8
10026787 movd edi,xmm1
1002678B psrldq xmm2,0Ch
10026790 movdqa xmmword ptr [esp+4F40h],xmm5
10026799 mov ecx,dword ptr [eax+edx*8+10CF4h]
100267A0 movss xmm0,dword ptr [eax+edx*8+10CF4h]
100267A9 mov dword ptr [eax+166B0h],ecx
100267AF movd ecx,xmm2
100267B3 mov esi,dword ptr [eax+ebx*8+10CF4h]
100267BA movss xmm4,dword ptr [eax+ebx*8+10CF4h]
100267C3 mov dword ptr [eax+166B4h],esi
100267C9 mov edx,dword ptr [eax+edi*8+10CF4h]
100267D0 movss xmm7,dword ptr [eax+edi*8+10CF4h]
100267D9 mov dword ptr [eax+166B8h],edx
100267DF movss xmm1,dword ptr [eax+ecx*8+10CF4h]
100267E8 unpcklps xmm0,xmm7
100267EB unpcklps xmm4,xmm1
100267EE unpcklps xmm0,xmm4
100267F1 mulps xmm0,xmm3
100267F4 movaps xmmword ptr [eax+166B0h],xmm0
100267FB mov ebx,dword ptr [esp+4F40h]
10026802 mov edi,dword ptr [esp+4F44h]
10026809 mov ecx,dword ptr [esp+4F48h]
10026810 mov esi,dword ptr [esp+4F4Ch]
10026817 movss xmm2,dword ptr [eax+ebx*8+10CF0h]
10026820 movss xmm5,dword ptr [eax+edi*8+10CF0h]
10026829 movss xmm3,dword ptr [eax+ecx*8+10CF0h]
10026832 movss xmm6,dword ptr [eax+esi*8+10CF0h]
1002683B unpcklps xmm2,xmm3
1002683E unpcklps xmm5,xmm6
10026841 unpcklps xmm2,xmm5
10026844 mulps xmm2,xmm0
10026847 movaps xmmword ptr [eax+166B0h],xmm2在评测时,sse版本在win上没有太多好处。
你有什么建议可以改进吗?霓虹灯/gcc有什么副作用吗?
目前,我只考虑将第一部分矢量化,并在循环中进行表读出和插值,希望它将从编译器优化中受益。
发布于 2013-07-18 22:28:57
OSX?那它就和霓虹灯没有关系了。
顺便说一句,霓虹灯无论如何也无法处理这么大的LUT。(关于这件事,我不知道上交所)
首先验证SSE是否可以处理这种大小的out,如果可以,我建议使用不同的编译器,因为GCC倾向于对内部函数进行内吸。
发布于 2013-07-18 23:39:28
这是我见过的最糟糕的编译器代码生成(假设启用了优化器)。值得一提的是对GCC的攻击。
主要问题:
val和val2的val和val2的索引加载到GPR中,并将索引的向量加载到堆栈,然后将它们加载到gpr.在霓虹灯和SSE上,这应该只需要四个加载和三四个解包(比目前的八个加载+六个解包要好得多)。
消除多余的堆栈流量可能会更难。确保优化器已打开。
发布于 2013-07-19 00:08:48
编译器之所以在这里创建“时髦的”代码(需要大量重载)的原因之一是,为了正确起见,它必须假设sftable[]数组中的数据可能会发生变化。要使生成的代码更好,请将其重新构造为如下所示:
VEC_INT iIdx = VEC_FLOAT2INT(m_fIndex);
VEC_FLOAT frac = VEC_SUB(m_fIndex ,VEC_INT2FLOAT(iIdx);
VEC_FLOAT fracnew;
// make it explicit that all you want is _four loads_
typeof(*sftable) tbl[4] = {
sftable[iIdx[0]], sftable[iIdx[1]], sftable[iIdx[2]], sftable[iIdx[3]]
};
m_fResult[0] = tbl[0].val2
m_fResult[1] = tbl[1].val2;
m_fResult[2] = tbl[2].val2;
m_fResult[3] = tbl[3].val2;
fracnew[0] = tbl[0].val1;
fracnew[1] = tbl[1].val1;
fracnew[2] = tbl[2].val1;
fracnew[3] = tbl[3].val1;
m_fResult=VEC_MUL( m_fResult,frac);
m_fResult=VEC_ADD( m_fResult,fracnew);
frac = fracnew;使用内部函数可能是有意义的(因为你在sftable[]中的布局是交错的),因为向量浮点数组fResult和frac很可能都可以通过一条指令从tbl[]加载(在SSE中解压hi/lo,在Neon中解压)。如果没有类似AVX2的VGATHER指令的帮助,“主”表查找是不能向量化的,但它不需要超过四次加载。
https://stackoverflow.com/questions/17724678
复制相似问题