JDK8 LocalDate.toEpochDay的性能下降奇怪

时间:2014-02-05 01:56:21

标签: java performance datetime benchmarking caliper

如果我们最终获得一个带JDK8的快速日期时间库,我很好奇。几乎所有的LocalDate计算都使用toEpochDay所以我查看了source,如果我能做得更好,那么大量的分支和分支让我很好奇。

results2

我淘汰了一些分支和除了一个分区以外的所有分支,但加速比预期差。所以我的第一个问题是如何使用多次除法的算法只需要大约30个周期(吞吐量)。 霍尔格的评论似乎已经回答了这个问题:一个小常数的除法得到了乘法的JIT-ed。 我是手动完成的,现在我一直以2倍的速度击败原来的实现。

benchmark非常简单,只需通过一组随机LocalDate进行迭代,然后转换它们toEpochDay。尽管具有随机性,但results非常一致。数组的大小是一个参数,我的主要问题是,其中2000和30000之间的大幅减速来自。应该会有一些减速,因为数据不再适合L1缓存,但两种算法的内存访问完全相同(即,只从数组中获取date)。

仍然存在的问题是:在迭代数组时,同一函数的两个简单的无内存访问实现的行为是如何变化的?原始算法的速度会大幅下降比我的。

我的algorithm可能不值得在这里复制,它没有文档记录,与原版一样神秘,而且只有一个非常简陋的test

2 个答案:

答案 0 :(得分:1)

我没有直接了解原因,但它确实是一个基准测试框架的缺点。与GC和每次调用成本相关的事情。我与JMH的性能相同,只有100个日期的工作台显示出比2000个日期更好的性能。我试图创建始终具有最大大小的dates数组,并且只迭代前100个,2000个,30000个元素。在这种情况下,所有版本的执行均等(在我的机器上为15.3 + - 0.3 ns)。

import org.openjdk.jmh.annotations.*;

import java.time.LocalDate;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;


@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OperationsPerInvocation(LocalDateBenchmark.ITERATIONS)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public class LocalDateBenchmark {
    public static final int MAX_ITERATIONS = 1000000;
    public static final int ITERATIONS = 30000;

    private static final LocalDate MIN_DATE = LocalDate.of(1900, 1, 1);
    private static final LocalDate MAX_DATE = LocalDate.of(2100, 1, 1);
    private static final int DAYS_BETWEEN = (int) (MAX_DATE.toEpochDay() - MIN_DATE.toEpochDay());

    public LocalDate[] dates = new LocalDate[MAX_ITERATIONS];
    private Random random;

    @Setup(Level.Trial)
    public void setUpAll() {
        Random r = ThreadLocalRandom.current();
        for (int i=0; i< dates.length; ++i) {
            dates[i] = MIN_DATE.plusDays(r.nextInt(DAYS_BETWEEN));
        }
    }

    @Setup(Level.Iteration)
    public void setUpRandom() {
        random = new Random();
    }

    @GenerateMicroBenchmark
    public int timeToEpochDay(LocalDateBenchmark state) {
        int result = 0;
        LocalDate[] dates = state.dates;
        int offset = random.nextInt(MAX_ITERATIONS - ITERATIONS);
        for (int i = offset; i < offset + ITERATIONS; i++) {
            LocalDate date = dates[i];
            result += date.toEpochDay();
        }
        return result;
    }
}

答案 1 :(得分:0)

那是因为算法中没有分歧。所有/ 4都由轮班取代。并且所有/ 100实际上都是* 0.01。这些分歧是为了可读性(呵呵)。我不确定在字节码发射或JIT编译期间是否发生了这种优化,看看类文件并找出它会很有趣。