最近我有一件很奇怪的事情-一种方法在分析器下非常慢,没有明显的原因。它只包含long
的少量操作,但被频繁调用-它的总体使用量约为程序总时间的30-40%,而其他部分似乎“重”得多。
我通常在x32 JVM上运行非内存密集型程序,但是假设我在64位类型上遇到问题,我尝试在x64 JVM上运行相同的程序-“实时场景”中的总体性能提高了2-3倍。之后,我为特定方法的操作创建了JMH基准测试,并为x32和x64 JVM的差异(多达50倍)感到震惊。
我会“接受”大约慢2倍的x32 JVM(较小的字长),但是我不知道30-50倍可能来自何处。 您能解释一下这种巨大差异吗?
回复评论:
所以看来我的问题的答案是
以下是结果(注意:? 10??
是Windows上未打印的特殊字符-以科学计数法表示为0.001 s / op以下的东西10e-??)
x32 1.8.0_152
Benchmark Mode Score Units Score (with 'return')
IntVsLong.cycleInt avgt 0.035 s/op 0.034 (?x slower vs. x64)
IntVsLong.cycleLong avgt 0.106 s/op 0.099 (3x slower vs. x64)
IntVsLong.divDoubleInt avgt 0.462 s/op 0.459
IntVsLong.divDoubleLong avgt 1.658 s/op 1.724 (2x slower vs. x64)
IntVsLong.divInt avgt 0.335 s/op 0.373
IntVsLong.divLong avgt 1.380 s/op 1.399
IntVsLong.l2i avgt 0.101 s/op 0.197 (3x slower vs. x64)
IntVsLong.mulInt avgt 0.067 s/op 0.068
IntVsLong.mulLong avgt 0.278 s/op 0.337 (5x slower vs. x64)
IntVsLong.subInt avgt 0.067 s/op 0.067 (?x slower vs. x64)
IntVsLong.subLong avgt 0.243 s/op 0.300 (4x slower vs. x64)
x64 1.8.0_152
Benchmark Mode Score Units Score (with 'return')
IntVsLong.cycleInt avgt ? 10?? s/op ? 10??
IntVsLong.cycleLong avgt 0.035 s/op 0.034
IntVsLong.divDoubleInt avgt 0.045 s/op 0.788 (was dead)
IntVsLong.divDoubleLong avgt 0.033 s/op 0.787 (was dead)
IntVsLong.divInt avgt ? 10?? s/op 0.302 (was dead)
IntVsLong.divLong avgt 0.046 s/op 1.098 (was dead)
IntVsLong.l2i avgt 0.037 s/op 0.067
IntVsLong.mulInt avgt ? 10?? s/op 0.052 (was dead)
IntVsLong.mulLong avgt 0.040 s/op 0.067
IntVsLong.subInt avgt ? 10?? s/op ? 10??
IntVsLong.subLong avgt 0.075 s/op 0.082
这是(固定的)基准代码
import org.openjdk.jmh.annotations.Benchmark;
public class IntVsLong {
public static int N_REPEAT_I = 100_000_000;
public static long N_REPEAT_L = 100_000_000;
public static int CONST_I = 3;
public static long CONST_L = 3;
public static double CONST_D = 3;
@Benchmark
public void cycleInt() throws InterruptedException {
for( int i = 0; i < N_REPEAT_I; i++ ) {
}
}
@Benchmark
public void cycleLong() throws InterruptedException {
for( long i = 0; i < N_REPEAT_L; i++ ) {
}
}
@Benchmark
public int divInt() throws InterruptedException {
int r = 0;
for( int i = 0; i < N_REPEAT_I; i++ ) {
r += i / CONST_I;
}
return r;
}
@Benchmark
public long divLong() throws InterruptedException {
long r = 0;
for( long i = 0; i < N_REPEAT_L; i++ ) {
r += i / CONST_L;
}
return r;
}
@Benchmark
public double divDoubleInt() throws InterruptedException {
double r = 0;
for( int i = 1; i < N_REPEAT_L; i++ ) {
r += CONST_D / i;
}
return r;
}
@Benchmark
public double divDoubleLong() throws InterruptedException {
double r = 0;
for( long i = 1; i < N_REPEAT_L; i++ ) {
r += CONST_D / i;
}
return r;
}
@Benchmark
public int mulInt() throws InterruptedException {
int r = 0;
for( int i = 0; i < N_REPEAT_I; i++ ) {
r += i * CONST_I;
}
return r;
}
@Benchmark
public long mulLong() throws InterruptedException {
long r = 0;
for( long i = 0; i < N_REPEAT_L; i++ ) {
r += i * CONST_L;
}
return r;
}
@Benchmark
public int subInt() throws InterruptedException {
int r = 0;
for( int i = 0; i < N_REPEAT_I; i++ ) {
r += i - r;
}
return r;
}
@Benchmark
public long subLong() throws InterruptedException {
long r = 0;
for( long i = 0; i < N_REPEAT_L; i++ ) {
r += i - r;
}
return r;
}
@Benchmark
public long l2i() throws InterruptedException {
int r = 0;
for( long i = 0; i < N_REPEAT_L; i++ ) {
r += (int)i;
}
return r;
}
}
答案 0 :(得分:1)
有很多变量需要检查。
如果我们仅看一下使用64位的处理器,则可以在同一步骤中对CPU寄存器进行更多操作,因为它使用每个字节而不是每个注册表四个字节。这样可以提高操作性能和内存分配。另外,某些CPU仅启用仅在64位模式下运行的高级功能
如果您使用相同的CPU进行测试,则需要上升,您需要考虑到要执行32位指令,CPU需要在虚拟模式或受保护模式下运行,而虚拟模式或受保护模式的运行速度要比真正的32位CPU慢。另外,某些指令集扩展可能无法使用32位模式启用,例如SSE-SIMD或AVX taht可能会提高某些操作速度。
如果您正在使用像Windows 10这样的现代操作系统,也需要考虑到该操作系统使用WOW64(x86模拟器)运行32位应用程序的情况
帮助文档: