Java 优化SSE代码
我目前正在为一个Java应用程序开发一个C模块,该应用程序需要一些性能改进(请参阅了解背景)。我尝试使用SSE intrinsic优化代码,它的执行速度比Java版本快一些(~20%)。然而,它仍然不够快 不幸的是,我在优化C代码方面的经验有些有限。因此,我很想就如何改进目前的实施提出一些想法 构成热点的内部循环如下所示:Java 优化SSE代码,java,c,optimization,sse,intel-vtune,Java,C,Optimization,Sse,Intel Vtune,我目前正在为一个Java应用程序开发一个C模块,该应用程序需要一些性能改进(请参阅了解背景)。我尝试使用SSE intrinsic优化代码,它的执行速度比Java版本快一些(~20%)。然而,它仍然不够快 不幸的是,我在优化C代码方面的经验有些有限。因此,我很想就如何改进目前的实施提出一些想法 构成热点的内部循环如下所示: for (i = 0; i < numberOfGFVectorsInFragment; i++) { // Load the 4 GF-elem
for (i = 0; i < numberOfGFVectorsInFragment; i++) {
// Load the 4 GF-elements from the message-fragment and add the log of the coefficeint to them.
__m128i currentMessageFragmentVector = _mm_load_si128 (currentMessageFragmentPtr);
__m128i currentEncodedResult = _mm_load_si128(encodedFragmentResultArray);
__m128i logSumVector = _mm_add_epi32(coefficientLogValueVector, currentMessageFragmentVector);
__m128i updatedResultVector = _mm_xor_si128(currentEncodedResult, valuesToXor);
_mm_store_si128(encodedFragmentResultArray, updatedResultVector);
encodedFragmentResultArray++;
currentMessageFragmentPtr++;
}
(i=0;i{
//从消息片段中加载4个GF元素,并将coeficent的日志添加到其中。
__m128i currentMessageFragmentVector=_mm_load_si128(currentMessageFragmentPtr);
__m128i currentEncodedResult=_mm_load_si128(EncodedFragmentResult数组);
__m128i logSumVector=\u mm\u add\u epi32(系数logvalueVector,currentMessageFragmentVector);
__m128i updatedResultVector=\u mm\u xor\u si128(currentEncodedResult,valuesToXor);
_mm_store_si128(encodedFragmentResultArray,updatedResultVector);
encodedFragmentResultArray++;
currentMessageFragmentPtr++;
}
即使不看组件,我也可以马上看出瓶颈来自4元素聚集内存访问和\u mm\u set\u epi32
打包操作。在您的情况下,\u mm\u set\u epi32在内部可能会作为一系列解包/hi
指令实现
这个循环中的大部分“工作”都来自于打包这4个内存访问。在没有SSE4.1的情况下,我要说的是,循环可以更快地非矢量化,但可以展开
如果你愿意使用SSE4.1,你可以试试这个。它可能会更快,但可能不会:
int* logSumArray = (int*)(&logSumVector);
__m128i valuesToXor = _mm_cvtsi32_si128(expTable[*(logSumArray++)]);
valuesToXor = _mm_insert_epi32(valuesToXor, expTable[*(logSumArray++)], 1);
valuesToXor = _mm_insert_epi32(valuesToXor, expTable[*(logSumArray++)], 2);
valuesToXor = _mm_insert_epi32(valuesToXor, expTable[*(logSumArray++)], 3);
我建议将循环展开至少4次迭代,并交错所有指令,以使此代码有可能表现良好
您真正需要的是Intel的AVX2聚集/分散指令。但这需要几年的时间……也许可以试试。
名字中带有“region”的函数应该很快。它们似乎没有使用任何特殊的指令集,但可能已经用其他方式对它们进行了优化…您看过编译器生成的程序集了吗?可能会有一些进一步的收获。如果您无法让编译器执行这些操作,请手动执行,并使用(注释良好的).s作为源文件,而不是.cDoes,这在某种程度上扼杀了Java的“可移植性”方面。@OrangeDog我现在已将程序集添加到问题中。生成该程序集时,您是否对编译器进行过任何优化?@OrangeDog yup。我使用了以下构建标志:cl/c/Zi/nologo-/Wall/WX-/Ox/Ob2/Oi/Ot/Oy-/D WIN32/D NDEBUG/D_windell/D UNICODE/D UNICODE/Gm-/EHsc/MD/GS/arch:SSE2/fp:precision/Zc:wchar\u t/Zc:forScope/Yc“StdAfx.h”/fp”Release\NetworkCodingAccelerator.pch”/Fo“Release\\\\\\\\\\\\\\\\\\\\/Fd\Release\vc100.pdb”/Gd/TC/analyze-/errorReport:prompt Stdafx.c您认为我应该添加什么?对于MSVC的编译器设置,我是个新手。谢谢,但您的代码会产生以下错误:错误C2275:“u m128i”:非法使用此类型作为表达式。如果我删除它编译的最后三行,错误似乎与覆盖valuesToXor beceause有关(错误消息总是指向设置valuesToXor的最后一行之后的表达式)。您是否包含SSE4.1头文件
?@Mystical我甚至看到msvc实现的\u mm\u set\u
函数作为4个副本复制到临时对齐的内存中,然后从内存加载到寄存器,看到这一点我只能摇头。@Mystical是的。例如,如果我这样写:uum128i valuesToXor=mm_cvtsi32_si128(expTable[*(logSumArray++))__m128i valuesToXor2=_mm_insert_epi32(valuesToXor,expTable[*(logSumArray++)),1);它可以编译,但如果我将其替换为:_m128i valuesToXor=_mm_cvtsi32_si128(expTable[*(logSumArray++));valuesToXor=_mm_insert_epi32(valuesToXor,expTable[*(logSumArray++]),1);编译器创建错误消息后的行。@Christian Rau:我以前也看到过。。。真是好笑。。。谢谢!它们似乎使用了许多与我类似的技巧(比如双日志表以避免额外的模)。不过,我会检查区域代码,看看他们是否还有其他优化。在阅读文档后,我实际上认为他们的代码比较慢。在对一个区域进行乘法和异或运算时,它们达到了约167MB/s。对于相同的操作,我实现了大约1GB/s(我可能有更快的CPU,但差异仍然很大)。问题是我这样做>1000/s,所以实际输出吞吐量只有~1Mb/s。