如果我们最终获得一个带JDK8的快速日期时间库,我很好奇。几乎所有的LocalDate
计算都使用toEpochDay
所以我查看了source,如果我能做得更好,那么大量的分支和分支让我很好奇。
我淘汰了一些分支和除了一个分区以外的所有分支,但加速比预期差。所以我的第一个问题是如何使用多次除法的算法只需要大约30个周期(吞吐量)。 霍尔格的评论似乎已经回答了这个问题:一个小常数的除法得到了乘法的JIT-ed。 我是手动完成的,现在我一直以2倍的速度击败原来的实现。
benchmark非常简单,只需通过一组随机LocalDate
进行迭代,然后转换它们toEpochDay
。尽管具有随机性,但results非常一致。数组的大小是一个参数,我的主要问题是,其中2000和30000之间的大幅减速来自。应该会有一些减速,因为数据不再适合L1缓存,但两种算法的内存访问完全相同(即,只从数组中获取date
)。
仍然存在的问题是:在迭代数组时,同一函数的两个简单的无内存访问实现的行为是如何变化的?原始算法的速度会大幅下降比我的。
答案 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编译期间是否发生了这种优化,看看类文件并找出它会很有趣。