java中向基准程序传递算法的方法

5kgi1eie  于 2021-07-03  发布在  Java
关注(0)|答案(2)|浏览(274)

我正在用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);

有了六七种不同的排序算法,这就开始觉得效率极低了。显而易见的“python式”解决方案是定义一个方法,该方法将方法作为参数并按如下方式对其进行计时:

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中把方法作为参数传递是不可取的(也不方便),一般来说这样做可能是不可能的。我假设每个sorter类都可以从一个实现基准测试方法的抽象类继承,但是有没有更好的方法在java中实现这一点呢?

baubqpgj

baubqpgj1#

在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);

话虽如此,这种方法可能会带来一些开销,但它仍然会给您提供一个很好的对比图。

i2byvkas

i2byvkas2#

然而,我认为在java中传递方法作为参数是不可取的(也不方便),一般来说这样做可能是不可能的
嗯?
从Java8开始,您可以传递诸如参数之类的泛型方法;你想要的是 Runnable .
如果在同一类中有两个方法:

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

然后,您可以使用方法引用在代码中对两者进行计时,如下所示:

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

然后使用这些方法引用(有效的lambda)生成一些计时代码:

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));
});

之后,您可以进一步细化代码,以便使用,例如, IntConsumer s。
现在,不可否认,出于基准测试的目的,一次运行是不行的。需要考虑的是jit,对于某个“一定量”的定义,jit只会在同一代码路径执行一定量之后启动。因此,尽管上面的代码会给你一个指示(这个指示值多少),但无论如何你都不应该把它当作绝对值。
为了进行基准测试,请考虑使用jmh。。。仔细阅读所有文件!jvm同时是运行代码的高效和复杂的工具。。。

相关问题