令人惊讶的Java性能
我有这样一个压力测试仪课程:令人惊讶的Java性能,java,performance-testing,Java,Performance Testing,我有这样一个压力测试仪课程: public abstract class StressTest { public static final int WARMUP_JIT_COMPILER = 10000; public interface TimedAction { void doAction(); } public static long timeAction(int numberOfTimes, TimedAction action) { ThreadMX
public abstract class StressTest {
public static final int WARMUP_JIT_COMPILER = 10000;
public interface TimedAction {
void doAction();
}
public static long timeAction(int numberOfTimes, TimedAction action) {
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
for (int i = 0; i < WARMUP_JIT_COMPILER; i++) {
action.doAction();
}
long currentTime = bean.getCurrentThreadCpuTime();
for (int i = 0; i < numberOfTimes; i++) {
action.doAction();
}
return (bean.getCurrentThreadCpuTime() - currentTime)/1000000;
}
}
private static boolean isPrime1(int n) { ... }
private static boolean isPrime2(int n) { ... }
private static boolean isPrime3(int n) { ... }
private static boolean isPrime4(int n) { ... }
private static final int NUMBER_OF_RUNS = 1000000;
public static void main(String[] args) {
long primeNumberFinderTime1 = StressTest.timeAction(NUMBER_OF_RUNS, () -> {
for (int i = 0; i < 100; i++) {
isPrime1(i);
}
});
long primeNumberFinderTime2 = StressTest.timeAction(NUMBER_OF_RUNS, () -> {
for (int i = 0; i < 100; i++) {
isPrime2(i);
}
});
long primeNumberFinderTime3 = StressTest.timeAction(NUMBER_OF_RUNS, () -> {
for (int i = 0; i < 100; i++) {
isPrime3(i);
}
});
long primeNumberFinderTime4 = StressTest.timeAction(NUMBER_OF_RUNS, () -> {
for (int i = 0; i < 100; i++) {
isPrime4(i);
}
});
}
class PrimeNumberFinder1 {
@Override
bool isPrime(i) { /* same code as in static isPrime1() */ };
}
class PrimeNumberFinder2 extends PrimeNumberFinder1 {
@Override
bool isPrime(i) { /* same code as in static isPrime2() */ };
}
class PrimeNumberFinder3 extends PrimeNumberFinder1 {
@Override
bool isPrime(i) { /* same code as in static isPrime3() */ };
}
class PrimeNumberFinder4 extends PrimeNumberFinder1 {
@Override
bool isPrime(i) { /* same code as in static isPrime4() */ };
}
class SomeClassWithPrimeNumberFinder {
PrimeNumberFinder1 _pnf;
void setPrimeNumberFinder(PrimeNumberFinder1 pnf) {
_pnf = pnf;
}
void stressTest() {
StressTest.doAction(10000000, () -> {
for (int i = 0; i < 100; i++) {
_pnf.isPrime(i);
}
});
}
}
我有一节课是这样的:
public abstract class StressTest {
public static final int WARMUP_JIT_COMPILER = 10000;
public interface TimedAction {
void doAction();
}
public static long timeAction(int numberOfTimes, TimedAction action) {
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
for (int i = 0; i < WARMUP_JIT_COMPILER; i++) {
action.doAction();
}
long currentTime = bean.getCurrentThreadCpuTime();
for (int i = 0; i < numberOfTimes; i++) {
action.doAction();
}
return (bean.getCurrentThreadCpuTime() - currentTime)/1000000;
}
}
private static boolean isPrime1(int n) { ... }
private static boolean isPrime2(int n) { ... }
private static boolean isPrime3(int n) { ... }
private static boolean isPrime4(int n) { ... }
private static final int NUMBER_OF_RUNS = 1000000;
public static void main(String[] args) {
long primeNumberFinderTime1 = StressTest.timeAction(NUMBER_OF_RUNS, () -> {
for (int i = 0; i < 100; i++) {
isPrime1(i);
}
});
long primeNumberFinderTime2 = StressTest.timeAction(NUMBER_OF_RUNS, () -> {
for (int i = 0; i < 100; i++) {
isPrime2(i);
}
});
long primeNumberFinderTime3 = StressTest.timeAction(NUMBER_OF_RUNS, () -> {
for (int i = 0; i < 100; i++) {
isPrime3(i);
}
});
long primeNumberFinderTime4 = StressTest.timeAction(NUMBER_OF_RUNS, () -> {
for (int i = 0; i < 100; i++) {
isPrime4(i);
}
});
}
class PrimeNumberFinder1 {
@Override
bool isPrime(i) { /* same code as in static isPrime1() */ };
}
class PrimeNumberFinder2 extends PrimeNumberFinder1 {
@Override
bool isPrime(i) { /* same code as in static isPrime2() */ };
}
class PrimeNumberFinder3 extends PrimeNumberFinder1 {
@Override
bool isPrime(i) { /* same code as in static isPrime3() */ };
}
class PrimeNumberFinder4 extends PrimeNumberFinder1 {
@Override
bool isPrime(i) { /* same code as in static isPrime4() */ };
}
class SomeClassWithPrimeNumberFinder {
PrimeNumberFinder1 _pnf;
void setPrimeNumberFinder(PrimeNumberFinder1 pnf) {
_pnf = pnf;
}
void stressTest() {
StressTest.doAction(10000000, () -> {
for (int i = 0; i < 100; i++) {
_pnf.isPrime(i);
}
});
}
}
通过此设置,PrimeNumberFind1
的速度大约与第一次测试中的isPrime1()一样快。但是在第一次测试中,PrimeNumberFind3
比isPrime3()慢大约200倍
如果我移动PrimeNumberFind3
使它首先运行,那么在第一次测试中,我会得到与isPrime3()相同的时间。其余时间也有点慢(5-10%),但与PrimeNumberFind3
不同
前3个PrimeNumberFind
只是循环和if。没有涉及任何国家。最后一个具有创建查找列表的构造函数,但也只是一个简单的循环。如果我从构造函数中取出代码并使用数组文本创建查找列表,则时间是相同的
知道为什么会发生这种情况吗?可能发生的情况是,最初的
iPrime
被丢弃为死代码,因为结果没有被保留/使用,所以它将运行得非常快,例如,比时钟周期快
但是,当您提供多个实现时,它必须选择调用哪个方法,因此,使用第二个方法需要更长的时间,但JIT不能内联两个以上的方法,因此第三个实现意味着测试速度大大降低,因为它需要做更多的工作来调用被丢弃的方法。您是否尝试将结果累积到
volatile
字段中而不是将其丢弃?您还忘了向我们展示相关的代码…我尝试将结果累加到volatile
字段中,但没有效果。我是Java新手,但volatile与线程缓存自己版本的值有关,对吗?这个程序是纯单线程的。@carlsb3rg你说得对,volatile
与多线程相关,防止对字段的读/写进行重新排序(粗略地说)。但作为上述操作的一个副作用,它也会完全停止优化读/写操作。这就是为什么即使在单线程微基准测试中,它们也被用来累积结果的原因。@biziclop累积是关键,但我不必累积到易变字段@彼得·劳瑞收到了,不过还是谢谢你。我相信volatile
以后会很有用;)@约翰伯林格虽然最好的办法是修改测试,但我认为他得到的结果值得解释对静态版本(特别是isPrime3())运行速度快得不可思议,因为它包含的所有内容都是返回(n==2 | | n==3…
,用于100以下的所有素数。当我看数字时,JIT内联两个方法非常有意义。现在我只需要弄清楚为什么一个方法在n<2时返回false,在n<4时返回true,在n%2==0时返回false,以及一个循环在3->sqrt(n)中尝试每个奇数比所有素数小于100的just or's n的返回语句快10%。@carlsb3rg内联也可能是这里的关键。长链的或可能会使您的方法超出内联的指令限制,而短循环的处理可能会更好。我建议你看看,它可能会派上用场。非常感谢!我将查看jitwatch,看看是否可以进行一些位操作。