Как запустить Java-программу с точно контролируемым временем выполнения?
Как я знаю JVM перед запуском приложения Java, он выделяет для него часть ОЗУ, и эта память может управляться пользователем перед запуском. Но есть еще одна проблема, когда я запускаю приложение, оно имеет разное время выполнения каждый раз.
вот очень простой пример с for loop:
package timespent;
public class Main {
public static void main(String[] args) {
long startTime = System.nanoTime();
int j = 0;
for (int i = 0; i < 1.E6; i++) {
j = i + 1;
}
long endTime = System.nanoTime();
long duration = (endTime - startTime);
System.out.println("duration = " + duration);
}
}
он печатает различные результаты:
duration = 5720542
duration = 7331307
duration = 7737946
duration = 6899173
предположим, я хочу, чтобы он был выполнен точно 10,000,000 наносекунд или 10 миллисекунды.
чего я хочу?
Я хочу, чтобы приложение java выполнялось с точным выполнением времени.
зачем мне это надо?
когда я запускаю приложение, я хочу показать точное время выполнения, оставшееся в окне запуска приложения, прежде чем он загрузит все компоненты.
Я полагаю, что это своего рода манипуляция процессором, и я хотел бы знать, возможно ли это или нет.
Вопрос 1: возможно ли это в Ява?
Вопрос 2: если это невозможно в Java, то есть ли способ достичь этого путем доступа к собственным методам ОС. например, путем приоритизации приложения Java или чего-то еще?
Вопрос 3: как насчет сохранения состояния приложения в файл и загрузка его в память?
7 ответов
существует ряд источников неопределенности в вашем измерении времени. И все эти источники не просто влияют на ваши измерения, это сама среда выполнения, которая является неопределенной. Среди источников неопределенности:
использование кэша (какие части памяти кэшируются в ЦП). Ваши данные могут быть вытеснены из кэша процессором, выполняющим фоновую задачу.
размещение памяти (память напрямую подключена к выполнение ядра процессора?). Это может измениться со временем, так как ваш процесс может быть перенесен в другое ядро в любое время.
программное обеспечение прерывает (ваша ОС опережает ваш процесс для запуска другого). Можно несколько смягчить, запустив тихую машину, но нет никакой гарантии, что вас не прервут.
тепловое дросселирование (ваш процессор решает, что он слишком горячий и отключает свою тактовую частоту). Ты ничего не можешь поделать. это если вы не готовы работать на каком-то встроенном процессоре с фиксированной тактовой частотой.
аппаратные прерывания (ваш сетевой разъем получил некоторые данные с другого компьютера в интернете). Вы не имеете никакого влияния на то, когда это происходит.
непредсказуемые задержки (Вы читаете некоторые данные с диска, но сначала диск должен ждать, пока данные не прибудут ниже головки чтения). Это может следовать шаблонам, когда вы повторяя одни и те же действия снова и снова, но как только вы получаете несвязанное аппаратное прерывание, это может вызвать неожиданную задержку
1/7200 rpm * 60 s/min = 8.3 ms
.сборка мусора (вы спрашиваете о Java, поэтому у вас есть GC, работающий в фоновом режиме). Даже самые лучшие, самые современные мусорщики не могут полностью избежать остановки мира время от времени. И даже когда они не останавливают мир, они все еще работают в фоновом режиме, вводя Шум времени выполнения через кеш, размещение памяти и программные прерывания.
Это, вероятно, самые важные источники, и могут быть другие. Дело в том, что процесс никогда только на машине. Если вы не работаете без ОС и не отключаете все аппаратные прерывания, вам просто нужно смириться с тем, что время выполнения будет отличаться от выполнения к выполнению, и просто нет способа исправить это.
Это просто невозможно. Прежде всего, измерение времени в наносекундах не является точным. Я чувствую, что этот пост объясняет, что хорошо.
во-вторых, у вас нет контроля над тем, как CPU планирует выполнение. Могут быть и другие задачи, занимающие время процессора, что задерживает выполнение вашей программы.
точное время выполнения произвольного кода недетерминировано, потому что зависит от других вещей, которые физическая машина одновременно делает.
даже если вы планировали сделать время выполнения "постоянным", отслеживая временную метку запуска и запланированную конечную временную метку и спящий основной поток на протяжении всего времени между выходом из программы, он все равно будет варьироваться, достаточно большое количество.
, когда, и как долго, потоки выполняются или ждать вне контроля программиста.
[TL; DR] это очень сложно/невозможно.
более длинный ответ см. тестирование Планарности путем добавления пути кандидатская диссертация-методология бенчмаркинга глава для некоторых из вопросов, включая:
- другие приложения, имеющие ресурсы. Т. е. недостаточно памяти, и операционная система должна создавать страницы приложений;или код работает медленнее, поскольку другое приложение использует CPU.
- разрешение часов - ваш код будет работать только как быстро, как ваш процессор способен. Перенесите его на другой компьютер, и ваши тесты могут дать вам совершенно разные результаты, поэтому не оптимизируйте его только для вашей системы.
- загрузка класса-когда JVM сначала запускает код, он должен загрузить классы в память (обычно с диска) и проанализировать байтовый код, поэтому при первом запуске он будет значительно медленнее, чем в последующие разы.
- Just-In-Time compilation-когда JVM сначала загружает байтовый код, он будет запускать его в чисто интерпретируемый режим. Как только он запустил блок байтового кода (т. е. одну функцию в вашем коде) несколько раз (скажем, 10 000), он может скомпилировать эту функцию в собственный код. Компиляция замедлит это выполнение, но затем последующие вызовы будут быстрее, поскольку он выполняет собственный, а не интерпретируемый код. Однако это не одноразовая компиляция, и если JVM обнаруживает, что определенные пути выполнения в блоке предпочтительны, то он может перекомпилировать байтовый код, чтобы попытаться оптимизируйте эти пути, и это может привести к нескольким собственным версиям байтового кода, пока JVM не стабилизирует код по своей статистике.
- сборка мусора-иногда ваш код будет прерван, в то время как Java вызывает сборщик мусора.
поэтому, если вы хотите проверить свое приложение, чтобы увидеть, как оно будет работать оптимально, тогда:
- остановите столько других приложений, сколько сможете;
- запустите код многие десятки тысяч раз;
- игнорировать первые 10,000-20,000 исполнений (для смягчения загрузки классов и компиляции JIT); и
- игнорировать итерации при сборке мусора (это сложнее определить, чем кажется).
Это даст вам представление о оптимальной производительности, но оптимальный и реальный мир это две разные вещи.
единственный способ приблизиться к этому-использовать Java в реальном времени в операционной системе, специально разработанной для поддержки выполнения в реальном времени.
как уже сказали, невозможно знать точное время, оставшееся из-за других факторов, влияющих на скорость вашей программы. Однако вы можете поместить вехи и ссылаться на прошлые прогоны, чтобы получить полуточное время, рассчитанное из разницы в фактическом времени, пока этот прогон по сравнению с предыдущими прогонами, как это делается при копировании больших каталогов файлов в Windows, например, или при загрузке больших файлов в Chrome.
таким образом, вы не указываете свою точную проблему, но предположим, это что-то вроде обработки 100 000 операций, которые требуют обращения к сторонней системе в интернете, и обычно это занимает ~15 минут. Вы можете отслеживать 1) Время начала, 2) ожидаемое время окончания и 3) часть завершена. Итак, когда вы закончите 1/2, вы можете взять истекшее время и сказать, что это то, сколько осталось. В основном получите скорость в операциях в секунду и разделите оставшиеся операции на это, чтобы получить количество секунд оставшийся.
double speed = completedOperations / elapsedSeconds;
double remainingSeconds = remainingOperations / speed;
Это может измениться. Скажем, вы начинаете процесс и после получения 1/4 пути через 5 минут, что запускаются резервные копии вне сайта, не только обмолот дисков компьютера, но подключение к интернету. Теперь все обрабатывается на 1/10 скорости. Ваше предполагаемое время завершения будет начинаться с 20 минут, а затем в 5 минут в нем будет 15 минут. Однако он замедляется в этот момент, поэтому вы делаете только 1/2 после 30 минут, а оставшееся время точка будет 30 минут. Теперь скажите, что резервные копии завершены, вы фактически сделаете это за 10 минут, но он говорит, что осталось 30 минут.
нет никакого способа обойти такую проблему, которая находится вне вашего контроля. Вы могли бы сделать пару вещей, чтобы смягчить его. Возможно, вы захотите взять скорость за последние 30 секунд обработки. Это будет Наиболее точным, если все будет продолжаться с текущей скоростью. Вы можете записать среднюю скорость в любое время дня исторически если это проблема. Вы можете усреднить общую скорость бега и скорость в последнюю минуту, если скорость колеблется.
еще одна вещь, которая может выбросить его, - это дисперсия в данных. Например, если вы смотрите на клиентов и обрабатываете их на основе даты, когда они стали клиентами, ваши первые 10 000 операций могут быть на постоянных клиентах, которые были с вами в течение многих лет и имеют тонны данных для обработки, в то время как последние 10 000 могут быть новыми клиентами с небольшим количеством данных, которые обрабатывают быстрее. Затем вы можете использовать базовый объем данных вместо подсчета клиентов...
однако, если вы хотите быть точным по какой-то причине (большую часть времени), вы можете подделать его за счет времени. Взять наибольшее нормальное время выполнения и время, затраченное с начала, чтобы обеспечить прогресс и оставшееся время. Затем, когда вся фактическая работа будет выполнена, поместите в sleep()
команда, чтобы переждать оставшееся время. Всегда будет шанс, что система загрузится или что-то заставляет его занять исключительно много времени, но вы можете изменить свое максимальное время на это новое значение.
времена ваших пробегов, вероятно, на какой-то кривой, чем дольше вы делаете время, тем более вероятно, что любой одиночный пробег завершится за это время, но тогда больше пробегов будут ждать ничего:
# ^
# |
### |
### |
##### R
####### U
########### N
################### S
Time-------------->
конечно, это кажется глупым, но ваше приложение будет работать на разных скоростях из-за переменных, которые вы не можете контролировать, и если рядом постоянное время выполнения важно для вас это единственный способ.
Это не будет работать свой путь. Это зависит от состояния системы, например, от того, сколько системных ресурсов работает в вашей программе.
Как я вижу, вы хотите отобразить время, оставшееся до открытия приложения. В этом случае предположим, что два пользователя запускают вашу программу на разных машинах с разной структурой ядра и тактовой частотой ...
но я могу дать предложение, вы можете просто прочитать данные вашей программы и настроить время на этой основе, например другое приложение, которое показывает ..% loaded или такая же, как функция диспетчера загрузки, которая показывает ..% загружаемый.