Java优化中的向量距离计算
作为图像处理功能的一部分,我需要计算 图像中的两行 这部分代码占用96%的运行时间:Java优化中的向量距离计算,java,optimization,Java,Optimization,作为图像处理功能的一部分,我需要计算 图像中的两行 这部分代码占用96%的运行时间: for(int dx=0;dx<size;dx++) { int left = a[pa+dx]; int right = b[pb+dx]; int diff = (left & 0xFF) - (right & 0xFF); sum += diff*diff; } Intel 12.1 1:58 GCC 4.6 0:43 GCC 4.4
for(int dx=0;dx<size;dx++) {
int left = a[pa+dx];
int right = b[pb+dx];
int diff = (left & 0xFF) - (right & 0xFF);
sum += diff*diff;
}
Intel 12.1 1:58
GCC 4.6 0:43
GCC 4.4 0:43
Clang 1:20
Java 7 1:20
Java 6 1:23
我想了解如何使HotSpot optimizer创建一个
与上面所示的C++代码一样快,最后它非常简单和容易。
优化行。它很小,但不需要使用
&0xFF
来计算差异:差异将是相同的有符号或无符号
100 - -1 = 101 // signed
228 - 127 = 101 // unsigned
那么它将是更紧的环体:
for (int dx = 0; dx < size; dx++) {
int diff = a[pa+dx] - b[pb+dx];
sum += diff*diff;
}
diff值的范围大于byte
(-128..127)的原因是java在计算之前自动将byte
扩展到int
,因为目标变量是int
,如果“size”如果数组a或b的大小,则可以避免以下情况:
try{
for (int dx = 0; ; dx++) {
...
...
}
}catch(ArrayIndexOutOfBoundException e){}
这两条线是直线还是曲线?您可以发布一个问题的一般表示,或者一个数组的数值示例吗?也许有更好的几何解, < P>在用不同的C++编译器和不同的java版本测试同一个算法后,我得出结论:GCC是<强>非常/强>好编译器,它比英特尔和CLANG更好地优化了代码。p> <>这是在C++和java中实现的相同算法的运行时间(当上面的行为运行时间的96%:)
for(int dx=0;dx<size;dx++) {
int left = a[pa+dx];
int right = b[pb+dx];
int diff = (left & 0xFF) - (right & 0xFF);
sum += diff*diff;
}
Intel 12.1 1:58
GCC 4.6 0:43
GCC 4.4 0:43
Clang 1:20
Java 7 1:20
Java 6 1:23
这表明Java的运行速度和clang一样快,而Intel编译器由于某种原因做得非常糟糕,但是gcc给出了最好的结果,所以我真的不能期望Java的运行速度比clang快
大多数C++编译器都是.< /p>
注:这是gcc生成的程序集:
.L225:
movzbl (%rcx), %r8d
movzbl (%rsi), %r10d
addl $1, %edx
addq $1, %rcx
addq $1, %rsi
subl %r10d, %r8d
imull %r8d, %r8d
movslq %r8d, %r8
addq %r8, %rax
cmpl %edx, %ebp
ja .L225
这个是由叮当声产生的:
.LBB0_26:
movzbl (%r11), %r13d
movzbl (%r14), %esi
subl %r13d, %esi
imull %esi, %esi
movslq %esi, %rsi
addq %rsi, %rcx
incq %r11
incq %r14
decq %r12
jne .LBB0_26
区别是什么?GCC重新排列指令,以便它们可以
在管线中平行运行,例如:
movzbl (%rcx), %r8d
movzbl (%rsi), %r10d
addl $1, %edx
addq $1, %rcx
addq $1, %rsi
归根结底,Java运行时很好
编辑:在为英特尔编译器提供了
-xHost
选项(针对当前CPU进行优化)后,运行时间提高到56秒(使用mmx指令),但仍然不如gcc快,但比Java略好,将&0xFF
移出循环
通过计算
int[]
-同时使用a
和b
的版本,并使用它们重写循环。在Java中,由于热点的原因,性能度量不是那么容易。您是如何度量的?占用大部分时间的函数,如果删除这些行,代码几乎不会运行任何时间。您可以尝试在某种战争中重复运行代码我已经准备好允许进行优化了。我不能告诉你多久进行一次,但可能会进行1.000到10.000次。之后,你会进行测量,看看是否有差异。代码占用大量CPU,运行大约一分钟……任何人都很难帮上忙,因为我们无法对您提供的孤立代码段进行基准测试,因此无法测试wh我们建议的任何方法都更快。不,这不一样,我需要将数字减为无符号:128-127=1;但是(有符号)(128)=-128因此(有符号-128-127)很多more@Artyom是的,这是相同的,因为128
不是有效的byte
值。byte
范围是-128
到127
请看,我处理的是一个图像数据,其中的值是0..255范围内的无符号字节,但是它们存储在只带符号的Java字节中。但是无论数据类型是否有符号,值都是相同的。@Artyom Dude,你没有“得到它”。区分有符号字节将生成范围[-255255]内的值
也是!因为java会在计算之前自动将它们加宽到int
。执行我添加到答案中的代码,自己看看。你知道,即使不检查dy的范围,边界也会由数组本身检查。此外,总和范围通常在数组的实际结尾之前结束。现代JVM可以选择编译器通常有很多标志打开和关闭优化。考虑使用-O3或更好地在所有平台上编译。您正确地将运行时间从<代码> 1:20改为<代码> 0:53。此外,我使用短而不是int来减少内存使用和提高性能。至少,编译器能够优化到单个指令,如movzbl
,因为它只是获取无符号字节!也许可以选择另一个JVM或-client/-server。基本上,尽可能简单地表达您需要的内容,为JVM提供最佳工作条件。我还认为您应该使用int
s和比较。我同时检查了int
和short
,并且short
更快。另外,在Java 7上,差异更明显(没有0xFF),在Java 6上,运行时间大约为1:10