На Хабре уже было тестирование Эльбрусов на разных языках программирования (например, здесь). И данный обзор стоит рассматривать как дополнение, с ещё одним тестом, новыми версиями компиляторов и новыми участниками (Rust, С++). Также обзор сделан с упором на тест возможностей именно компиляторов и настройки оптимизации.

Предыстория

Однажды получив доступ к серверу на базе Эльбрус 8С, появилось желание оценить эффективность его работы с Java. Я уже был осведомлён о сложности JIT-компиляции на e2k и о проделанной ребятами из УниПро работе. Но, сделав замеры, их нужно с чем-то сравнить. Для этого хорошо подходят C++ и Rust, компиляторы которых должны лучше справляться с оптимизацией под e2k. Особенно C++, компиляции которого уделено большое внимание в рекомендациях МЦСТ. В качестве тестов был выбран расчёт простых чисел с помощью решета Эратосфена (блочный вариант). Теоретически, алгоритм должен хорошо оптимизироваться под e2k. И тем интереснее посмотреть, как с этим справятся компиляторы на разных языках.

Тестовые стенды:

x86:

  • AMD FX-6300@3500 Мгц. (турбобуст отключен).

  • Intel Celeron (Haswell) G1820@2700 Мгц.

Софт:

Ubuntu 22.04.

Java: OpenJDK Runtime Environment (build 11.0.25+9-post-Ubuntu-1ubuntu122.04).

Rust: rustc / cargo v.1.83.0; LLVM version: 19.1.1.

C++: GCC v11.4.0; LLVM version 19.1.5.

e2k:

  • Эльбрус 8С@1200 Мгц.

Софт:

Elbrus Linux 7.2

Java: OpenJDK Runtime Environment (build 11.0.15-Unipro+0-adhoc.root.openjdk11-11.0.15).

Rust: rustc / cargo v.1.57.0.

C++: lcc:1.26.22:Jan-10-2024:e2k-v4-linux (gcc (GCC) 9.3.0 compatible)

Испытуемые: Java, Rust, C++(GCC, LСC).

Тестовая задача

В качестве теста выступает решето Эратосфена в блочном варианте. Один поток. На трёх языках реализовано максимально идентично. Программа в консольном варианте. Есть возможность повторного расчёта.

  • исходник и jar-архив Java;

  • исходник и исполняемые (Win, Linux) файлы (+ ассемблер) Rust;

  • исходник и исполняемые (Linux) файлы (+ ассемблер) C++.

Методика тестирования

Выполняем два запуска по пять прогонов поиска простых чисел в диапазоне 0 - 5*108. Первый прогон прогревочный и в расчёт не идёт. Для Java прогревочный проход обязателен. И, как показала практика, на C++ и Rust первый прогон тоже чуть медленнее. По оставшимся результатам двух прогонов вычисляется средний показатель.

Описание опций тестирования
  • gcc O0: $ g++ -march=native main.cpp -o eratosthenes_O0

  • gcc O2: $ g++ -O2 -march=native main.cpp -o eratosthenes_O2

  • gcc O3: $ g++ -O3 -march=native main.cpp -o eratosthenes_O3

  • gcc O4: $ g++ -O4 -march=native main.cpp -o eratosthenes_O4

  • gcc O4 +fast-math: $ g++ -O4 -march=native -ffast -ffast-math main.cpp -o eratosthenes_O4fast-math

  • gcc O4 +fast-math +PGO: двухфазная компиляция.

    1-я фаза.

    • g++ -O4 -march=elbrus-v4 -ffast -ffast-math -fprofile-generate -Wall -c -fmessage-length=0 -MMD -MP -MF"pgo-1.d" -MT"pgo-1.d" -o "pgo-1.o" "main.cpp"

    • g++ -fprofile-generate -o "pgo-1" pgo-1.o

    Сбор статистики: $ ./pgo-1 с диапазонами: 100 миллионов; 500 миллионов; 2 миллиарда.

    2-я фаза.

    • g++ -O4 -march=elbrus-v4 -ffast -ffast-math -fprofile-use -Wall -c -fmessage-length=0 -MMD -MP -MF"pgo-1.d" -MT"pgo-1.d" -o "pgo-1.o" "main.cpp"

    • g++ -fprofile-use -o "eratosthenes_O4fast-math+PGO" ./pgo-1.o

  • gcc O4 +fast-math +PGO +long_int: Все типы int в коде заменены на long int;

    перед циклом с вызовом "makeHoles(&block, curPnIdx);" ставим "#pragma swp".

    Затем выполняем двухфазную компиляцию.

    1-я фаза.

    • g++ -O4 -march=elbrus-v4 -ffast -ffast-math -fforce-loop-apb -fforce-vect -fforce-swp -fprofile-generate -Wall -c -fmessage-length=0 -MMD -MP -MF"pgo-1.d" -MT"pgo-1.d" -o "pgo-1.o" "main.cpp"

    • g++ -fprofile-generate -o "pgo-1" pgo-1.o

    Сбор статистики: $ ./pgo-1 с диапазонами: 100 миллионов; 500 миллионов; 2 миллиарда.

    2-я фаза.

    • g++ -O4 -march=elbrus-v4 -ffast -ffast-math -fforce-loop-apb -fforce-vect -fforce-swp -fprofile-use -Wall -c -fmessage-length=0 -MMD -MP -MF"pgo-1.d" -MT"pgo-1.d" -o "pgo-1.o" "main.cpp"

    • g++ -fprofile-use -o "eratosthenes_O4long_int+PGO" ./pgo-1.o

Результаты тестов

Некоторые неожиданности.

Опция -march=native ни на одной из платформ не дала профита по сравнению с -march=x86-64. При этом исполняемый файл, полученный с такой опцией на AMD FX, на Intel Celeron (Haswell) выпадал с ошибкой: "Недопустимая инструкция (образ памяти сброшен на диск)".

gcc/lcc O0

gcc/lcc O2

gcc/lcc O3

gcc/lcc O4

lcc O4 +fast-math

lcc O4 +fast-math +PGO

lcc O4 +fast-math +PGO +long_int

Rust
O2

Rust
O3

Java

Elbrus 8C @ 1.2Ghz

45712

4694

3912

3830

2479

2149

1968

4941

5033

19357

AMD FX @ 3.5Ghz

7743

2373

2208

2205

--

--

--

1818

1918

4635

Cel G1820 @ 2.7Ghz

7508

1648

1523

1543

--

--

--

1183

1213

5123

Пояснение к результатам тестов
  • Жирным выделены результаты самых удачных настроек компиляции для каждой машины.

  • Некоторые опции компиляции, указанные в таблице, не дали никакого прироста к производительности на x86. По этой причине полноценное тестирование с этими опциями не проводилось и данные в таблице не заполнены.

  • Обозначение gcc/lcc означает, что x86 использовался компилятор gcc, а на e2k - lcc (компилятор МЦСТ, совместимый с gcc).

Сравним на графике результаты компиляции C++. Сразу бросается в глаза разница между lcc O0 и lcc O2 на Эльбрусе. Такова плата за запуск неоптимизированных программ на e2k.

В отличие от x86, для Эльбруса оптимизация O4 даёт профит.

( ! ) К сожалению, на период проведения тестов и написания статьи двухфазный режим компиляции для Rust не доступен. Как появится такая возможность, результаты будут добавлены в таблицу.

Таким образом, для Rust везде лучший результат показала оптимизация O2.

Итак, лучшие настройки оптимизации для данного теста на платформах:

  • x86: Rust - O2, C++ - O3.

  • e2k: Rust - O2, C++ - O4 +fast-math +PGO +long_int.

P.S.

Не являюсь специалистом по Rust и C++. Код достаточно простой и там вряд ли будут серьёзные недочёты. Но мог недоработать в плане настроек компиляции.
Благодарю компанию МЦСТ за возможность ознакомления с ЦП семейства Эльбрус!