Недавно я заметил, что объявление массива, содержащего 64 элемента, намного быстрее (более 1000 раз), чем объявление того же типа массива с 65 элементами.
Вот код, который я использовал для проверки:
public class Tests{
public static void main(String args[]){
double start = System.nanoTime();
int job = 100000000;//100 million
for(int i = 0; i < job; i++){
double[] test = new double[64];
}
double end = System.nanoTime();
System.out.println("Total runtime = " + (end-start)/1000000 + " ms");
}
}
Это работает примерно 6 мс, если я заменю new double[64]
с new double[65]
занимает около 7 секунд. Эта проблема становится экспоненциально более серьезной, если задание распределяется по все большему и большему количеству потоков, откуда и возникла моя проблема.
Эта проблема также возникает с различными типами массивов, такими как int[65]
или String[65]
. Эта проблема не возникает с большими строками:, String test = "many characters";
но начинает возникать, когда это заменяется наString test = i + "";
Мне было интересно, почему это так и можно ли обойти эту проблему.
System.nanoTime()
предпочтительнееSystem.currentTimeMillis()
для сравнительного анализа.byte
вместоdouble
.Ответы:
Вы наблюдаете поведение, вызванное оптимизацией, выполненной JIT-компилятором вашей виртуальной машины Java. Такое поведение воспроизводимо запускается для скалярных массивов до 64 элементов и не запускается для массивов более 64.
Прежде чем вдаваться в подробности, давайте подробнее рассмотрим тело цикла:
double[] test = new double[64];
Тело не имеет никакого эффекта (наблюдаемое поведение) . Это означает, что вне выполнения программы не имеет значения, выполняется этот оператор или нет. То же верно для всего цикла. Так что может случиться так, что оптимизатор кода преобразует цикл во что-то (или во что-то) с той же функциональностью и другим временным поведением.
Для тестов вы должны придерживаться как минимум двух следующих рекомендаций. Если бы вы это сделали, разница была бы значительно меньше.
Теперь перейдем к деталям. Неудивительно, что существует оптимизация, которая запускается для скалярных массивов не более 64 элементов. Оптимизация является частью анализа побега . Он помещает небольшие объекты и небольшие массивы в стек вместо того, чтобы размещать их в куче, или, что еще лучше, полностью их оптимизирует. Вы можете найти некоторую информацию об этом в следующей статье Брайана Гетца, написанной в 2005 году:
Оптимизацию можно отключить с помощью параметра командной строки
-XX:-DoEscapeAnalysis
. Магическое значение 64 для скалярных массивов также можно изменить в командной строке. Если вы выполните свою программу следующим образом, между массивами с 64 и 65 элементами не будет разницы:java -XX:EliminateAllocationArraySizeLimit=65 Tests
Сказав это, я настоятельно не рекомендую использовать такие параметры командной строки. Я сомневаюсь, что это имеет большое значение для реалистичного приложения. Я бы использовал его только в том случае, если бы был абсолютно уверен в необходимости - а не на основе результатов каких-то псевдо-тестов.
источник
Существует множество способов отличия в зависимости от размера объекта.
Как указано в nosid, JITC может (скорее всего) выделять небольшие «локальные» объекты в стеке, а ограничение размера для «маленьких» массивов может составлять 64 элемента.
Выделение в стеке происходит значительно быстрее, чем в куче, и, что более важно, стек не нужно собирать сборщиком мусора, поэтому накладные расходы на сборку мусора значительно сокращаются. (И для этого тестового примера накладные расходы сборщика мусора, вероятно, составляют 80-90% от общего времени выполнения.)
Кроме того, после распределения значения в стеке JITC может выполнить «устранение мертвого кода», определив, что результат
new
никогда нигде не используется, и, убедившись, что нет побочных эффектов, которые могут быть потеряны, полностью устранитьnew
операцию, а затем сам (теперь пустой) цикл.Даже если JITC не выполняет выделение стека, вполне возможно, что объекты меньшего размера, чем определенный размер, будут размещены в куче иначе (например, из другого «пространства»), чем более крупные объекты. (Однако обычно это не привело бы к столь значительным временным различиям.)
источник