作为图像处理功能的一部分,我需要计算它们之间的平方和 图像中有两行。
这部分代码占用了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;
}
其中:
a
,b
属于byte[]
sum
是long
size
为int
,通常值较大(约为400)运行Java 7 64位。我尝试用a[pa+dx]
之类的内容替换a[pa++]
表现并不好。
与C ++编写的完全相同的代码完全执行保存运行 整体快两倍(!)和 据我所知,不应该有这个Java的重要原因 代码是快速的,特别是当边界检查可以移出循环时 编译器。
如何优化这些内容以执行与C ++代码一样的操作 - 最终它是如此 整数算术它不应该在Java中慢得多
编辑:C ++示例如下:
unsigned char const *srcptr=&a[pa];
unsigned char const *tgtptr=&b[pb];
for(int dx=0;dx < size;dx++) {
int p1=*srcptr++;
int p2=*tgtptr++;
int diff = p1 - p2;
sum += diff * diff;
}
我想了解如何使HotSpot优化器创建代码 与上面显示的C ++代码一样快,最后它非常简单容易 优化生产线。
答案 0 :(得分:2)
它只是很小,但您不需要& 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;
}
关于有符号和无符号字节算术似乎存在一些混淆。如果您怀疑它们是相同的,请执行以下命令:
byte a = -128;
byte b = 127;
int diff = a - b;
System.out.println(diff); // -255
a = 127;
b = -128;
diff = a - b;
System.out.println(diff); // 255
原因 diff值的范围大于byte
(-128..127),是java自动将byte
扩展为int
< em>在计算之前,因为目标变量是int
。
答案 1 :(得分:1)
在我使用不同的C ++编译器和不同的Java版本测试相同的算法后,我得出结论,GCC是非常良好的编译器,它比intel和clang更好地优化代码!
这些是在C ++和Java中实现的相同算法的运行时(当上面的行是运行时的96%时:
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一样快,英特尔编译器出于某种原因做得非常糟糕,但是gcc给出了最好的结果,所以我不能指望Java运行得比 大多数C ++编译器都可以。
注意这是由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
这是由clang生成的:
.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
有什么区别?海湾合作委员会重新安排指示,以便他们可以 在管道中并行运行,例如:
movzbl (%rcx), %r8d
movzbl (%rsi), %r10d
addl $1, %edx
addq $1, %rcx
addq $1, %rsi
总的来说,Java运行时间很好。
在向英特尔编译器提供-xHost
选项(优化当前CPU)后,编辑,运行时间提高到56秒(使用mmx指令),但仍然没有gcc快,但是比Java好一点
答案 2 :(得分:1)
将& 0xFF
移到循环外部。
通过计算int[]
- a
和b
的版本并使用这些来重写您的循环来执行此操作。
答案 3 :(得分:-1)
如果“size”,如果数组a或b的大小可以避免条件:
try{
for (int dx = 0; ; dx++) {
...
...
}
}catch(ArrayIndexOutOfBoundException e){}
这两条直线还是弯曲的?您可以发布问题的格式表示,或数组的数字示例吗?也许有更好的几何解决方案?