- PVSM.RU - https://www.pvsm.ru -

Precise timestamp

Пока идёт горячее обсуждение быть или нет быть jigsaw в java 9 и в каком виде ему быть — не стоит забывать про полезняшки, которые несёт с собой девятка — и одна из них — повышение точности Clock.systemUTC()JDK-8068730 [1].

Что же было раньше ?

До java 8 был System.currentTimeMillis() и System.nanoTime(), и если первый давал wall clock время, но с миллисекундным разрешением, то второй даёт время с разрешением до наносекунд, но область применения ограничена измерением разности времён, причём в рамках одной jvm — и ни о каком использовании такой временной метки между разными машинами и быть не может.

Поэтому часто велосипедят свои precise timestamp дающие wall clock время с большим разрешением, чем у currentTimeMillis (используя jni со всеми вытекающими) — более подробно про разницу между currentTimeMillis и nanoTime, и про велосипед можно почитать в моём старом посте [2].

Java 8 заложил очень мощный фундамент — Java Time API. С ним можно сказать пока и joda time, и встроить свой велосипед в java.time.Clock, т.к. штатный SystemClock по своей сути работает поверх System.currentTimeMillis() и не может обеспечить разрешение, лучше, чем миллисекунда.

И вот теперь в игру вступает java 9 и ничего не ломая (что касается времени и его измерения) приносит улучшение — можно выбросить свой jni велосипед, по крайней мере на Linux, MacOSX, BSD, Solaris и Windows — см. коммит в openjdk [3].

С практической точки зрения имеют смысл микросекундные временные метки, а не наносекундные — причина тому, что ntp в рамках intranet способна дать время с точностью до 1 мкс.

Запилим провайдера точного wall clock времени с микросекундным разрешением (исходники [4] в т.ч. и native часть):

public final class PreciseTimestamp {
    static final Clock clock = Clock.systemUTC();

    static {
        try {
            System.loadLibrary("precisetimestamp");
        } catch (Throwable e){
           throw new RuntimeException(e.getMessage(), e);
        }
    }

    // JNI microsecond timestamp provider
    public static native long getMicros();

    // Critical JNI microsecond timestamp provider
    public static native long getCMicros();

    public static long clockMicros(){
        final Instant instant = clock.instant();
        return TimeUnit.SECONDS.toMicros(instant.getEpochSecond()) + TimeUnit.NANOSECONDS.toMicros(instant.getNano());
    }
}    

Java 8 даст примерно такие результаты

JNI micros:             1494398000506568
Critical JNI micros:    1494398000506986
ClockUTC micros:        1494398000507000
currentTimeMillis:      1494398000507

Ничего удивительного — внутри старый-добрый System.currentTimeMillis()

И Java 9:

JNI micros:             1494398039238236
Critical JNI micros:    1494398039238439
ClockUTC micros:        1494398039238498
currentTimeMillis:      1494398039239

Т.е. штатный SystemClock действительно можно использовать как замену jni велосипеду.

Но как же перформанс ? — крикнут перформансники — и будут правы — на лицо лишнее создание объекта Instant.

Пилим benchmark [5], который сравнивает конечно же jni-велосипед (и обычный, и critical), метод основанный на clock, и для оценки масштабов бедствия System.currentTimeMillis() и System.nanoTime():

Precise timestamp - 1

Смотрите доклад про Escape Analysis и скаляризацию [6], если не понятно, почему вызов clock.instant() оказывается дороже (хоть и не намного), чем вызов более сложного метода clockMicros:

public static long clockMicros(){
    final Instant instant = clock.instant();
    return TimeUnit.SECONDS.toMicros(instant.getEpochSecond()) + TimeUnit.NANOSECONDS.toMicros(instant.getNano());
}

Выводы: Использование SystemClock в 9ке вполне может заменить jni велосипед — цена ~10% от вызова, много это или мало это — каждый решает сам — я готов жертвовать этими 10%, чтобы забыть головную боль про сборку jni библиотеки и не забывать её деплоить.

Автор: vladimir_dolzhenko

Источник [7]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/java/254858

Ссылки в тексте:

[1] JDK-8068730: https://bugs.openjdk.java.net/browse/JDK-8068730

[2] моём старом посте: http://dolzhenko.blogspot.com/2012/11/java-nanotime.html

[3] коммит в openjdk: http://hg.openjdk.java.net/jdk9/jdk9/hotspot/rev/fca33371ff0b

[4] исходники: https://gist.github.com/vladimirdolzhenko/a46431907d31bbfda73305d876711f28

[5] benchmark: https://gist.github.com/vladimirdolzhenko/a46431907d31bbfda73305d876711f28#file-perftiming-java

[6] доклад про Escape Analysis и скаляризацию: https://www.youtube.com/watch?v=K6c3W6vhQOA

[7] Источник: https://habrahabr.ru/post/328334/