В последнее время у меня есть очень странная вещь - один метод был очень медленным под профилировщиком без очевидной причины. Он содержит несколько операций с long
, но вызывается довольно часто - его общее использование составило около 30-40% от общего времени программы, тогда как другие части кажутся гораздо «тяжелее».
Обычно я запускаю не требующие памяти программы на x32 JVM, но, предположив, что у меня проблемы с 64-битным типом, я попытался запустить то же самое на x64 JVM - общая производительность в «живом сценарии» повысилась в 2-3 раза , После этого я создал эталонные тесты JMH для операций из определенного метода и был шокирован разницей в x32 и x64 JVM - до 50 раз.
Я бы «принял» примерно в 2 раза медленнее xVV JVM (меньший размер слова), но у меня нет подсказок, откуда может приходить 30-50 раз. Можете ли вы объяснить эту резкую разницу?
Ответы на комментарии :
- Я переписал тестовый код, чтобы «вернуть что-то» и избежать «удаления мертвого кода» - похоже, он ничего не изменил для «x32», но некоторые методы для «x64» стали значительно медленнее.
- Оба теста были выполнены под «клиентом». Запуск под '-server' не имел заметного эффекта.
Так что, кажется, ответ на мой вопрос
- 'тестовый код' был неправильным: из-за 'отсутствия возвращаемого значения' он позволял JVM выполнять 'удаление мертвого кода' или какую-либо другую оптимизацию, и кажется, что 'x32 JVM' делает меньше таких оптимизаций, чем 'x64 JVM' - что вызвало столь значительную «ложную» разницу между x32 и x64
- разница в «правильном тестовом коде» до 2x-5x раз - это кажется разумным
Вот результаты (Примечание: ? 10??
- это специальные символы, не напечатанные в Windows - это что-то ниже 0,001 с / оп, записанное в научной записи как 10e - ??)
x32 1.8.0_152
Benchmark Mode Score Units Score (with 'return')
IntVsLong.cycleInt avgt 0.035 s/op 0.034 (?x slower vs. x64)
IntVsLong.cycleLong avgt 0.106 s/op 0.099 (3x slower vs. x64)
IntVsLong.divDoubleInt avgt 0.462 s/op 0.459
IntVsLong.divDoubleLong avgt 1.658 s/op 1.724 (2x slower vs. x64)
IntVsLong.divInt avgt 0.335 s/op 0.373
IntVsLong.divLong avgt 1.380 s/op 1.399
IntVsLong.l2i avgt 0.101 s/op 0.197 (3x slower vs. x64)
IntVsLong.mulInt avgt 0.067 s/op 0.068
IntVsLong.mulLong avgt 0.278 s/op 0.337 (5x slower vs. x64)
IntVsLong.subInt avgt 0.067 s/op 0.067 (?x slower vs. x64)
IntVsLong.subLong avgt 0.243 s/op 0.300 (4x slower vs. x64)
x64 1.8.0_152
Benchmark Mode Score Units Score (with 'return')
IntVsLong.cycleInt avgt ? 10?? s/op ? 10??
IntVsLong.cycleLong avgt 0.035 s/op 0.034
IntVsLong.divDoubleInt avgt 0.045 s/op 0.788 (was dead)
IntVsLong.divDoubleLong avgt 0.033 s/op 0.787 (was dead)
IntVsLong.divInt avgt ? 10?? s/op 0.302 (was dead)
IntVsLong.divLong avgt 0.046 s/op 1.098 (was dead)
IntVsLong.l2i avgt 0.037 s/op 0.067
IntVsLong.mulInt avgt ? 10?? s/op 0.052 (was dead)
IntVsLong.mulLong avgt 0.040 s/op 0.067
IntVsLong.subInt avgt ? 10?? s/op ? 10??
IntVsLong.subLong avgt 0.075 s/op 0.082
А вот (фиксированный) код теста
import org.openjdk.jmh.annotations.Benchmark;
public class IntVsLong {
public static int N_REPEAT_I = 100_000_000;
public static long N_REPEAT_L = 100_000_000;
public static int CONST_I = 3;
public static long CONST_L = 3;
public static double CONST_D = 3;
@Benchmark
public void cycleInt() throws InterruptedException {
for( int i = 0; i < N_REPEAT_I; i++ ) {
}
}
@Benchmark
public void cycleLong() throws InterruptedException {
for( long i = 0; i < N_REPEAT_L; i++ ) {
}
}
@Benchmark
public int divInt() throws InterruptedException {
int r = 0;
for( int i = 0; i < N_REPEAT_I; i++ ) {
r += i / CONST_I;
}
return r;
}
@Benchmark
public long divLong() throws InterruptedException {
long r = 0;
for( long i = 0; i < N_REPEAT_L; i++ ) {
r += i / CONST_L;
}
return r;
}
@Benchmark
public double divDoubleInt() throws InterruptedException {
double r = 0;
for( int i = 1; i < N_REPEAT_L; i++ ) {
r += CONST_D / i;
}
return r;
}
@Benchmark
public double divDoubleLong() throws InterruptedException {
double r = 0;
for( long i = 1; i < N_REPEAT_L; i++ ) {
r += CONST_D / i;
}
return r;
}
@Benchmark
public int mulInt() throws InterruptedException {
int r = 0;
for( int i = 0; i < N_REPEAT_I; i++ ) {
r += i * CONST_I;
}
return r;
}
@Benchmark
public long mulLong() throws InterruptedException {
long r = 0;
for( long i = 0; i < N_REPEAT_L; i++ ) {
r += i * CONST_L;
}
return r;
}
@Benchmark
public int subInt() throws InterruptedException {
int r = 0;
for( int i = 0; i < N_REPEAT_I; i++ ) {
r += i - r;
}
return r;
}
@Benchmark
public long subLong() throws InterruptedException {
long r = 0;
for( long i = 0; i < N_REPEAT_L; i++ ) {
r += i - r;
}
return r;
}
@Benchmark
public long l2i() throws InterruptedException {
int r = 0;
for( long i = 0; i < N_REPEAT_L; i++ ) {
r += (int)i;
}
return r;
}
}