На Хабре уже было тестирование Эльбрусов на разных языках программирования (например, здесь). И данный обзор стоит рассматривать как дополнение, с ещё одним тестом, новыми версиями компиляторов и новыми участниками (Rust, С++). Также обзор сделан с упором на тест возможностей именно компиляторов и настройки оптимизации.
Тестовые стенды:
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 Мгц.
Софт:
Java: OpenJDK Runtime Environment (build 11.0.15-Unipro+0-adhoc.root.openjdk11-11.0.15).
Rust: rustc / cargo v.1.57.0.
C++: GCC v9.3.0 compatible; LLVM version 13.0.1.
Испытуемые: Java, Rust, C++(GCC, LLC).
Тест
В качестве теста выступает решето Эратосфена в блочном варианте. Один поток. На трёх языках реализовано максимально идентично. Программа в консольном варианте. Есть возможность повторного расчёта.
Методика тестирования
Выполняем два запуска по пять прогонов поиска простых чисел в диапазоне 0 - 5*108. Первый прогон разогревочный и в расчёт не идёт. Для Java прогревочный проход обязателен. И, как показала практика, на C++ и Rust первый прогон тоже чуть медленнее. По оставшимся результатам двух прогонов вычисляется средний показатель.
Результаты тестов
В сравнении gcc и llc, gcc оказался чуть эффективнее, но разница очень маленькая, меньше 1%. Поэтому ниже в тестах будут фигурировать значения llc с обозначением gcc/llc. Значения указаны в миллисекундах.
Некоторые неожиданности.
Ожидалось, что исполняемые файлы, полученные на одной x86-машине, должны работать на другой x86-машине. Но C++ программа, скомпилированная на AMD FX с настройкой оптимизации выше, чем O0, на Intel Celeron (Haswell) выпадала с ошибкой: "Недопустимая инструкция (образ памяти сброшен на диск)". Однако с Rust таких проблем не наблюдалось. И так же всё, что скомпилировано на Intel, работало на AMD FX. При этом, независимо от того, на какой машине был получен исполняемый файл, результаты тестов совпадали.
gcc |
gcc/llc O2 |
gcc/llc O3 |
gcc/llc O4 |
Rust |
Rust |
Java |
|
Elbrus 8C @ 1.2Ghz |
45712 |
4694 |
3912 |
3830 |
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 |
Жирным выделены результаты самых удачных настроек компиляции для каждой машины.
Сразу бросается в глаза разница между gcc O0 и gcc/llc O2 на Эльбрусе. Такова плата за запуск неоптимизированных программ на e2k.
В отличие от x86, для Эльбруса оптимизация O4 даёт профит.
А вот для Rust везде лучший результат показала оптимизация O2.
Итак, лучшие настройки оптимизации для данного теста на платформах:
x86: Rust - O2, C++ - O3.
e2k: Rust - O2, C++ - O4.
P.S.
Не являюсь специалистом по Rust и C++. Код достаточно простой и там вряд ли будут серьёзные недочёты. Но мог недоработать в плане настроек компиляции.
Благодарю компанию МЦСТ за возможность ознакомления с ЦП семейства Эльбрус!
Комментарии (30)
redf1sh
13.12.2024 15:50Не увидел у вас ссылки на исходный код. Как мне проверить ваши измерения?
И так, лучшие настройки оптимизации для данного теста на платформах:
x86: Rust - O2, C++ - O3.
Как-то бедно вы смотрели. Откроем список опций в GCC https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html сделаем поиск по "-f" и получим 900+ результатов. Грубо говоря половина будет -fno. Грубая оценка даёт вам 450 опций управления компиляцией. А вы рассмотрели только одну. Также не пробовали режимы компиляции с профилем. Не пробовали опции -march=, которые на Эльбрусе дают большой прирост.
Я не понял какой компилятор вы использовали для Эльбруса.В сравнении gcc и llc, gcc оказался чуть эффективнее, но разница очень маленькая
Это один и тот же компилятор. LCC является совместимым с GCC по опциям, чтобы облегчить жизнь пользователям по системе сборки. И на Эльбрусах gcc является алиасом lcc в системе. Так что если есть разница, то скорее всего это ошибка измерения.
У меня вызывает вопрос следующее:
C++: GCC v9.3.0 compatible; LLVM version 13.0.1.
Какая версия LLVM у lcc, если его там нет? Или вы тестировали clang? А если нет, то зачем написали?
VVS_AMD Автор
13.12.2024 15:50Спасибо за дельный комментарий.
Ссылки на исходники находятся в конце статьи в разделе "Ресурсы".
Настроек компиляции действительно пробовал мало. Но -march=native пробовал и разницы не заметил. Предварительно почитал рекомендации МЦСТ. Из описанных там опций компиляции увидел смысл только в двухфазной компиляции с профилем. У прочих настроек не увидел пользы для моего кода. Компилировал двумя способами: 1) g++ -Ox -march=native main.cpp -o main; 2) cmake --build . Файл CmakeLists.txt можете найти в исходниках. g++ давал мизерный (десятые доли процента), но стабильный плюс со всеми режимами оптимизации. Так что ошибка измерений исключена.
Допускаю, что мог напутать с lcc/gcc. Тут мои познания достаточно скромные. А версию LLVM я указал не случайно. Путь к этой самой LLVM прописан в cmake-файле.nick758
13.12.2024 15:50Ссылки на исходники находятся в конце статьи в разделе "Ресурсы".
Ссылку на C++ исправьте: https://gitlab.com/vvsamd/the-sieve-of-eratosthenes-in-c
redf1sh
13.12.2024 15:50Я не спорю, что путь к LLVM прописан в CMakeLists.txt. Я не понимаю зачем. Вы сами написали, что собираете компилятором g++. LLVM никак не используется в вашем main.cpp. Зачем он тогда?
Вижу, что прописанproject(LLVMPassSample)
что подсказывает мне CMakeFiles.txt взят от балды из интернета от студенческого репозитория с реализацией своего pass'a.cmake_minimum_required(VERSION 3.7) project(LLVMPassSample) set(CMAKE_CXX_STANDARD 17) set(LLVM_DIST_PATH "/usr/local/opt/llvm-clang/current" CACHE STRING "LLVM distribution install path") set(LLVM_DIR ${LLVM_DIST_PATH}/lib/cmake/llvm) find_package(LLVM REQUIRED CONFIG) message(STATUS "Using LLVM version ${LLVM_PACKAGE_VERSION}") list(APPEND CMAKE_MODULE_PATH ${LLVM_CMAKE_DIR}) set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin) set(LLVM_LIBRARY_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib) include(HandleLLVMOptions) include(AddLLVM) add_compile_options(-march=native) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3") add_definitions(${LLVM_DEFINITIONS}) include_directories(${LLVM_INCLUDE_DIRS}) add_executable(sieve_of_Eratosthenes main.cpp)
VVS_AMD Автор
13.12.2024 15:50Я же написал, ко компилировал двумя способами: g++ и cmake. И про мизерную разницу между компиляторами... как раз про это.
CMakeFiles.txt взят от балды из интернета
Не от балды, а как результат поиска вариантов использования LLVM в cmake. На статус спеца по C++ не претендую. И в статье об этом написал. У меня, как у java-разработчика с минимальными знаниями C++ и Rust, написав годный код на этих языках, стояла задача собрать годный исполняемый файл с применением самых профитных оптимизаций. Единственной ошибкой в этом вопросе пока неиспользование PGO. Исправляю этот момент.
Огромная просьба! Прочитайте внимательнее статью и мои комментарии. А то вы уже начинаете с меня спрашивать то, чего я не писал...
Даже тут:В сравнении gcc и llc, gcc оказался чуть эффективнее, но разница очень маленькая
Это один и тот же компилятор. LCC является совместимым с GCC по опциям, чтобы облегчить жизнь пользователям по системе сборки. И на Эльбрусах gcc является алиасом lcc в системе.
У меня написано про llc - LLVM static compiler. А вы мне про LCC...
redf1sh
13.12.2024 15:50Смешались в кучу кони, люди,
Давайте я объясню подробнее в чём проблема.
У меня написано про llc - LLVM static compiler. А вы мне про LCC...
Возможно вы действительно не опечатались и пытались использовать транслятор llc, что уже само по себе вызывает вопрос. Это не полноценный компилятор для языка С++ на котором написана ваша программа. Это только бэкенд для трансляции LLVM-IR в ассемблер. У него нет фронтенда и запуска линкера. Вам нужен был драйвер компиляции. В проекте LLVM для языка C++ это clang++.Я же написал, ко компилировал двумя способами: g++ и cmake. И про мизерную разницу между компиляторами... как раз про это.
cmake -- это не компилятор. Это система генерации файлов сборки для систем сборки (make, ninja, visual studio и т.д.). Она ничего не компилирует. Она создаёт инструкции для целевой системы сборки при выполнении которых и будет запущен компилятор.
Не от балды, а как результат поиска вариантов использования LLVM в cmake. На статус спеца по C++ не претендую. И в статье об этом написал. У меня, как у java-разработчика с минимальными знаниями C++ и Rust, написав годный код на этих языках, стояла задача собрать годный исполняемый файл с применением самых профитных оптимизаций. Единственной ошибкой в этом вопросе пока неиспользование PGO. Исправляю этот момент.Огромная просьба! Прочитайте внимательнее статью и мои комментарии. А то вы уже начинаете с меня спрашивать то, чего я не писал...
LLVM это не только компилятор. Это ИНФРАСТРУКТУРА для ПОСТРОЕНИЯ компиляторов. Я не просто так написал, что в CMakeFiles.txt у вас заготовка для своего пасса. И компилятор clang/LLVM (а именно так, так как вам нужен ещё и фронтенд) там не прописывается. Вы могли сами это проверить, но давайте я сделаю это за вас:
$ lscpu Architecture: e2k ... CPU family: 4 Model name: E8C CPU MHz: 1300 $ git clone https://gitlab.com/vvsamd/the-sieve-of-eratosthenes-in-c $ cmake --version cmake version 3.15.4 $ mkdir build && cd build $ cmake .. -- The C compiler identification is GNU 7.3.0 -- The CXX compiler identification is GNU 7.3.0 -- Check for working C compiler: /usr/bin/cc -- Check for working C compiler: /usr/bin/cc -- works ... -- Using LLVM version 9.0.1 ... $ cmake --build . -v gmake[2]: Entering directory '/the-sieve-of-eratosthenes-in-c/build' [ 50%] Building CXX object CMakeFiles/sieve_of_Eratosthenes.dir/main.cpp.o /usr/bin/c++ -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -fPIC -fvisibility-inlines-hidden -Werror=date-time -std=c++11 -w -ffunction-sections -fdata-sections -O3 -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -march=native -std=gnu++1z -o CMakeFiles/sieve_of_Eratosthenes.dir/main.cpp.o -c /the-sieve-of-eratosthenes-in-c/main.cpp [100%] Linking CXX executable sieve_of_Eratosthenes /usr/bin/cmake -E cmake_link_script CMakeFiles/sieve_of_Eratosthenes.dir/link.txt --verbose=1 /usr/bin/c++ -fPIC -fvisibility-inlines-hidden -Werror=date-time -std=c++11 -w -ffunction-sections -fdata-sections -O3 CMakeFiles/sieve_of_Eratosthenes.dir/main.cpp.o -o sieve_of_Eratosthenes gmake[2]: Leaving directory '/the-sieve-of-eratosthenes-in-c/build' $ c++ --version lcc:1.25.10:Nov--7-2020:e2k-v4-linux gcc (GCC) 7.3.0 compatible
Что здесь происходит?
Я выкачал с вашего gitlab ваш проект
Я выполнил его конфигурацию через cmake и получил сообщение, что будет использован компилятор lcc, совместимый с GCC 7.3.0
Я получил сообщение, что будет подключен проект LLVM (но не будет использован компилятор clang++/LLVM(!))
Я собрал проект и что видно по строкам компиляции, он собирался с помощью LCC
Вы НЕ собрали ваше решето компилятором, связанным с LLVM. Вы подключили с помощью строк, связанных с LLVM всю необходимую инфраструктуру (заголовочные файлы, библиотеки (libllvm?)) для разработки своей фазы компиляции в составе LLVM. Чувствуете разницу?
Чтобы собрать с clang/LLVM достаточно было выполнить:
$ clang++ -O3 main.cpp -o main
Тут даже cmake не нужен. Но если он вам так понравился, то можете написать CMakeLists.txt как-то так
cmake_minimum_required(VERSION 3.7) project(the_sieve_of_eratosthenes_in_c) add_compile_options(-march=native) add_compile_options(-O3) add_executable(sieve_of_eratosthenes main.cpp)
И собрать так проект так:
$ mkdir build && cd build $ cmake .. -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_C_COMPILER=clang -- The C compiler identification is Clang 9.0.1 -- The CXX compiler identification is Clang 9.0.1 -- Check for working C compiler: /usr/bin/clang -- Check for working C compiler: /usr/bin/clang -- works ... $ cmake --build . -v [ 50%] Building CXX object CMakeFiles/sieve_of_eratosthenes.dir/main.cpp.o /usr/bin/clang++ -march=native -O3 -o CMakeFiles/sieve_of_eratosthenes.dir/main.cpp.o -c /the-sieve-of-eratosthenes-in-c/main.cpp /the-sieve-of-eratosthenes-in-c/main.cpp:168:5: warning: only one parameter on 'main' declaration [-Wmain] int main(int endValue) { ^ 1 warning generated. [100%] Linking CXX executable sieve_of_eratosthenes /usr/bin/cmake -E cmake_link_script CMakeFiles/sieve_of_eratosthenes.dir/link.txt --verbose=1 /usr/bin/clang++ CMakeFiles/sieve_of_eratosthenes.dir/main.cpp.o -o sieve_of_eratosthenes
Теперь мы собрали проект с помощью clang++/LLVM
Давайте я и проведу сравнение (5*10^8 чисел):
clang-9: -O3 3731мс
Но мне clang не интересен.lcc 1.29: -O3 -march=elbrus-v4: 3364мс
lcc 1.29: -O4 -march=elbrus-v4 -ffast -ffast-math: 2351мс
Замечаем, что счётчики циклов 32-битные, когда мы работаем на 64-битах. Это заставит компилятор генерировать расширение. Замена int -> long int даёт:
lcc 1.29: -O4 -march=elbrus-v4 -ffast -ffast-math + long int 2135мс
Экспериментируем с опциями дальше
lcc 1.29: -O4 -march=elbrus-v4 -ffast -ffast-math -fforce-loop-apb + long int: 2058мс
lcc 1.29: -O4 -march=elbrus-v4 -ffast -ffast-math -fforce-loop-apb -fforce-vect -fforce-swp + long int: 2053мсЗаметим, что есть проблемы с циклом на main.cpp:100. Пометим его #pragma swp
lcc 1.29: -O4 -march=elbrus-v4 -ffast -ffast-math -fforce-loop-apb -fforce-vect -fforce-swp + long int + swp: 2033мс
Судя по perf железо занято на 90.1%. Дальше ускорять уже нет ни желания, ни времени
PGO тут не помогло. Здесь слишком мало кода и предсказатель профиля и так справился хорошо
Armmaster
13.12.2024 15:50К методике замеров нет смысла придираться, как говорится, что-то померяли и ладно. Единственное, было бы очень полезно хотя бы изначальный C/rust/Java код привести, а в идеале ещё и ассемблер для каждого варианта компиляции.
Armmaster
13.12.2024 15:50Так, вопрос по исходному коду снимается, в конце есть ссылки, но они сливаются с тегами в конце статьи, из-за чего их совсем не заметно. Лучше в начало вынести. По ассемблеру пожелание остаётся в силе
m0xf
Какой же медленный этот эльбрус. Celeron G1820 - процессор 2014 года с весьма посредственной производительностью. Если сравнить с современным процессором разница производительности будет уже в десятки раз.
ss-nopol
Для 99.9% процентов офисных задач достаточно.
VVS_AMD Автор
8С вряд ли пропишется в каком-нибудь офисе. Это скорее ЦП для серверов. 2С3 для офисов больше подходит. У него и однопоточая производительность значительно выше, раза в два - три.
Armmaster
На данной задаче разница между 8С и 2С3 должна скалироваться согласно частоте, т.е. улучшение будет 2/1.2 = 1.67 раза.
VVS_AMD Автор
Я, как бы, имел ввиду в общем...
Ну а в этой задаче, почему линейно частоте? У 2C3 пиковая производительность в два раза больше операций за такт. Вы смотрели исходники теста? По моему, самая затратная функция makeHoles() отлично разложится в широкую команду любой длины. Там блоки по 10000!
Armmaster
Ох уж эти операции за такт, было бы дело в них, Эльбрусу не было бы равных!)
В 2 раза больше операций в такт это за счёт векторизации, причем для плавающих инструкций в первую очередь. Здесь это вряд ли актуально.
VVS_AMD Автор
Жаль, что нет образца потестить. Самое крутое, что есть в общем доступе, это E8C.
Armmaster
Развлекайтесь:
https://ce.mentality.rip/
OpenA
на каждую такую операцию в интеле/амд найдется дополнительная стадия конвеера и преимуществ в итоге не видно.
Разница лишь в том что эльбрусу такая производительность дается относительно легко и дешево, тогда как над длинным конвеером в суперскаляре надо пыхтеть.
Armmaster
Вы понятия не имеете, о чём говорите. Впрочем, как обычно.
Особенно смешно читать про "не видно преимуществ", когда современные и не очень ОоО ядра, имея существенно меньшую площадь, обладают в разы большой производительностью по сравнению с Vliw
OpenA
А вы не умеете читать, я говорил про то что эльбрус с его вроде бы много команд за такт, не имеет преимуществ перед интел/амд которые хоть и не имеют столько устройств зато имеют длинный конвеер который умеют очень плотно набивать, + имеют 256битные вектора против 64 битных например у 8С, и как следствие невозможность эмулировать два avx/такт
Другое дело что нигде и не заявляется что 25 операций/такт кого то там порвут, заявляется что они обеспечивают высокую производительность, тесты алгоритмов шифрования показывают что эльбрус вполне себе приближается по производительности к вполне себе современным intel/амд с задействованными avx -ами
И вот эта возможность пусть на некоторых задачах выступать на уровне флагманов досталась относительно дешево, это нужно тоже учитывать.
если бы это было так тогда бы все использовали OoO в dsp, но почему то когда нужно что то быстро считать, затрачивая минимум энергии все выбирают vliw
Armmaster
Я могу лишь повторить, что вы понятия не имеете, о чём говорите, поэтому вышенаписанное - это какой то винегрет, где спутаны глубина и ширина конвейера и много ещё чего другого. Давайте вы для начала хотя бы Харриса осилите. В конце концов, вы только на моей памяти уже минимум 3 года пытаетесь выступать со своими "идеями", но что-то прогресса в понимании не видно. Стыдно должно быть уже
OpenA
Не нужно повторять, нужно приводить аргументы, я ничего не говорил про какую то глубину и ширину конвеера не надо мне приписывать какие то глупости которые я не говорил. Ни с какими идеями я так же не выступаю, просто читаю и отвечаю на комментарии. Все. Обратитесь к психотерапевту.
VVS_AMD Автор
Во-первых, Эльбрус 8С и Haswell практически одногодки. И с тех пор Эльбрусы прошли ещё два поколения. И по производительности они тоже хорошо прибавили. Во-вторых, Celeron G1820 не так уж и слаб в однопотоке. А тесты проводились как раз в однопотоке. Современные топовые ЦП, может, раза в три-четыре быстрее.
Хотя с тем, что Эльбрусы сильно не дотягивают по производительности до современных топовых ЦП, спорить не стану.
nixtonixto
Этот Celeron на старте продаж стоил $42, причём в эту цену ещё входит графический ускоритель... Это сверхбюджетное решение, и сравнивать с ним что-то не сверхбюджетное - как минимум, некорректно.
VVS_AMD Автор
Внимание! Мы тестим однопоток! Это раз.
Целерон тоже можно засунуть в четырёхпроцессорный сервер и получить в итоге 32 ядра? Он поддерживает ECC память? Статья не про "выбор самого выгодного ЦП на 2015 год". Это два.
Ядрён, батон, какая цена?
Эльбрус можно и с лампочкой сравнить, если речь пойдёт о содержании драг.металлов в электронных компонентах. Главное - оставаться в теме.
nixtonixto
Тогда почему вы сравниваете с целероном и ещё более древним FX, а не с серверными процессорами с поддержкой ECC-памяти?
VVS_AMD Автор
Я уже не знаю как ответить, честно. Тему статьи пробовали читать?
Kassiy_Pontiy_Pilat
Вы все померяли или как обычно блаблабла
OpenA
8C процессор 2015г