API解析时区的速度非常慢

API解析时区的速度非常慢

本文介绍了使用新的java.time API解析时区的速度非常慢的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我只是将模块从旧的java日期迁移到新的java.time API,并注意到性能大幅下降。归结为使用时区解析日期(我一次解析数百万个)。

I was just migrating a module from the old java dates to the new java.time API, and noticed a huge drop in performance. It boiled down to parsing of dates with timezone (I parse millions of them at a time).

解析没有时区的日期字符串( yyyy / MM / dd HH:mm:ss )速度快 - 比旧的Java日期快2倍,在我的电脑上每秒约1.5M操作。

Parsing of date string without a time zone (yyyy/MM/dd HH:mm:ss) is fast - about 2 times faster than with the old java date, about 1.5M operations per second on my PC.

但是,当模式包含时区时( yyyy / MM / dd HH:mm:ss z )使用新的 java.time API,性能下降了大约15倍,而使用旧的API时,它的速度与没有时区的速度一样快。请参阅下面的性能基准。

However, when the pattern contains a time zone (yyyy/MM/dd HH:mm:ss z), the performance drops about 15 times with the new java.time API, while with the old API it is about as fast as without a time zone. See the performance benchmark below.

有没有人知道我是否可以使用新的 java.time API?目前,作为一种解决方法,我使用旧的API进行解析,然后将 Date 转换为Instant,这不是特别好。

Does anyone have an idea if I can somehow parse these strings quickly using the new java.time API? At the moment, as a workaround, I am using the old API for parsing and then convert the Date to Instant, which is not particularly nice.

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder;
import java.util.concurrent.TimeUnit;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OperationsPerInvocation;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;
import org.openjdk.jmh.infra.Blackhole;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

@OutputTimeUnit(TimeUnit.MILLISECONDS)
@BenchmarkMode(Mode.AverageTime)
@OperationsPerInvocation(1)
@Fork(1)
@Warmup(iterations = 3)
@Measurement(iterations = 5)
@State(Scope.Thread)
public class DateParsingBenchmark {

    private final int iterations = 100000;

    @Benchmark
    public void oldFormat_noZone(Blackhole bh, DateParsingBenchmark st) throws ParseException {

        SimpleDateFormat simpleDateFormat =
                new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

        for(int i=0; i<iterations; i++) {
            bh.consume(simpleDateFormat.parse("2000/12/12 12:12:12"));
        }
    }

    @Benchmark
    public void oldFormat_withZone(Blackhole bh, DateParsingBenchmark st) throws ParseException {

        SimpleDateFormat simpleDateFormat =
                new SimpleDateFormat("yyyy/MM/dd HH:mm:ss z");

        for(int i=0; i<iterations; i++) {
            bh.consume(simpleDateFormat.parse("2000/12/12 12:12:12 CET"));
        }
    }

    @Benchmark
    public void newFormat_noZone(Blackhole bh, DateParsingBenchmark st) {

        DateTimeFormatter dateTimeFormatter = new DateTimeFormatterBuilder()
                .appendPattern("yyyy/MM/dd HH:mm:ss").toFormatter();

        for(int i=0; i<iterations; i++) {
            bh.consume(dateTimeFormatter.parse("2000/12/12 12:12:12"));
        }
    }

    @Benchmark
    public void newFormat_withZone(Blackhole bh, DateParsingBenchmark st) {

        DateTimeFormatter dateTimeFormatter = new DateTimeFormatterBuilder()
                .appendPattern("yyyy/MM/dd HH:mm:ss z").toFormatter();

        for(int i=0; i<iterations; i++) {
            bh.consume(dateTimeFormatter.parse("2000/12/12 12:12:12 CET"));
        }
    }

    public static void main(String[] args) throws RunnerException {
        Options opt = new OptionsBuilder().include(DateParsingBenchmark.class.getSimpleName()).build();
        new Runner(opt).run();
    }
}

以及100K操作的结果:

And the results for 100K operations:

Benchmark                                Mode  Cnt     Score     Error  Units
DateParsingBenchmark.newFormat_noZone    avgt    5    61.165 ±  11.173  ms/op
DateParsingBenchmark.newFormat_withZone  avgt    5  1662.370 ± 191.013  ms/op
DateParsingBenchmark.oldFormat_noZone    avgt    5    93.317 ±  29.307  ms/op
DateParsingBenchmark.oldFormat_withZone  avgt    5   107.247 ±  24.322  ms/op

认为它会起作用。

I did not do it myself, but I think it will work.

在问题的评论中建议调用的确切性质可能在1.8版本之间发生了变化。

It is suggested in the comments of the question that the exact nature of the invocation might have changed between 1.8 versions.

肯定会被解决。

Then If one finds a way to create a similar file with only the needed zones and load that one instead, the performance issues will surely be resolved.

这篇关于使用新的java.time API解析时区的速度非常慢的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-31 10:50