Целочисленная производительность - разница в 30-50 раз больше x32 против x64 jvm? - PullRequest
0 голосов
/ 02 июля 2018

В последнее время у меня есть очень странная вещь - один метод был очень медленным под профилировщиком без очевидной причины. Он содержит несколько операций с 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;
    }

}

1 Ответ

0 голосов
/ 06 июля 2018

Есть много переменных для проверки.

Если мы смотрим только на процессор, использующий 64-битную систему, вы можете адресовать больше операций в регистры ЦП тем же шагом, поскольку он использует октеты вместо четырех октетов на реестр. Это увеличивает производительность операций и выделение памяти. Кроме того, некоторые CPU поддерживают только расширенные функции, работающие в 64-битном режиме

Если вы используете один и тот же ЦП для выполнения тестов, вы должны принять во внимание, что для выполнения 32-битных инструкций ЦП должен работать в виртуальном режиме или защищенном режиме, который работает медленнее, чем настоящий 32-битный ЦП. , Также некоторые расширения набора команд, вероятно, не могут быть включены с использованием 32-разрядного режима, например SSE-SIMD или AVX, что может увеличить скорость некоторых операций.

Кроме того, если вы используете современную ОС, такую ​​как Windows 10, вы должны принять во внимание, что ОС запускает 32-битные приложения с использованием WOW64 (эмулятор x86)

Справочный документ:

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