5

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

结果2

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

基准测试非常简单,只需遍历一个随机数组LocalDate并转换它们中的每一个toEpochDay。尽管存在随机性,但结果非常一致。数组的大小是一个参数,我的主要问题是2000 到 30000 之间的大幅减速来自哪里。由于数据不再适合 L1 缓存,因此应该会有所放缓,但是两种算法的内存访问完全相同(即,除了date从数组中获取之外没有)。

仍然悬而未决的问题是:当迭代一个数组时,同一函数的两个简单的无内存访问实现的行为是如何发生变化的?原始算法的减速比我的要大得多。

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

4

2 回答 2

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;
    }
}
于 2014-02-05T09:41:42.120 回答
0

那是因为算法中没有除法。所有 / 4 都被班次取代。所有的 / 100 实际上都是 * 0.01。这些部门是为了可读性而存在的(呵呵)。我不确定这种优化是否发生在字节码发射或 JIT 编译期间,查看类文件并找出答案会很有趣。

于 2014-02-05T02:26:01.017 回答