В последнее время у меня очень странная вещь: один метод был чрезвычайно медленным при профайлере без очевидной причины. Он содержит несколько операций с long
, но вызывается довольно часто - его общее использование составляло около 30-40% от общего времени программы, тогда как другие части выглядят намного "тяжелее".
Обычно я запускаю программы, не содержащие памяти, на JVM x32, но, предполагая, что у меня проблема с 64-разрядным типом, я попытался запустить то же самое на x64 JVM - общая производительность в "реальном сценарии" была на 2-3 раза лучше. После этого я создал тесты JMH для операций из определенного метода и был шокирован разницей в JVM x32 и x64 - до 50 раз.
Я бы "согласился" примерно в 2 раза медленнее x32 JVM (размер меньшего слова), но у меня нет никаких подсказок, откуда может появиться 30-50 раз. Можете ли вы объяснить это резкое различие?
Ответы на комментарии:
- Я переписал тестовый код, чтобы "вернуть что-то" и избежать "устранения мертвого кода". Похоже, что он ничего не изменил для "x32", но некоторые методы на "x64" были значительно медленнее.
- Оба теста выполнялись под "клиентом". Работа под "-server" не имела заметного эффекта.
Так что кажется, что ответ на мой вопрос
- "тестовый код" был неправильным: из-за "отсутствия возвращаемого значения" он разрешил JVM делать "удаление мертвого кода" или любую другую оптимизацию, и похоже, что "x32 JVM" делает меньше таких оптимизаций, чем "x64 JVM", - которые вызвали такие значительная "ложная" разница между x32 и x64
- Первичная разница по "правильному тестовому коду" составляет до 2x-5 раз - это кажется разумным
Вот результаты (Примечание ? 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;
}
}