Java 为什么小型包装方法没有得到优化?
我正在运行一个实验来确定包装方法的性能开销。我读过JIT编译器和/或JVM优化小方法的文章,但我似乎总是会受到3-5%的性能损失 代码如下:Java 为什么小型包装方法没有得到优化?,java,Java,我正在运行一个实验来确定包装方法的性能开销。我读过JIT编译器和/或JVM优化小方法的文章,但我似乎总是会受到3-5%的性能损失 代码如下: import java.util.* ; public class WrappingTest1{ private WrappingTest1(){ // Empty. } private static void findPrimes( final Long maxValue , f
import java.util.* ;
public class WrappingTest1{
private WrappingTest1(){
// Empty.
}
private static void findPrimes(
final Long maxValue ,
final List< Long > foundPrimes
){
if(
maxValue > 2L
){
Boolean isPrime ;
foundPrimes.clear() ;
for(
Long i = 2L ;
i <= maxValue ;
i += 1L
){
isPrime = true ;
for(
Long j = 2L ;
j < i ;
j += 1L
){
if(
( i % j ) == 0
){
isPrime = false ;
}
}
if(
isPrime
){
foundPrimes.add(
i
) ;
}
}
}
}
private static void wrapper(
final Long input ,
final List< Long > output
){
findPrimes(
input ,
output
) ;
}
public static void main(
final String[] args
){
ArrayList< Long > primes ;
Long startTime ;
Long endTime ;
Double duration ;
Double meanDuration ;
Long primeRange ;
Long warmupIterations ;
Long benchmarkIterations ;
primes = new ArrayList<>() ;
meanDuration = 0.0 ;
primeRange = 100L ;
warmupIterations = 20000L ;
benchmarkIterations = 100000L ;
System.out.println(
"Experiment started."
) ;
// Unwrapped warmup.
for(
Long i = 0L ;
i < warmupIterations ;
i += 1L
){
findPrimes(
primeRange ,
primes
) ;
}
// Unwrapped benchmark.
startTime = System.nanoTime() ;
for(
Long i = 0L ;
i < benchmarkIterations ;
i += 1L
){
findPrimes(
primeRange ,
primes
) ;
}
endTime = System.nanoTime() ;
duration = ( endTime.doubleValue() - startTime.doubleValue() ) / 1E9 ;
System.out.println(
"Unwrapped runtime: " + duration + " seconds."
) ;
// Wrapped warmup.
for(
Long i = 0L ;
i < warmupIterations ;
i += 1L
){
wrapper(
primeRange ,
primes
) ;
}
// Wrapped benchmark.
startTime = System.nanoTime() ;
for(
Long i = 0L ;
i < benchmarkIterations ;
i += 1L
){
wrapper(
primeRange ,
primes
) ;
}
endTime = System.nanoTime() ;
duration = ( endTime.doubleValue() - startTime.doubleValue() ) / 1E9 ;
System.out.println(
"Wrapped runtime: " + duration + " seconds."
) ;
System.out.println(
"Experiment completed."
) ;
}
}
Experiment started.
Unwrapped runtime: 4.851473465 seconds.
Wrapped runtime: 5.078349508 seconds.
Experiment completed.
为什么会这样?如何让JVM内联包装的方法,或者以其他方式对其进行优化,从而忽略包装
谢谢。乐观,选择迭代的最终长maxValue,但使用100作为maxValue
如果将循环中的long替换为int,可能会获得10倍的加速
第二个和第三个重大改进是,循环到Math.sqrt i,并且仅当它还没有被证明不是素数时:
for (int j = 2; j <= Math.sqrt(i) && isPrime; ++j)
因此,令人惊讶的是,包装版本更快。陛下
改变PrimeFinder[]pfs中的顺序,它们走得更近,包裹:2.46,未包裹2.52 看起来减速的原因不是包装/取消包装方法调用的优化或缺乏,而是主方法本身缺乏优化。通过分析显示,按照默认设置,在10K预热迭代之后,包装/取消包装的方法调用得到了优化。然而,只有在大约70K的预热迭代之后,JVM评测才会报告编译了主方法。因此,如果预热迭代次数小于70K,则未包装的运行时明显低于包装的运行时;但如果预热迭代次数为70K及以上,则两个运行时是相似的。当然,这只适用于指定的基准测试-单个程序可能会有不同的结果。@用户未知:谢谢。这实际上不是我的,而是场景项目的格式。由于我在同一个项目中构建了这个基准测试,所以它的格式与Eclipse formatter相同,我认为没有迫切需要更改它。谢谢您的回答。从long移到integer有助于减少时间,但是unwrapped方法仍然比wrapped方法快得多。此外,与将其作为静态方法相比,将主要查找程序封装在抽象中会使其速度减慢?围绕调用编写for循环10x?更改调用顺序会导致运行时之间的比率发生微小变化,但包装的运行速度仍然明显慢于取消包装的运行速度。但是,如果预热运行足够长的时间,主方法就会得到优化,这似乎会导致包装和未包装的方法具有相似的运行时间。一个围绕调用的循环可以实现这一点,但是仅仅运行100K-200K迭代的预热也可以实现这一点。
import java.util.* ;
public class WrappingTest1
{
PrimeFinder[] pfs = new PrimeFinder[2];
int primeRange = 1000;
private WrappingTest1 ()
{
// pfs[1] = new UnwrappedFinder ();
// pfs[0] = new WrappedFinder (pfs[1]);
pfs[0] = new UnwrappedFinder ();
pfs[1] = new WrappedFinder (pfs[0]);
}
void test ()
{
for (PrimeFinder pf: pfs)
runblock (pf);
}
void loopy (int iterations, PrimeFinder pf, ArrayList <Integer> primes)
{
for (int i = 0; i < iterations; ++i)
pf.findPrimes (primeRange, primes);
}
void runblock (PrimeFinder pf)
{
int warmupIterations = 20000;
int benchmarkIterations = 100000;
ArrayList <Integer> primes = new ArrayList<Integer> (50000) ;
// warmup.
loopy (warmupIterations, pf, primes);
// enchmark.
Long startTime = System.nanoTime();
loopy (benchmarkIterations, pf, primes);
Long endTime = System.nanoTime() ;
Double duration = (endTime.doubleValue () - startTime.doubleValue ()) / 1E9 ;
System.out.printf ("%s runtime: %4.2f seconds.\n", pf.name(), duration);
// had to make sure, that we're really producing valid primes:
// and that they survive the code changes.
for (int p: primes) {
System.out.printf ("%d ", p);
}
System.out.println ("bye");
}
abstract class PrimeFinder {
abstract void findPrimes (final int maxValue, final List <Integer> foundPrimes);
abstract String name ();
}
class UnwrappedFinder extends PrimeFinder {
String name () {return "Unwrapped";}
void findPrimes (final int maxValue, final List <Integer> foundPrimes)
{
if (maxValue > 2)
{
foundPrimes.clear () ;
for (int i = 2; i <= maxValue; ++i)
{
Boolean isPrime = true;
for (int j = 2; j <= Math.sqrt(i) && isPrime; ++j)
if ((i % j) == 0)
isPrime = false;
if (isPrime)
foundPrimes.add (i);
}
}
}
}
class WrappedFinder extends PrimeFinder {
String name () {return " Wrapped";}
private PrimeFinder pf;
public WrappedFinder (PrimeFinder ppf)
{
pf = ppf;
}
void findPrimes (final int input, final List <Integer> output) {
pf.findPrimes (input, output);
}
}
public static void main (final String[] args)
{
System.out.println ("Experiment started.");
WrappingTest1 wt1 = new WrappingTest1 ();
wt1.test ();
System.out.println ("Experiment completed.") ;
}
}
Unwrapped runtime: 2,34 seconds.
Unwrapped runtime: 2,53 seconds.
Unwrapped runtime: 2,50 seconds.
Unwrapped runtime: 2,50 seconds.
Unwrapped runtime: 2,49 seconds.
Unwrapped runtime: 2,52 seconds.
Unwrapped runtime: 2,59 seconds.
Unwrapped runtime: 2,60 seconds.
Unwrapped runtime: 2,58 seconds.
Unwrapped runtime: 2,52 seconds.
Wrapped runtime: 2,36 seconds.
Wrapped runtime: 2,36 seconds.
Wrapped runtime: 2,36 seconds.
Wrapped runtime: 2,36 seconds.
Wrapped runtime: 2,37 seconds.
Wrapped runtime: 2,37 seconds.
Wrapped runtime: 2,36 seconds.
Wrapped runtime: 2,41 seconds.
Wrapped runtime: 2,37 seconds.
Wrapped runtime: 2,37 seconds.