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);
    ... 
    ...
}

Результаты меня озадачили. На самом деле я заметил две вещи:

  1. Чем больше SIMULATION_DURATION, тем больше максимум (продолжительность);
  2. Значение max(duration) может достигать 2 с (да секунд, без опечаток), когда SIMULATION_DURATION = 10 с. Если SIMULATION_DURATION = 100 мс, я наблюдаю макс (продолжительность) около 30 мс.

Мои вопросы следующие:

  1. Как может копировать массив из 9 целых чисел так долго?
  2. Почему длительность составляет менее 0,1 мс 99% времени и действительно высока оставшиеся 1%?
  3. Почему это зависит от значения SIMULATION_DURATION?
  4. Я делаю ошибку, используя System.currentTimeMillis() для этого типа теста, и поэтому результаты совершенно неточны?
  5. 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 это примитивный тип.

Другие вопросы по тегам