当前位置:  开发笔记 > 编程语言 > 正文

使用新的java.time API解析时区的速度非常慢

如何解决《使用新的java.timeAPI解析时区的速度非常慢》经验,为你挑选了1个好方法。

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

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

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

有没有人知道我是否能以某种方式使用新的java.timeAPI 快速解析这些字符串?目前,作为一种解决方法,我使用旧的API进行解析,然后将其转换Date为Instant,这不是特别好.

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

以及100K操作的结果:

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

更新:

我刚刚对java.time类进行了一些分析,实际上,时区解析器似乎实现效率非常低.解析一个独立的时区只会导致所有的缓慢.

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

    DateTimeFormatter dateTimeFormatter = new DateTimeFormatterBuilder()
            .appendPattern("z").toFormatter();

    for(int i=0; i

ZoneTextPrinterParserjava.timebundle中调用了一个类,它在内部制作每个parse()调用(via ZoneRulesProvider.getAvailableZoneIds())中所有可用时区的集合的副本,这对于区域解析花费的99%的时间负责.

那么,答案可能是编写我自己的区域解析器,这也不会太好,因为那时我无法构建DateTimeFormatter通道appendPattern().



1> user1803551..:

如您的问题和我的评论中所述,每次需要解析时区时,ZoneRulesProvider.getAvailableZoneIds()都会创建一组新的所有可用时区的字符串表示形式(其中的键static final ConcurrentMap ZONES).1

幸运的是,a ZoneRulesProvider是一个abstract设计为子类的类.该方法protected abstract Set provideZoneIds()负责填充ZONES.因此,如果子类提前知道要使用的所有时区,则子类只能提供所需的时区.由于该类提供的条目少于默认提供程序(包含数百个条目),因此它有可能显着减少调用时间getAvailableZoneIds().

该ZoneRulesProvider API提供了如何注册一个指令.请注意,提供程序无法取消注册,只能进行补充,因此删除默认提供程序并添加自己的提供程序并不是一件简单的事情.system属性java.time.zone.DefaultZoneRulesProvider定义默认提供程序.如果它返回null(via System.getProperty("...")则加载JVM的臭名昭着的提供者.使用System.setProperty("...", "fully-qualified name of a concrete ZoneRulesProvider class")一个可以提供他们自己的提供者,这是第2段中讨论的提供者.

最后,我建议:

    子类 abstract class ZoneRulesProvider

    实现了protected abstract Set provideZoneIds()只用所需的时区.

    将系统属性设置为此类.

我自己没有这样做,但我确信它会因为某些原因而失败,认为它会起作用.


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

编辑:找到更多信息

上述默认ZoneRulesProviderfinal class TzdbZoneRulesProvider位于java.time.zone.从路径中读取该类中的区域:( JAVA_HOME/lib/tzdb.dat在我的例子中,它位于JDK的JRE中).该文件确实包含许多区域,这里是一个片段:

 TZDB  2014cJ Africa/Abidjan Africa/Accra Africa/Addis_Ababa Africa/Algiers 
Africa/Asmara 
Africa/Asmera 
Africa/Bamako 
Africa/Bangui 
Africa/Banjul 
Africa/Bissau Africa/Blantyre Africa/Brazzaville Africa/Bujumbura Africa/Cairo Africa/Casablanca Africa/Ceuta Africa/Conakry Africa/Dakar Africa/Dar_es_Salaam Africa/Djibouti 
Africa/Douala Africa/El_Aaiun Africa/Freetown Africa/Gaborone 
Africa/Harare Africa/Johannesburg Africa/Juba Africa/Kampala Africa/Khartoum 
Africa/Kigali Africa/Kinshasa Africa/Lagos Africa/Libreville Africa/Lome 
Africa/Luanda Africa/Lubumbashi 
Africa/Lusaka 
Africa/Malabo 
Africa/Maputo 
Africa/Maseru Africa/Mbabane Africa/Mogadishu Africa/Monrovia Africa/Nairobi Africa/Ndjamena 
Africa/Niamey Africa/Nouakchott Africa/Ouagadougou Africa/Porto-Novo Africa/Sao_Tome Africa/Timbuktu Africa/Tripoli Africa/Tunis Africa/Windhoek America/Adak America/Anchorage America/Anguilla America/Antigua America/Araguaina America/Argentina/Buenos_Aires America/Argentina/Catamarca  America/Argentina/ComodRivadavia America/Argentina/Cordoba America/Argentina/Jujuy America/Argentina/La_Rioja America/Argentina/Mendoza America/Argentina/Rio_Gallegos America/Argentina/Salta America/Argentina/San_Juan America/Argentina/San_Luis America/Argentina/Tucuman America/Argentina/Ushuaia 
America/Aruba America/Asuncion America/Atikokan America/Atka 
America/Bahia

然后,如果找到一种方法来创建仅包含所需区域的类似文件并加载该区域,则可能无法确定性能问题.

推荐阅读
oDavid_仔o_880
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有