【问题标题】:Any good high performance java library that works with timestamp?任何与时间戳一起使用的高性能java库?
【发布时间】:2011-06-24 08:37:29
【问题描述】:

我正在寻找高性能的 Java 库,它提供的方法可以让我获得:

  1. 当前时间 (0-24)
  2. 当天 (1-31)
  3. 本周 (1-52)
  4. 当月 (1-12)
  5. 当前赛季 (1-4)
  6. 当年(YYYY 或 YY)

性能对我来说是最重要的问题。这就是为什么我不能使用标准日历类。最好的解决方案是在不创建新对象的情况下完成所有计算。

添加:为了澄清,我再提一次:每秒超过 100000 次操作。

【问题讨论】:

  • 日历非常慢。我需要做大约 +100000 次操作。日历不适用于我的情况。需要某种纯数学公式。没有对象。
  • +100000 需要多少时间?如果您必须在 1 天内这样做,没问题 :-)
  • 您是否正确使用Calendar?你能展示一些示例代码吗?
  • 我很想知道 Joda Time 是如何像@Gareth 的回答中所建议的那样对您产生如此大的影响。我发布了another answer(我想这更像是一个问题)来询问cmets。我的实验表明,对于您似乎正在使用的用途,Joda Chronology 比 JDK Calendar 慢。

标签: java performance datetime timestamp


【解决方案1】:

您实际上可以使用出色的 joda-time 库来执行此操作。 joda 中的 Chronology 实现提供了 DateTime getHour getDay 类型方法背后的逻辑。如果您想跳过创建DateTime 瞬间,直接使用它没有问题。

long msSinceEpoch = System.currentTimeMillis();

Chronology chronology = ISOChronology.getInstanceUTC() // This can be static

chronology.hourOfDay().get(msSinceEpoch);
chronology.dayOfMonth().get(msSinceEpoch);
chronology.weekOfWeek().get(msSinceEpoch);
chronology.monthOfYear().get(msSinceEpoch);
chronology.years().get(msSinceEpoch);

会话不是 joda 的概念。根据您自己的规则,您必须从该月开始计算。

joda-time documentation on Chronology

【讨论】:

  • 对了,Season 我可以轻松计算自己了 :-) 谢谢你的建议!
  • 嗨,Davis,JODA 的工作就像一个魅力。每秒 4 000 000 次操作。只是想知道您使用的是哪个 JDK?很奇怪你有 System.currentTimeInMillis();我只有 System.currentTimeMillis();再次感谢你们,祝英格兰一切顺利!
  • 我使用自己定制的OpenJDK版本,我自己编译并在智能手机上运行只是为了惹恼Oracle
【解决方案2】:

您确定Calendar 太慢还是您只是相信?您测量了吗?

在 Java 中创建新对象非常便宜。保留它们很昂贵。

也就是说,要么尝试 Joda Time,要么将 Java 代码中的算法复制到静态辅助方法中。

【讨论】:

  • 非常感谢亚伦的建议。当然,我做了一些测量。日历非常慢,它的实现无法处理我的负载。
  • 我尝试了measuring it 使用问题中建议的用法并接受了答案。在这种情况下,日历的运行速​​度似乎比 Joda 快。
【解决方案3】:

您可以获取当前时间并通过除法和余数计算当前小时、分钟、秒、毫秒。日、月、年每天只变化一次,所以你只需要在变化时计算。

您可以使用缓存的日/月/年并在亚微秒时间内计算其余部分,而无需创建对象(每天超过一次)

import java.util.Calendar;
import java.util.TimeZone;

public class Daytime {
    private static final long HOUR_MS = 3600*1000;

    public short year;
    public byte month, day;
    public byte hour, min, sec;
    public short millis;

    private long lastMillis = Integer.MIN_VALUE;
    private final TimeZone timeZone;
    private long timeOffset;

    public Daytime() {
        timeZone = TimeZone.getTimeZone("UTC");
        now();
    }

    public Daytime(TimeZone timeZone) {
        this.timeZone = timeZone;
        now();
    }

    public void now() {
        long now = System.currentTimeMillis() + timeOffset;
        if (now == lastMillis) return;
        long cachePeriod = now/HOUR_MS;
        // could the day have changed?
        if (cachePeriod != lastMillis/HOUR_MS) {
            timeOffset = timeZone.getOffset(now);
            Calendar cal = Calendar.getInstance(timeZone);
            year = (short) cal.get(Calendar.YEAR);
            month = (byte) cal.get(Calendar.MONTH);
            day = (byte) cal.get(Calendar.DAY_OF_MONTH);
            hour = (byte) cal.get(Calendar.HOUR);
        }
        millis = (short) (now % 1000);
        now /= 1000;
        sec = (byte) (now % 60);
        now /= 60;
        min = (byte) (now % 60);
    }

    public static void main(String... args) {
        Daytime dt = new Daytime();
        long start = System.nanoTime();
        final int runs = 10 * 1000 * 1000;
        for(int i=0;i< runs;i++)
            dt.now();
        long time = System.nanoTime() - start;
        System.out.printf("Daytime.now() took %.3f micro-seconds on average%n", time/1e3/runs);
    }
}

打印

Daytime.now() took 0.287 micro-seconds on average

关于夏令时更改的发生方式有多种假设。您可能需要更改检查周期以满足您的需要。

【讨论】:

  • 感谢您提供基准代码。实际上,我必须为大量在线和离线用户计算一些近乎实时的统计数据。统计数据基于我在上面问题中提到的数字。这就是为什么我很难调整计算。
  • 我相信使用缓存的信息是要走的路。如果您想要亚毫秒级计时,您需要使用 System.nanoTime() 或通过 JNI 进行系统调用。
【解决方案4】:

只使用一个对象并像这样更新它:

Date d = new Date();
...
d.setTime(System.currentTimeMillis());

【讨论】:

  • Sun 的 java.util.Date 版本在您调用 setTime() 后会创建一个新对象,所以我猜它不适合。
  • @fernacolo 有什么来源吗?我不知道。
  • 我无法粘贴 Sun 的代码,因为它受版权保护且“机密”,但您可以检查一下:安装 JDK 5 或更早版本,打开 src.zip,找到 java/util/Date.java,并查看 setTime(long millis)。这将清除名为 cdate 的引用。然后,无论何时您需要某个组件(如年、日),它都会调用 normalize() 来为该引用实例化一个新对象。
【解决方案5】:

您可以使用java.util.Calander,如果对此不满意,您可以使用Joda Time

这可以帮助你。

【讨论】:

    【解决方案6】:

    您可以使用Joda Time,因为它比日历快得多,然后这样做:

    LocalDateTime now = new LocalDateTime(); // automatically points to current datetime.
    int hour = now.getHourOfDay();
    int day = now.getDayOfMonth();
    int week = now.getWeekOfWeekyear();
    int month = now.getMonthOfYear();
    int year = now.getYear();
    int season = getSeason(day, month);
    

    getSeason() 很容易实现。没有任何解决方案可以在不创建任何对象的情况下完成所有这些操作。对了,你为什么需要这么多性能?!?!

    【讨论】:

      【解决方案7】:

      请注意,Calendar.getInstance 非常昂贵,但您通常可以通过调用该方法一次并重用来获取您的 Calendar 对象(除非您需要为每个线程提供日历:日历不是线程安全的)。

      【讨论】:

      • 我跑了一个小板凳,可以在 1 秒内调用 Calendar.getInstance() 50.000 次。怎么这么慢?
      • 我可以在 1 秒内循环创建 500 万个不同的字符串。我为什么要关心在执行热点中创建大量字符串? 如果您查看日历创建,它并不完全是轻量级操作。如果您在执行热点中分析代码,我想您会发现维护单个 Calendar 会比每次需要 Calendar 时调用 Calendar.getInstance 便宜。我似乎记得自己在生产代码中进行了更改。但这只是一个建议。
      • 顺便说一句,在你的基准测试中试试这个。在循环中使用 calendar.getTime() 获取日期。版本 1:日历是在循环内创建的。版本 2:日历是在循环之前创建的。加快重复次数。哪个跑得更快? (我发现第 2 版的运行速度快了 20 倍以上)。
      【解决方案8】:

      我已经在 SO 上阅读了很多关于 Joda Time 的帖子,现在我终于下载并试了一下。

      Gareth Davis 的回答已经被接受,我对此没有异议。但我很想知道 Joda Time 的不同之处在哪里。

      根据问题和接受的答案,我做了一个类来比较JDK Calendar和Joda Time Chronology的执行时间。

      我发现 Calendar 实现始终运行得更快,而不是快两倍。

      import java.util.Calendar;
      import java.util.Date;
      
      import org.joda.time.Chronology;
      import org.joda.time.chrono.ISOChronology;
      
      
      public class CalendarTest {
      
          private static final int ITERATIONS = 1000000;
      
          public static void main(String[] args) {
              for (int i = 0; i < 10; i++) {
                  jdkCalendar();
                  jodaChronology();
              }
          }
      
          private static void jdkCalendar() {
              long start = System.currentTimeMillis();
              Calendar c = Calendar.getInstance();
              int hourOfDay = 0;
              int dayOfMonth = 0;
              int weekOfYear = 0;
              int month = 0;
              int year = 0;
              for (int i = 0; i < ITERATIONS; i++) {
                  c.setTimeInMillis(System.currentTimeMillis());
                  hourOfDay = c.get(Calendar.HOUR_OF_DAY);
                  dayOfMonth = c.get(Calendar.DAY_OF_MONTH);
                  weekOfYear = c.get(Calendar.WEEK_OF_YEAR);
                  month = c.get(Calendar.MONTH);
                  year = c.get(Calendar.YEAR);
              }
              long duration = System.currentTimeMillis() - start;
              System.err.printf("jdk:  duration %d, hourOfDay: %d, dayOfMonth: %d, weekOfYear: %d, month: %d, year: %d\n", duration, hourOfDay, dayOfMonth, weekOfYear, month, year);
          }
      
          private static void jodaChronology() {
              long start = System.currentTimeMillis();
              Chronology chronology = ISOChronology.getInstanceUTC(); // This can be static
              int hourOfDay = 0;
              int dayOfMonth = 0;
              int weekOfYear = 0;
              int month = 0;
              int year = 0;
              for (int i = 0; i < ITERATIONS; i++) {
                  long msSinceEpoch = System.currentTimeMillis();
                  hourOfDay = chronology.hourOfDay().get(msSinceEpoch);
                  dayOfMonth = chronology.dayOfMonth().get(msSinceEpoch);
                  weekOfYear = chronology.weekOfWeekyear().get(msSinceEpoch);
                  month = chronology.monthOfYear().get(msSinceEpoch);
                  year = chronology.years().getValue(msSinceEpoch);
              }
              long duration = System.currentTimeMillis() - start;
              System.err.printf("joda: duration %d, hourOfDay: %d, dayOfMonth: %d, weekOfYear: %d, month: %d, year: %d\n", duration, hourOfDay, dayOfMonth, weekOfYear, month, year);
          }
      
      }
      

      样本输出:

      jdk:  duration 1714, hourOfDay: 12, dayOfMonth: 24, weekOfYear: 25, month: 5, year: 2011
      joda: duration 2099, hourOfDay: 12, dayOfMonth: 24, weekOfYear: 25, month: 6, year: 41
      jdk:  duration 377, hourOfDay: 12, dayOfMonth: 24, weekOfYear: 25, month: 5, year: 2011
      joda: duration 689, hourOfDay: 12, dayOfMonth: 24, weekOfYear: 25, month: 6, year: 41
      jdk:  duration 340, hourOfDay: 12, dayOfMonth: 24, weekOfYear: 25, month: 5, year: 2011
      joda: duration 680, hourOfDay: 12, dayOfMonth: 24, weekOfYear: 25, month: 6, year: 41
      jdk:  duration 330, hourOfDay: 12, dayOfMonth: 24, weekOfYear: 25, month: 5, year: 2011
      joda: duration 653, hourOfDay: 12, dayOfMonth: 24, weekOfYear: 25, month: 6, year: 41
      jdk:  duration 326, hourOfDay: 12, dayOfMonth: 24, weekOfYear: 25, month: 5, year: 2011
      joda: duration 596, hourOfDay: 12, dayOfMonth: 24, weekOfYear: 25, month: 6, year: 41
      jdk:  duration 337, hourOfDay: 12, dayOfMonth: 24, weekOfYear: 25, month: 5, year: 2011
      joda: duration 620, hourOfDay: 12, dayOfMonth: 24, weekOfYear: 25, month: 6, year: 41
      jdk:  duration 471, hourOfDay: 12, dayOfMonth: 24, weekOfYear: 25, month: 5, year: 2011
      joda: duration 590, hourOfDay: 12, dayOfMonth: 24, weekOfYear: 25, month: 6, year: 41
      jdk:  duration 326, hourOfDay: 12, dayOfMonth: 24, weekOfYear: 25, month: 5, year: 2011
      joda: duration 591, hourOfDay: 12, dayOfMonth: 24, weekOfYear: 25, month: 6, year: 41
      jdk:  duration 336, hourOfDay: 12, dayOfMonth: 24, weekOfYear: 25, month: 5, year: 2011
      joda: duration 595, hourOfDay: 12, dayOfMonth: 24, weekOfYear: 25, month: 6, year: 41
      jdk:  duration 327, hourOfDay: 12, dayOfMonth: 24, weekOfYear: 25, month: 5, year: 2011
      joda: duration 560, hourOfDay: 12, dayOfMonth: 24, weekOfYear: 25, month: 6, year: 41
      

      【讨论】:

      • 嗨,Sudocode!非常感谢您的基准测试。我做了同样的事情(代码有点不同,但也做了 1 000 000 次操作)。我有 JODA:〜300 和日历〜720。没那么多,但对我来说日历还是比较慢。我在 Java 版本“1.6.0_22”Java(TM) SE 运行时环境(构建 1.6.0_22-b04-307-10M3261)Java HotSpot(TM) 64 位服务器 VM(构建 17.1-b03-307,混合模式)上进行了测试)
      • @MinimeDJ 我现在尝试使用 3 个不同的 JVM 运行相同的代码,第一个是来自我原始基准测试的那个:IBM Corporation, 1.5.0, 2.3, IBM J9虚拟机。 Sun Microsystems Inc.,1.6.0_24,19.1-b02,Java HotSpot(TM) 64 位服务器虚拟机。 IBM Corporation,1.6.0,2.4,IBM J9 VM。总体而言,1.6 JVM 的性能更好,但 JDK 与 Joda 并没有逆转。对于此代码,JDK 日历实现仍然始终优于 Joda Chronlogy 实现。
      【解决方案9】:

      当您需要在每次调用字段的 getter 之前创建 DateTime 类的实例时,我会推荐 JodaTime。但是,当您调用 getter 方法而不是创建实例时,最好使用 JDK 8 中的 java.time 库,或向后移植 ThreeTenABP

      这是因为 JodaTime 的 DateTime 类自纪元开始以来仅存储 long iMillis,并且某些字段(如 minuteOfHour、dayOfMonth)的所有 getter 总是从头开始计算此值。但是ZonedDateTime of java.time 将此字段存储在单独的变量中,例如:int year, short month, short day,...
      我创建了some benchmarks for comparision

      【讨论】:

        猜你喜欢
        • 2014-06-17
        • 2020-07-26
        • 1970-01-01
        • 2016-04-18
        • 1970-01-01
        • 2021-01-20
        • 2023-03-30
        • 1970-01-01
        • 2015-05-25
        相关资源
        最近更新 更多