System currenttimemillis java что возвращает
Приёмы и хитрости начинающего Java-программиста
Авторизуйтесь
Приёмы и хитрости начинающего Java-программиста
эксперт по разработке ПО компании «Рексофт»
В этой статье собрана небольшая коллекция практик, трюков и подсказок, с помощью которых вы сэкономите своё время при изучении Java и написании кода на этом языке программирования.
Организация работы
Чистый код
В крупных проектах на первый план выходит не создание нового кода, а поддержка существующего, поэтому очень важно с самого начала его правильно организовать. При разработке нового приложения всегда помните о трех основных принципах чистого и поддерживаемого кода:
Работа с ошибками
Stack Trace (Трассировка стека)
Выявление ошибок — это, пожалуй, самая трудоемкая часть процесса разработки на Java. Трассировка стека позволяет вам точно отслеживать, где именно в проекте возникла ошибка или исключение (exception).
NullPointerException
Исключения, возникающие из-за null значений ( NullPointerException ), довольно часто появляются при попытке вызвать метод у несущестующего объекта.
Возьмем для примера следующий код:
Прим. перев. А вот пример от меня как переводчика материала:
Дата и Время
System.currentTimeMillis или System.nanoTime?
В Java есть два стандартных способа проведения операций со временем, и не всегда ясно, какой из них следует выбрать.
Метод System.currentTimeMillis() возвращает текущее количество миллисекунд с начала эры Unix в формате Long. Его точность составляет от 1 до 15 тысячных долей секунды в зависимости от системы.
Метод System.nanoTime() имеет точность до одной миллионной секунды (наносекунды) и возвращает текущее значение наиболее точного доступного системного таймера.
Таким образом, метод System.currentTimeMillis() лучше применять для отображения и синхронизации абсолютного времени, а System.nanoTime() для измерения относительных интервалов времени.
Валидация Даты из строки
Пример его использования:
Строки
Оптимизация строки
Для конкатенации (сложения) строк в Java используется оператор «+», для примера, в цикле for новый объект может создаваться для каждой новой строки, что приводит к потере памяти и увеличению времени работы программы.
Необходимо избегать создания Java строк через конструктор, пример:
Одинарные и двойные кавычки
Что ты ожидаешь в результате выполнения этого кода?
Казалось бы, строка должна возвращать «HaHa», но на самом деле это будет «Ha169».
Двойные кавычки обрабатывают символы как строки, но одинарные кавычки ведут себя иначе. Они преобразуют символьные операнды ( ‘H’ и ‘a’ ) в целые значения посредством расширения примитивных типов — получается 169.
Математика
Float или Double?
Программисты часто не могут выбрать необходимую точность для чисел с плавающей запятой. Float требует всего 4 байта, но имеет только 7 значащих цифр, а Double в два раза точнее (15 цифр), но в два раза прожорливее.
Фактически, большинство процессоров могут одинаково эффективно работать как с Float, так и с Double, поэтому воспользуйтесь рекомендацией Бьорна Страуструпа (автор языка С++):
Выбор правильной точности для решения реальных задач требует хорошего понимания природы машинных вычислений. Если у вас его нет, либо посоветуйтесь с кем-нибудь, либо изучите проблему самостоятельно, либо используйте Double и надейтесь на лучшее.
Проверка на нечетность
Можно ли использовать этот код для точного определения нечетного числа?
Он не только решает проблему отрицательных чисел, но и работает более производительно, чем предыдущий метод. Арифметические и логические операции выполняются намного быстрее, чем умножение и деление.
Возведение в степень
Возвести число в степень можно двумя способами:
Использование библиотечной функции рекомендуется только в случае крайней необходимости, например, в случае дробной или отрицательной степени.
Простое умножение в Java работает в 300-600 раз эффективнее, кроме того, его можно дополнительно оптимизировать:
JIT оптимизация
Код Java обрабатывается с использованием JIT-компиляции: сначала он транслируется в платформенно-независимый байт-код, а затем в машинный код. При этом оптимизируется все возможное, и разработчик может помочь компилятору создать максимально эффективную программу.
В качестве примера рассмотрим две простые операции:
Давайте измерим время выполнения каждого из них:
Запустив этот код несколько раз, мы получим примерно следующее:
Схема очевидна: группировка переменных в круглые скобки ускоряет работу программы. Это связано с генерацией более эффективного байт-кода при умножении одинаковых значений.
Вы можете узнать больше об этом эксперименте здесь. Или можете провести свой собственный тест, используя онлайн-компилятор Java.
Java Specialist
Блог о памяти, сборщике мусора, многопоточности и производительности в java
Содержание
понедельник, 16 апреля 2012 г.
Измерение времени и засыпание потоков
System.currentTimeMillis()
System.nanoTime()
Второй метод использует специальные счетчики, не связанные с системными часами (хотя на некоторых платформах он и может быть реализован через них, но это скорее исключение нежели правило). Формально System.nanoTime() возвращает наносекунды, но последовательные вызовы этого метода вряд ли дадут точность больше микросекунд. На большинстве систем данный метод будет возвращать неубывающие значения, для чего ему может потребоваться внутренняя синхронизация, если он будет вызываться на разных процессорах. Поэтому производительность этого метода очень сильно зависит от железа, и на некоторых машинах запрос к этому методу может легко занимать больше времени, чем запрос к System.currentTimeMillis() [2].
Учитывая, относительность времени, возвращаемого данным методом, его невозможно использовать, скажем, для измерения времени передачи сообщения от одного бокса к другому. Хотя конечно можно измерить время полного round-trip, вычесть время проведенное на второй машине и поделить на два. Однако если у вас распределенное приложение и вам очень важно мереть время затраченное на определенных операциях, которые распределены по разным боксам, то вы можете написать нативный метод, который будет возвращать абсолютное время с большей точность. Я видел такой подход в одном из проектов, с которыми мне приходилось интегрироваться.
Thread.sleep() / Object#wait()
С помощью данных методов можно попросить текущий поток уснуть на определенное количество миллисекунд. Точность просыпания будет зависеть от размера интервала прерываний на вашей ОС. На Windows это обычно 10 мс (но на некотором железе может быть и 15 мс [4]). Однако длина этого интервала может быть изменена даже стандартными средствами java. Данное переключение происходит автоматически, если вы просите заснуть любой поток на время не кратное, текущему интервалу прерываний. Причем, когда данный поток проснется, ОС вернется обратно к штатному режиму.
Как замерить время выполнения
Зачастую требуется узнать, сколько времени выполняется тот или иной код. Иногда требуется замерить время выполнения метода или какой-то задачи. В данной статье мы расскажем вам принципы замера времени в Java и покажем лучшие практики для конкретных задач.
Замер времени с помощью currentTimeMills()
Это довольно простой способ измерить время. Метод System.currentTimeMillis() вернёт вам текущее время в миллисекундах. Его потребуется вызвать до выполнения нужной задачи и после, а затем вычислить разницу. В итоге мы узнаем время выполнения в миллисекундах:
При работе с данным методом следует учитывать его специфику: System.currentTimeMillis() показывает текущее время, основываясь на системных часах и может выдавать некорректный результат.
Замер времени с помощью nanoTime()
Ещё один метод для получения текущего времени это System.nanoTime(). Как следует из названия, этот метод возвращает время с точностью до нансекунд. Также работа этого метода не зависит от системных часов.
Он используется аналогично:
Для получения значения в миллисекундах результат можно разделить на 1000:
Важно: Хотя метод nanoTime() может возвращать время в наносекундах, он не гарантирует, что значения между его вызовами будут обновляться с точностью до наносекунд.
Но всё же это более приемлемый вариант, чем System.currentTimeMillis().
Замер времени с помощью Instant и Duration
В Java 8 добавили новый java.time API. В частности, ля измерения времени подойдут два новых класса – Instant и Duration. Оба эти класса иммутабельны.
Instant обозначает момент времени с начала эпохи Unix (1970-01-01T00:00:00Z). Для создания момента мы используем метод Instant.now(). После того, как мы создали два момент, вычислим разницу в миллисекундах:
Рекомендуется использовать именно этот подход в Java 8 и выше.
Замер времени выполнения с помощью StopWatch
StopWatch – это класс из библиотеки Apache Commons Lang. Он работает как секундомер. Для его использования сначала требуется подключить библиотеку к проекту:
Теперь создадим экземпляр StopWatch. Затем начнём отсчёт с помощью метода start() и окончим отсчёт с помощью метода stop():
StopWatch удобно использовать тогда, когда в проекте уже подключена данная библиотека.
Исходный код
Заключение
В данной статье мы разобрали простые методы замера времени выполнения в Java. Для простых замеров можно использовать все вышеперечисленные методы, кроме currentTimeMillis (из-за того, что он зависит от системных часов).
System currenttimemillis java что возвращает
That will print a human readable time in your local timezone.
To understand exactly what System.currentTimeMills() returns we must first understand: what UTC is, what UNIX timestamps are and how they form a universal time keeping standard.
A human readable moment in time | 2015-03-07 16:00 UTC | |
The same moment as a UNIX timestamp in milliseconds | 1425744000000 | get it with System.currentTimeMillis() in Java get it with new Date().getTime() in Javascript get it with round(microtime(true) * 1000) in PHP |
Note that System.currentTimeMillis() is based on the time of the system / machine it’s running on. In conclusion, the UNIX timestamp is just a system / format for representing a moment in time. The ‘snapshot’ above represents the same moment from 4 different perspectives: John’s timezone, Juliette’s timezone, UTC (Coordinated Universal Time) and milliseconds from the Unix epoch. Java’s Calendar (and deprecated Date ) itself stores time as milliseconds since the Unix Epoch. This is great for many reasons. It happens not only in Java but in other programming languages as well. In my experience i found the perfect time-keeping architecture emerges naturally from this: the Unix Epoch is January 1st, 1970, midnight, UTC. Therefore if you choose to store time as milliseconds since the Unix Epoch you have a lot of benefits: What might happen above is that the sorting for such a small array could be so fast that the duration shows up as zero. For this you can use the trick of repeating the method call multiple times so that you get more relevant durations & comparisons: The easiest way to manually deduce when the method started would be to convert 2015-03-15 10:13:32:564 into milliseconds, subtract 3367, then convert back to a time. The starting point of the activity is relevant because other activities that ran in parallel can be investigated. It’s important to note though that a line such as the one above is normally indicative of poor logging because if the starting point is relevant it can be calculated programmatically (and printed) at the moment of logging. System.currentTimeMillis() offers precision to the millisecond but its accuracy still depends on the underlying machine. This is why, in some cases, it might happen that two subsequent calls can return the same number even if they are in fact more than 1ms apart. The same is true about nanoTime() : precision is down to the nanosecond but accuracy doesn’t follow, it still depends on the machine. UTC stands for Coordinated Universal Time. GMT stands for Greenwich Mean Time. UTC is a universal time keeping standard by itself and a GMT successor. A time expressed in UTC is essentially the time on the whole planet. A time expressed in GMT is the time in the timezone of the Greenwich meridian. In current computer science problems (and probably most scientific ones) UTC and GMT expressed in absolute value happen to have identical values so they have been used interchangeably. A detailed analysis reveals that literature and history here are a bit ambiguous. UTC essentially appeared in 1960, GMT being the ‘main thing’ until then. Unlike GMT which is based on solar time and originally calculated a second as a fraction of the time it takes for the Earth to make a full rotation around its axis, UTC calculates a second as “the duration of 9192631770 periods of the radiation corresponding to the transition between the two hyperfine levels of the ground state of the cesium 133 atom”. UTC’s second is far more precise than GMT’s original second. In 1972 leap seconds were introduced to synchronize UTC time with solar time. These 2 turning points (different definition of a second and the introduction of leap seconds) ‘forced’ GMT to be the same as UTC based on what seemed a gradual, tacit convention. If you were to calculate true GMT today i would see it based on its original definition of 1 second = 1/86400 days and this would for sure return a different absolute value than what UTC gives us. From this point of view the name “GMT” seems deprecated, but kept around for backward compatibility, traditional timezone based representation of time and sometimes legal reasons. Experiments in program optimisationThis method reports current time with the millisecond accuracy. One may think that, because of this, the performance of this method is irrelevant. Who cares if obtaining current time takes 0.1 or 0.2 ms if the measured interval is 100 or 500 milliseconds long? There are, however, cases when we might still want to invoke this method frequently. Here are the examples: Detecting and reporting abnormally long execution times. For instance, we can measure the time it takes to execute an HTTP request. In most cases (we hope) it takes below one milliseconds, which will report as zero if we use this method, but we want to be alarmed if the measured time is abnormally long (e.g. exceeds 100 ms). In this case we’ll time every request, and there may be hundreds of thousands, or even millions, of them per second. Obtaining timestamps to be associated with some objects, for instance, with cached data – to arrange time-based eviction from a cache. Measuring the duration of some long, but frequently initiated asynchronous processes, such as requests to remote servers. Time-stamping some real-world events. For instance, a trading system may wish to record timestamps of incoming orders and performed deals. In short, despite rather low accuracy of this method, there are cases when it can be called very often, so a very valid question arises: what is the performance of this method? Measuring speedThe way to test the performance of currentTimeMillis() is straightforward: we call it many times, while making sure that it isn’t optimised out altogether: Running it on Windows (my notebook, which is Windows 10, Core i7-6820HQ @2.7GHz), using Java 1.8.0_92, we get: This is very good result. When time is reported in 3.8 ns, we can put time request instructions just about anywhere. We can measure time of any operation and attach timestamp to any resource. It is virtually free. We can query time 260 million times per second. Let’s run it on Linux. The one I use for testing is of RHEL flavour with a kernel version of 3.17.4, and it runs on a dual Xeon® CPU E5-2620 v3 @ 2.40GHz. We had to reduce the iteration count by a couple of zeroes (make it one million), because the test runs two hundred times longer. The average time to query time in Linux is 640 ns, which is more than half a microsecond. We can only execute this call 1.5 million times per second. This is really shocking, and it means for us that we must be careful with our use of currentTimeMillis() on Linux. While still applicable for measuring time of sequential long operations, this method can’t really be used for the tasks listed above. Why is this and what can be done? Windows versionThe Windows version of this code ( hotspot/src/os/windows/vm/jvm.cpp/os_windows.cpp ) looks like this: In MSVC, we can call this function and trace its execution in a debugger. The disassembly in the 32-bit mode shows its code: This is indeed very clever. The function contains no system calls. Everything happens in the user space. There is a memory area that is mapped into the address space of every process. Some background thread regularly updates three double-words there, which contain the high part of the value, the low part, and the high part again. The client reads all three, in this order, and, if the two high parts are equal, the low part is considered consistent with them (the strong memory access ordering of x86 guarantees this). If not, the procedure must be repeated. This is very unlikely event, that’s why the entire procedure is so fast. The function looks even better in the 64-bit mode: Here the background thread can write the value atomically, and the client can atomically read it. Actually, the code could have been even better – we could write a 64-bit value in one instruction: However, the code is much faster than any possible system call anyway. What is the resolution of this timer? Let’s collect the values into an array and print it later: Linux versionIt is very unlikely that the time is spent in multiplication or division by 1000, but let’s measure the performance of gettimeofday anyway: but this time the jump takes us to the real implementation. Here it is, with some added comments: The vDSO is mapped to a new address each time (probably, some security feature). However, the function is always placed at the same address as above ( 0x00007ffff7ffae50 ) when run under gdb (I’m not sure what causes this). The function accesses some memory at addresses not far from its code (such as 0x7ffff7ff8080 or 0x7ffff7ff90f0 ), always using the “relative-to-IP” addressing mode. This makes this function completely relocatable – it can be mapped to any address in the user space, together with all its data. Let’s look at the source code of our version of Linux kernel (3.17.4). Note that this is not the latest one (Linux distributors are very conservative people). The code of the latest one differs quite a bit (including some constant definitions). Here is the code (in arch/x86/vdso/vclock_gettime.c ): Some background thread updates the fields of this structure at regular intervals. Since there are more than two of these fields, the Windows trick of writing the high part of the number twice doesn’t work. However, a similar trick works. The writer maintains a version number of the data in the structure (the seq field). It gets incremented by one when the writer starts updating the structure, and again by one after it finishes (with appropriate write barrier instruction being used). As a result, the odd value means that the data isn’t consistent. The reader must read the number, make sure it’s even (wait a bit using pause instruction if not), read all the values of interest from the structure, read the version number again, and if it is the same as in the beginning, the data is considered correct. This is what gtod_read_begin and gtod_read_retry functions are for. Read barrier instructions must be used to make sure that the processor didn’t re-order reading the version number with reading of the actual data. However, the strong memory ordering of Intel x86 makes this unnecessary, so the read barrier call ( smp_rnb() ) is empty in our case. What is the real resolution of these time values? Let’s read these values in a loop and print them when they change. Now we can run a loop to detect change in wall_time_snsec : A typical fragment of the output: The new nanosecond value doesn’t start at zero; it starts at 652692, which is over half a millisecond. It’s not always that big – I saw values of 200K and 300K. In short, two variables available in the gtod structure provide very fast access to a rather coarse time value with a one millisecond resolution. Just like in Windows. However, gettimeofday() does not stop here. It tries to improve accuracy using other methods. In the code above this is what the vgetsns() function is responsible for: The idea is that somewhere in the system there is a high-frequency timer, which we can ask for a current tick count. We record the reading of that timer at the last tick of the coarse timer ( gtod->cycle_last ), and get the difference. The gtod->mask and gtod->mult fields help convert the reading of that timer to our 30-attosecond units. This can explain the choice of those units: it’s not that Linux designers wanted to measure times of molecular processes; the unit is chosen very small to reduce the errors during this conversion. In any case, the code only makes use of the high-precision timer to get the offset from the coarse time. The timer doesn’t have to run very stable for years. It is only supposed to be stable enough to provide the time offset between the coarse timer ticks. Moreover, its clock frequency may vary, as long as that variance is known to the system and reflected in appropriate update of the mask and mult fields. The system is very well designed to provide very accurate time very fast. So what went wrong? In our case, the vclock_mode field is set to 2, which means using the HPET (High Precision Event Timer). This timer is a hardware piece included into all modern PCs: a replacement of the good old 8253/8254 chip. That chip ran a counter, which, upon reaching zero, could trigger an interrupt, and could also be read directly using IN instructions. It ran at the frequency of 1.19318 MHz, and, if programmed to expire every 65536 clocks, caused an interrupt every 55 ms (18.2 Hz), which we all remember since the MS DOS days. The HPET runs at higher frequency, counts up rather than down, and is available via memory-mapped I/O rather than I/O ports. The routine to read the counter looks like this: This is compiled into just one 32-bit memory read instruction: The entire gettimeofday in the case of a HPET time source is compiled into a sequence of memory reads plus some shifts and multiplications. No divisions or other expensive operations are present. Why is it then executing so slowly? I didn’t have a sampling profiler available on the test machine, but there is a poor man’s solution: to attach gdb to a running program that executes gettimeofday in a loop, and then interrupt it. In all the cases that I tried it it always stopped at exactly one place: after the above-mentioned instruction (at gettimeofdat+550 ). This indicates that that instructions alone takes most of the execution time. This is easy to verify. Let’s resolve the hpet_page in a similar way to resolving vsyscall_gtod_data and read this variable in a loop: This is really sad: the timer runs at high frequency, but there is no way to read it at this frequency. We can read it at about 1.8 MHz, which is just a bit higher than the frequency of the MS DOS timer. The coarse timerThere is an easy way out, because the coarse timer is available via the user API: the clock_gettime() function: Among multiple values defined in for clk_id there is one that does exactly what we need: CLOCK_REALTIME_COARSE (value 5). The code for this function is present in the same vDSO ( vclock_gettime.c ): The nanosecond times reported by this function are 999991 ns (1 ms) apart, and the execution takes 8.4 ns. The TSC time sourceWe’ve learned that the slow execution of currentTimeMillis() was caused by two factors: HPET, however, isn’t the only time source these days. Perhaps, not even the most common: many systems use TSC. This could make the entire study irrelevant. However, in our project the servers are configured with the HPET time source for a reason: this time source integrated well with the NTP client, allowing for smooth time adjustment, while TSC was not as stable (I don’t know the details; this is what the local Linux gurus say and I have no option but to trust them). Some other developers may find themselves in the same situation. Besides, a Java developer can’t always know what time source will be configured on the machine the program will run. That’s why I feel that the findings made so far are still important. However, it is still interesting to find out how the result would change if we used the TSC time source. TSC stands for the time stamp counter, which is simply the number of CPU cycles counted since the startup time (it is only 64-bit wide, so it will wrap around in 243 years at 2.4GHz clock rate). This value can be read using rdtsc instruction. Traditionally, there were two problems with this value: The first one seems to be indeed a problem. I tried getting the rdtsc value from several cores at once, synchronised on writing into some memory location. Even in the best case I got differences of a couple of thousand cycles. Sometimes more. However, this is only a problem if the programmer wants to use the TSC manually; in this case the thread affinity must be set accordingly. The OS knows when it re-schedules threads from one core to another, so it can make all necessary adjustments. The second problem seems to be a thing of the past. The Intel doc says this:
For Pentium M processors (family [06H], models [09H, 0DH]); for Pentium 4 processors, Intel Xeon processors (family [0FH], models [00H, 01H, or 02H]); and for P6 family processors: the time-stamp counter increments with every internal processor clock cycle. The internal processor clock cycle is determined by the current core-clock to busclock ratio. Intel® SpeedStep® technology transitions may also impact the processor clock. For Pentium 4 processors, Intel Xeon processors (family [0FH], models [03H and higher]); for Intel Core Solo and Intel Core Duo processors (family [06H], model [0EH]); for the Intel Xeon processor 5100 series and Intel Core 2 Duo processors (family [06H], model [0FH]); for Intel Core 2 and Intel Xeon processors (family [06H], DisplayModel [17H]); for Intel Atom processors (family [06H], DisplayModel [1CH]): the time-stamp counter increments at a constant rate.
|