2013-04-23 3 views
4

Скажем, я создаю массив, предназначенный для эмуляции памяти процессора:Быстрее ли падение и воссоздание массива, или заполнение его нулями, и почему?

byte[] mem = new byte[0xF00]; 

Этот массив используется в течение операции эмуляции, и в конечном итоге (читай: с частотой) должны быть отброшены или сброса. Мой вопрос, который быстрее, и почему?

mem = new byte[0xF00]; 

или:

for(int i = 0; i < mem.length; i++) mem[i] = 0; 

Это может показаться не значительное, но при эмуляции огромного количества процессоров, небольшая эффективность делает разницу. Разница в скорости будет получена от мусорной коллекции JVM; в одном массив должен быть сброшен и собран мусор, однако JVM больше не должен выделять (и, возможно, нулевую?) новую память. Во втором случае стоимость JVM исключается, но нам все равно придется перебирать каждый элемент массива.

В качестве дополнительных предостережения на этот вопрос:

  1. ли затраты изменения соотношения с размером типа данных? Например, как насчет short[]?
  2. Является ли длина массива влияющим на соотношение затрат?
  3. Самое главное, Почему?
+0

IMO скажет, что вы должны позволить GC выполнять свою работу, поэтому лучше создать новый массив байтов. Но если вы действительно хотите убедиться, создайте теплый код для JIT, а затем протестируйте свои теории на разных ОС, например Windows и Linux, с помощью той же машины JVM, то есть HotSpot, используйте профилировщик для измерения результатов (или что-то еще) и получить ваши результаты и свои собственные выводы. –

+0

Я предлагаю вам создать небольшой фрагмент, который проверяет это, и измеряет время для обоих. –

+1

Другим вариантом будет использование 'Arrays.fill', также как FYI. –

ответ

4

Перераспределение массива на самом деле не увеличивает стоимость каждого GC, поскольку GC только посещает и копирует живые объекты и ничего не делает с мертвыми объектами. Однако выделение объектов приводит к более частым появлению небольших GC. Но, если ни один из недавно выделенных объектов все еще жив, стоимость младшего GC очень низкая, и основные GC не будут вызваны вообще.

Кроме того, размещение объектов в современных версиях Java является дешевым, и обнуление пространства распределения может быть легко принято считать наиболее эффективным обнулением, которое может достичь JVM. Если вам удается обнулить массив в вашем коде так же быстро, как это делает JVM (Редактировать: Как уже упоминалось, Стивен Шланскер, компилятор JIT может оптимизировать циклы заполнения памяти), повторное использование массива должно быть быстрее. Во всяком случае, пока проиллюстрированный вами цикл while оптимизирован JIT-компилятором, я предполагаю, что он будет значительно медленнее.

Чтобы ответить на другие вопросы:

  • ГЦ нули распределение пространства (Eden) сразу, так что не будет разница ли сво short[] или byte[].Тем не менее, для цикла for будет требоваться только половина количества итераций до нуля того же количества байтов при использовании short[] вместо byte[] (установка байта или короткого замыкания на 0 не должна иметь никакого значения)
  • Более длинный массив получает, тем больше итераций потребуется для вашего цикла. Таким образом, этот рост является линейным. GC также потребуется амортизированное линейное время до нулевого диапазона байтов, поэтому я полагаю, что соотношение между обоими подходами остается постоянным. Тем не менее, могут существовать более эффективные способы обнуления больших областей памяти, чем небольшие, что сделало бы обнуление GC (всего выделенного пространства одновременно) более эффективным, чем подход цикла. Для очень больших массивов ситуация может измениться: они распределяются непосредственно в поколение Tenured (если не используется G1), и поэтому вызовут большие GC, которые намного дороже.
+2

Стоит отметить, что (по крайней мере, HotSpot) JIT имеет особые знания о циклах заполнения памяти. Поэтому вы не должны предполагать, что цикл Java медленный. Всегда измеряйте! –

1

Я бы определенно пошел за mem = new byte[0xF00]; и позволил GC сделать все остальное.

Использование памяти может быть немного больше, но это не повлияет на ваше приложение, если вы не делаете это тысячи раз в секунду.

Время выполнения будет намного быстрее, и нет необходимости вручную вызывать GC, он все равно выполнит свою работу.

5

Вы можете проверить это самостоятельно, но сброс и повторное создание массива примерно одинаковы.

Однако, у него есть два минусы

  • это вызывает кэш данных процессора для прокрутки, снижая его эффективность.
  • это делает вызывая GC более вероятно, особенно если вы делаете это часто, что делает паузу системы, или замедляет его (если он одновременно)

Я предпочитаю повторно использовать массив, а не потому, что это самый быстрый , но это оказывает наименьшее влияние на остальную часть вашего приложения.


for (int size = 16; size <= 16* 1024; size *= 2) { 
    int count1 = 0, count1b = 0,count2 = 0; 
    long total1 = 0, total1b = 0, total2 = 0; 
    for (long i = 0; i < 10000000000L; i += size) { 
     long start = System.nanoTime(); 
     long[] longs = new long[size]; 
     if (longs[0] + longs[longs.length - 1] != 0) 
      throw new AssertionError(); 
     long mid = System.nanoTime(); 
     long time1 = mid - start; 
     Arrays.fill(longs, 1L); 
     long time2 = System.nanoTime() - mid; 
     count1b++; 
     total1b += time1; 
     if (time1 < 10e3) {// no GC 
      total1 += time1; 
      count1++; 
     } 
     if (time2 < 10e3) {// no GC 
      total2 += time2; 
      count2++; 
     } 
    } 
    System.out.printf("%s KB took on average of %,d ns to allocate, %,d ns to allocate including GCs and %,d ns to fill%n", 
      size * 8/1024.0, total1/count1, total1b/count1b, total2/count2); 
} 

печатает

0.125 KB took on average of 35 ns to allocate, 36 ns to allocate including GCs and 19 ns to fill 
0.25 KB took on average of 39 ns to allocate, 40 ns to allocate including GCs and 31 ns to fill 
0.5 KB took on average of 56 ns to allocate, 58 ns to allocate including GCs and 55 ns to fill 
1.0 KB took on average of 75 ns to allocate, 77 ns to allocate including GCs and 117 ns to fill 
2.0 KB took on average of 129 ns to allocate, 134 ns to allocate including GCs and 232 ns to fill 
4.0 KB took on average of 242 ns to allocate, 248 ns to allocate including GCs and 368 ns to fill 
8.0 KB took on average of 479 ns to allocate, 496 ns to allocate including GCs and 644 ns to fill 
16.0 KB took on average of 1,018 ns to allocate, 1,055 ns to allocate including GCs and 1,189 ns to fill 
32.0 KB took on average of 2,119 ns to allocate, 2,200 ns to allocate including GCs and 2,625 ns to fill 
64.0 KB took on average of 4,419 ns to allocate, 4,604 ns to allocate including GCs and 4,728 ns to fill 
128.0 KB took on average of 8,333 ns to allocate, 9,472 ns to allocate including GCs and 8,685 ns to fill 

Доказывая только, что трудно предположить, один подход быстрее, чем другие во всех случаях.

Если я изменю long[] к int[] я вижу так же

0.125 KB took on average of 35 ns to allocate, 36 ns to allocate including GCs and 16 ns to fill 
0.25 KB took on average of 40 ns to allocate, 41 ns to allocate including GCs and 24 ns to fill 
0.5 KB took on average of 58 ns to allocate, 60 ns to allocate including GCs and 40 ns to fill 
1.0 KB took on average of 86 ns to allocate, 87 ns to allocate including GCs and 94 ns to fill 
2.0 KB took on average of 139 ns to allocate, 143 ns to allocate including GCs and 149 ns to fill 
4.0 KB took on average of 256 ns to allocate, 262 ns to allocate including GCs and 206 ns to fill 
8.0 KB took on average of 472 ns to allocate, 481 ns to allocate including GCs and 317 ns to fill 
16.0 KB took on average of 981 ns to allocate, 999 ns to allocate including GCs and 516 ns to fill 
32.0 KB took on average of 2,098 ns to allocate, 2,146 ns to allocate including GCs and 1,458 ns to fill 
64.0 KB took on average of 4,312 ns to allocate, 4,445 ns to allocate including GCs and 4,028 ns to fill 
128.0 KB took on average of 8,497 ns to allocate, 9,072 ns to allocate including GCs and 7,141 ns to fill 
+0

Хотя это полезно, и я благодарю вас за ваш ответ, я должен указать, что это не совсем полный ответ на мой вопрос. – Zyerah

+0

1 + 2) Они не сопоставимы напрямую, и соотношение будет различным на разных машинах. 3) Почему бы и нет? Чем дольше массив, тем больше работы вы должны делать и чем больше вы получаете доступ к памяти, тем дольше это требуется. Примечание: GC завершает новый массив, который выполняется в сеансе GC. Если вы обнулите массив, это произойдет, когда вы это сделаете. –

+0

Я полагаю, что это имеет смысл. Хорошо, спасибо! – Zyerah

3

Я согласен с замечанием, что resuing массив будет иметь наименьшее воздействие на приложения, но ваш конкретный случай, кажется, не влияет на GC много:

for(int i = 0; i < mem.length; i++) mem[i] = 0; 

В выше цикле (mem.length это 61440), было бы 2*61400 задания и 61400 сравнения.

Теперь в случае GC во время этапа выделения выделения или выделения памяти конкретного объекта весь фрагмент памяти будет отменен, и IMO должен быть быстрее, чем статистика из цикла выше.

Но фактическая стоимость GC на производительность приложения возникает, когда поведение кода/приложения вызывает слишком много циклов GC (даже хуже, если его частые крупные циклы). Ваш конкретный случай не показывает, что более высокий явный GC.

Я думаю, что подход цикла в byte[] будет лучше. Если бы это было Object[], мы могли бы иметь другой подход.

0

Здесь важны 4 фактора.

1) Что такое целевая платформа? (У этого есть много ОЗУ? Несколько процессорных ядер?) 2) Каков максимальный объем памяти, который вы планируете выделить? (Большие суммы, скорее всего, будут способствовать распределению/освобождению) 3) Какую JVM вы планируете использовать? 4) Если ваше приложение является критическим для производительности, почему вы его разрабатываете на Java?

К тому же, я бы сказал, «не беспокойтесь о преждевременной оптимизации». Сначала напишите программное обеспечение, затем профилируйте его, а затем оптимизируйте детали, которые выполняются медленно. Как правило, производительность алгоритма, как правило, представляет собой большую проблему, чем производительность структуры данных, особенно когда ваша структура данных в основном представляет собой просто пустое пространство адресации.

Смежные вопросы