JAVA - Копирование массива в конструкторе неожиданно медленно после большого количества вызовов
В настоящее время я пытаюсь улучшить производительность кода Java. Немного покопавшись, чтобы увидеть, где требуется оптимизация, я получил следующую настройку (упрощено для ясности).
Конструктор Board, который вызывается большое количество раз (~200k до 2M):
public Board(Board board) {
long now = System.currentTimeMillis();
this.macroBoard = new int[9];
int [] boardToCopy = board.getMacroBoard();
for (int i = 0; i < 9; i++){
this.macroBoard[i] = boardToCopy[i];
}
long duration = System.currentTimeMillis() - now;
if (duration > THRESHOLD){
System.err.println(duration);
}
}
И в другом классе:
long end = System.currentTimeMillis() + SIMULATION_DURATION;
while (System.currentTimeMillis() < end) {
...
...
Board board = new Board(otherBoard);
...
...
}
Результаты меня озадачили. На самом деле я заметил две вещи:
- Чем больше SIMULATION_DURATION, тем больше максимум (продолжительность);
- Значение max(duration) может достигать 2 с (да секунд, без опечаток), когда SIMULATION_DURATION = 10 с. Если SIMULATION_DURATION = 100 мс, я наблюдаю макс (продолжительность) около 30 мс.
Мои вопросы следующие:
- Как может копировать массив из 9 целых чисел так долго?
- Почему длительность составляет менее 0,1 мс 99% времени и действительно высока оставшиеся 1%?
- Почему это зависит от значения SIMULATION_DURATION?
- Я делаю ошибку, используя System.currentTimeMillis() для этого типа теста, и поэтому результаты совершенно неточны?
- GC вовлечен в это странное поведение, когда я создаю большое количество объектов Board?
2 ответа
Похоже, что вашей виртуальной машине не хватает памяти и она пытается собрать GC, чтобы она могла выделить память для новых массивов. Вы можете найти информацию по этой ссылке, чтобы включить ведение журнала GC и получить более подробную информацию о поведении GCing нашей виртуальной машины: https://dzone.com/articles/enabling-and-analysing-the-garbage-collection-log
Также я рекомендую использовать System.nanoTime()
измерить производительность. Для получения дополнительной информации: System.currentTimeMillis против System.nanoTime
Чтобы ответить на вопросы напрямую:
Как может копировать массив из 9 целых чисел так долго?
Это, безусловно, не должно. Проверьте журналы GC, чтобы убедиться, что GC замедляет работу виртуальной машины.
Почему длительность составляет менее 0,1 мс 99% времени и действительно высока оставшиеся 1%?
В течение 99% времени у вас не хватает памяти, и поэтому нет проблем с выделением места для нового Board
объекты.
Почему это зависит от значения SIMULATION_DURATION?
Значение SIMULATION_DURATION
непосредственно контролирует количество Board
объекты.
Я делаю ошибку, используя System.currentTimeMillis() для этого типа теста, и поэтому результаты совершенно неточны?
Проверьте ссылку на другой вопрос переполнения стека выше.
GC вовлечен в это странное поведение, когда я создаю большое количество объектов Board?
Проверьте ответ выше.
Ответ пранавмалхотры более ценен, чем мой, но есть улучшение, которое, безусловно, должно быть сделано.
За
this.macroBoard = new int[9];
int[] boardToCopy = board.getMacroBoard();
for (int i = 0; i < 9; i++){
this.macroBoard[i] = boardToCopy[i];
}
первая оптимизация будет
this.macroBoard = new int[9];
int[] boardToCopy = board.getMacroBoard();
System.arraycopy(boardToCopy, 0, macroBoard, 0, 9);
Или даже:
int[] boardToCopy = board.getMacroBoard();
this.macroBoard = Arrays.copyOf(boardToCopy, 9);
Оптимизация может принимать разные формы. Если целые числа на плате имеют диапазон 0... 127, можно поместить каждые 7 битов int в long, так как 7 * 9 = 63 < 64 бит long. long
это примитивный тип.