将方法传递给Java中的基准程序

时间:2017-09-04 15:08:12

标签: java algorithm parameter-passing benchmarking timing

我在Java中实现了许多排序算法,并且希望能够对每个算法进行计时和比较。目前,我有一个脚本,分别对每个算法进行计时,具有大量的代码冗余:

long min = 1000000;
long startTime;
long endTime;

QuickSort quicksorter = new QuickSort();

for (int i = 0; i != 10000; ++i) {
    startTime = System.nanoTime();
    quicksorter.sort();
    endTime = System.nanoTime();
    if ((endTime - startTime) < min)
        min = endTime - startTime;
}

System.out.printf("Quicksort min time is %d ns.\n", min);


BinaryInsertionSort binarysorter = new BinaryInsertionSort();
min = 1000000;

for (int i = 0; i != 10000; ++i) {
    startTime = System.nanoTime();
    binarysorter.sort();
    endTime = System.nanoTime();
    if ((endTime - startTime) < min)
        min = endTime - startTime;
}

System.out.printf("Binary insertion sort min time is %d ns.\n", min);

使用六种或七种不同的排序算法,这开始感觉非常低效。显而易见的&#34; python-esque&#34;解决方案是定义一个方法,该方法将方法作为参数并按如下方式计时:

long timeit(function func) {
    min = 1000000;

    for (int i = 0; i != 10000; ++i) {
        startTime = System.nanoTime();
        func();
        endTime = System.nanoTime();
        if ((endTime - startTime) < min)
            min = endTime - startTime;
    }

    System.out.printf("Min execution time is %d ns.\n", min);
}

但是,我认为在Java中将方法作为参数传递是不可取的(并且不方便),并且通常这样做可能是不可能的。我想每个排序器类都可以从一个实现基准测试方法的抽象类继承,但是有没有更好的方法在Java中实现这个?

2 个答案:

答案 0 :(得分:2)

  

但是,我认为在Java中将方法作为参数传递是不可取的(并且不方便),并且通常这样做可能是不可能的

嗯?

从Java 8开始,你可以传递这样的泛型方法作为参数;你想要的是Runnable

如果您在同一个类中有两个方法:

public final class Foo
{
    void f1() { /* whatever */ }
    void f2() { /* whatever */ }
}

然后您可以使用方法引用在代码中计算时间,如:

final Runnable r1 = Foo::f1;
final Runnable r2 = Foo::f2;

然后将这些方法引用(有效的lambdas)用于某些时序代码:

long start, end;

Stream.of(r1, r2).forEach(r -> {
    start = System.nanoTime();
    r.run();
    end = System.nanoTime();
    System.out.println("Time spent in ns: " + (end - start));
});

之后,您可以进一步优化代码,以便使用IntConsumers

现在,诚然,出于基准目的,一次运行不会这样做。有一个JIT需要考虑,JIT只会在执行相同代码路径的一定量执行后启动,以获得“特定数量”的某些定义。所以,虽然上面的代码会给你一个指示(对于这个指示的价值),你不应该把它当作绝对的。

出于基准测试目的,请考虑使用jmh ...并仔细阅读所有文档! JVM同时也是运行代码的高效复杂设备......

答案 1 :(得分:1)

你正在思考Java世界中的正确路线。但是,如果你不想在这种情况下搞乱继承,你可以通过反射来实现你需要的一种方法,方法如下:

public static long timeit(Class c) {
    long startTime, endTime, min;

    // new object and the method to call
    Object instance = c.newInstance();
    Method method = getClass().getDeclaredMethod("sort");

    min = Long.MAX_VALUE;

    for (int i = 0; i != 10000; ++i) {
        startTime = System.nanoTime();
        // call the function
        method.invoke(obj);
        endTime = System.nanoTime();
        min = endTime - startTime < min ? endTime - startTime : min;
    }
    return min;
}


//then you call it like this:
long time1 = timeit(QuickSort.class);

话虽如此,这种方法可能会引入一些开销,但它仍然可以给你一个很好的比较图片。