JDK8 LocalDate.toEpochDay的性能下降奇怪

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

结果2

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

基准测试非常简单,只需迭代一组随机的LocalDate并将它们转换为toEpochDay 。 尽管具有随机性,但结果非常一致。 数组的大小是一个参数,我的主要问题是2000到30000之间的大幅减速来自何处 。 应该会有一些减速,因为数据不再适合L1缓存,但两种算法的内存访问完全相同(即,没有,但从数组中获取date )。

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

我的算法在这里可能不值得复制,它没有文档记录,与原版一样神秘,而且只有一个非常基本的测试 。

我没有直接理解这个原因,但它肯定是一个基准框架的缺点。 与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; } } 

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