Java 为什么某些函数的执行时间差别很大?

Java 为什么某些函数的执行时间差别很大?,java,performance-testing,Java,Performance Testing,我的代码: import java.math.BigDecimal; public class ScienceFair { private static long NewtonMethod() { BigDecimal TWO = new BigDecimal(2); BigDecimal SQRT_TWO = new BigDecimal("1.414213562373095048801688724209698078569671875376948073176679737990

我的代码:

import java.math.BigDecimal;
public class ScienceFair {

private static long NewtonMethod()
{
    BigDecimal TWO = new BigDecimal(2);
    BigDecimal SQRT_TWO = new BigDecimal("1.4142135623730950488016887242096980785696718753769480731766797379907324784621070388503875343276415727");
    BigDecimal TOLERANCE = BigDecimal.ONE.scaleByPowerOfTen(-100);

    long start = System.nanoTime();

    BigDecimal a = new BigDecimal(1);

    while(a.subtract(SQRT_TWO).abs().compareTo(TOLERANCE) >= 0) {
        a = a.add(TWO.divide(a, 100, BigDecimal.ROUND_HALF_UP)).divide(TWO);
    }

    return System.nanoTime() - start;
}

private static long MidpointMethod()
{
    BigDecimal TWO = new BigDecimal(2);
    BigDecimal SQRT_TWO = new BigDecimal("1.4142135623730950488016887242096980785696718753769480731766797379907324784621070388503875343276415727");
    BigDecimal TOLERANCE = BigDecimal.ONE.scaleByPowerOfTen(-100);

    long start = System.nanoTime();

    BigDecimal a = new BigDecimal(1);
    BigDecimal b = new BigDecimal(2);
    while(a.add(b).divide(TWO).subtract(SQRT_TWO).abs().compareTo(TOLERANCE) >= 0) {
        if(a.multiply(a).subtract(TWO).abs().compareTo(b.multiply(b).subtract(TWO).abs()) == 1)
        {
            a = a.add(b).divide(TWO);
        }
        else
        {
            b = a.add(b).divide(TWO);
        }
    }
    return System.nanoTime() - start;
}
private static long SecantMethod()
{
    BigDecimal TWO = new BigDecimal(2);
    BigDecimal SQRT_TWO = new BigDecimal("1.4142135623730950488016887242096980785696718753769480731766797379907324784621070388503875343276415727");
    BigDecimal TOLERANCE = BigDecimal.ONE.scaleByPowerOfTen(-100);

    long start = System.nanoTime();

    BigDecimal a = new BigDecimal(1);
    BigDecimal b = new BigDecimal(2);
    BigDecimal b_old = new BigDecimal(2);
    while(a.add(b).divide(TWO).subtract(SQRT_TWO).abs().compareTo(TOLERANCE) >= 0) {
        b_old = b;
        b = a.multiply(b).add(TWO).divide(a.add(b), 100, BigDecimal.ROUND_HALF_UP);
        a = b_old;
    }

    return System.nanoTime() - start;
}

public static void main(String[] args) {
    double a = 0;
    int trials = 100;
    for(int i=1; i<= trials; i++)
    {
        a += (NewtonMethod() / 10e6);
    }
    System.out.printf("Newton's Method: %f\n", a/trials);
    a = 0;
    for(int i=1; i<= trials; i++)
    {
        a += (MidpointMethod() / 10e6);
    }
    System.out.printf("Midpoint Method: %f\n", a/trials);
    a = 0;
    for(int i=1; i<= trials; i++)
    {
        a += (SecantMethod() / 10e6);
    }
    System.out.printf("Secant Method: %f\n", a/trials);
}
}

一般来说,对Java代码进行基准测试是困难的。您的简单基准不够可靠

我发现您的方法存在三个问题(可能还有更多问题):

  • 您不能让JVM“预热”。当同一代码多次运行时,JVM开始优化。例如,内联并开始启动
  • 在测量期间,GC可以打开。它可能会把结果搞得一团糟
  • JVM只在类第一次使用时加载它们。因此,使用
    java.math.BigDecimal
    的第一个方法是不吉利的,因为它浪费了将该类加载到内存中的时间。其他方法没有此惩罚-
    BigDecimal
    已加载
  • 我向你推荐两件事:

  • 阅读这篇伟大的文章:
  • 使用可靠的Java基准测试框架-。它会自动生成在线可见的漂亮报告——参考此

  • (当您正确执行基准测试时-在评论中发布一个链接。我想看看您的方法如何执行)

    一般来说,对Java代码进行基准测试是很困难的。您的简单基准不够可靠

    我发现您的方法存在三个问题(可能还有更多问题):

  • 您不能让JVM“预热”。当同一代码多次运行时,JVM开始优化。例如,内联并开始启动
  • 在测量期间,GC可以打开。它可能会把结果搞得一团糟
  • JVM只在类第一次使用时加载它们。因此,使用
    java.math.BigDecimal
    的第一个方法是不吉利的,因为它浪费了将该类加载到内存中的时间。其他方法没有此惩罚-
    BigDecimal
    已加载
  • 我向你推荐两件事:

  • 阅读这篇伟大的文章:
  • 使用可靠的Java基准测试框架-。它会自动生成在线可见的漂亮报告——参考此

  • (当您正确执行基准测试时-在评论中发布一个链接。我想看看您的方法如何执行)

    尝试运行1000个不进行测量的试验,然后运行1000个进行测量的试验。JIT扮演着重要的角色,它需要很多执行来检测热点并编译它们。此外,您的测试分配了许多对象,因此GC可以启动一个缓慢的测试。我建议使用一个真正的微观基准测试框架,比如卡尺。试着在不测量的情况下运行1000次测试,然后在测量的情况下运行1000次测试。JIT扮演着重要的角色,它需要很多执行来检测热点并编译它们。此外,您的测试分配了许多对象,因此GC可以启动一个缓慢的测试。我建议使用真正的微观基准测试框架,如Caliper.Hi。我只尝试了牛顿迭代,但得到了一个错误,即基准无法解析为类型。请参见问题1中的“我的编辑”。确保正确导入了基准类并引用了jar。2.看看这个,你应该有方法
    timeNewton
    timemiddoint
    timeSecant
    。我只尝试了牛顿迭代,但得到了一个错误,即基准无法解析为类型。请参见问题1中的“我的编辑”。确保正确导入了基准类并引用了jar。2.看看这个,你应该有方法
    timeNewton
    timemiddoint
    timeSecant
    public class ScienceFairTwo {
    public static void main(String[] args) throws Exception {
    Runnable NewtonMethod = new Runnable()          
     {
            public void run()
            {
                    BigDecimal TWO = new BigDecimal(2);
                    BigDecimal SQRT_TWO = new BigDecimal("1.4142135623730950488016887242096980785696718753769480731766797379907324784621070388503875343276415727");
                    BigDecimal TOLERANCE = BigDecimal.ONE.scaleByPowerOfTen(-100);
    
                    long start = System.nanoTime();
                    BigDecimal a = new BigDecimal(1);
    
                    while(a.subtract(SQRT_TWO).abs().compareTo(TOLERANCE) >= 0) {
                        a = a.add(TWO.divide(a, 100, BigDecimal.ROUND_HALF_UP)).divide(TWO);
                    }
            }
        };
            System.out.println("Newton's Method: " + new Benchmark(NewtonMethod));
    }
    }