Приветствую! Я разработчик в компании НИЦ ЦТ. Мы разрабатываем операционную систему, адаптированную под российские процессоры Эльбрус. Процессоры Эльбрус работают на своей оригинальной архитектуре, которая имеет свои преимущества и недостатки. В частности, интерпретируемые языки программирования не блещут производительностью. Вот мы и решили провести сравнительное тестирование различных языков, компиляторов и интерпретаторов чтобы выяснить, что лучше использовать для разработки под Эльбрус.
В статье представлены результаты бенчмарка Programming language benchmark, основанного на решении набора задач идентичными алгоритмами, реализованными на разных языках. Это позволяет оценить эффективность генерируемого компиляторами (или интерпретаторами) кода для выбранной архитектуры, поскольку скорость выполнения напрямую зависит от архитектурных особенностей процессора. Учитывая использование одного ядра процессора в тестах, результаты отражают потенциал оптимизации кода на низком уровне для каждого языка программирования в рамках заданной аппаратной платформы. Полученные данные позволят разработчикам делать более обоснованный выбор языка программирования для задач, критичных к производительности, с учетом специфики целевой архитектуры.
Тестируемые железки
Процессоры Эльбрус 2С3 и 8С2 также известный как 8СВ — российские процессоры, разработанные компанией МЦСТ, основанные на архитектуре Эльбрус, которая существенно отличается от x86 и ARM. Вот основные характеристики тестируемых процессоров:
Характеристика | E2C3 | E8C2 |
---|---|---|
Производитель | Elbrus-MCST | Elbrus-MCST |
Архитектура | e2kv6 | e2kv5 |
Тип архитектуры | VLIW | VLIW |
Порядок байтов | Little Endian | Little Endian |
Количество ядер | 2 | 8 x 2 = 16 |
Потоков на ядро | 1 | 1 |
Базовая частота | 1454.54 МГц | 960.00 МГц |
Максимальная частота | 2000.00 МГц | 1500.00 МГц |
Кэш L1d | 128 КБ (2 x 64 КБ) | 1 МБ (16 x 64 КБ) |
Кэш L1i | 256 КБ (2 x 128 КБ) | 2 МБ (16 x 128 КБ) |
Кэш L2 | 4 МБ (2 x 2 МБ) | 8 МБ (16 x 512 КБ) |
Кэш L3 | - | 32 МБ (2 x 16 МБ) |
NUMA узлы | - | 2 |
Оперативная память | 16 ГБ | 256 ГБ |
Важно отметить: Производительность процессоров Эльбрус нельзя напрямую сравнивать с x86 или ARM по тактовой частоте или количеству ядер из-за принципиальных отличий в архитектуре. Эффективность Эльбрус сильно зависит от качества компиляции и оптимизации программного обеспечения.
Данные тесты проводились без оптимизации на уровне кода при помощи сторонних библиотек (были взяты как есть и не подгонялись для оптимизации на 2с3 и 8с2) и только на процессорах Эльбрус.
Методика
В качестве набора тестов был выбран Programming language benchmark. Использовали не все тесты, потому что не всё получилось затащить под е2к.
Наша версия тестов и скрипты для их запуска.
Список доработок незначительный:
Добавил версии компиляторов/интерпретаторов прямо в название директории для упрощения работы скрипта-обвязки;
Были добавлены отдельные директории для lcc, fortran, clang, java и т.д., для того чтобы можно было одним скриптом запустить тесты сразу для всех версий;
Был доработан тест bedcov для go. Авторы оригинального теста использовали функцию мин макса (
maxStart = max(maxStart, interval.start)
), которую добавят только в версии 1.21. Добавил рукописные функции мин макса, чтобы запускать тесты на МЦСТшном go 1.17Матмул для php отказывался работать, по причине того, что не хватает памяти. Добавил строку, которая увеличивает количество используемой памяти для теста. Не совсем православно выдавать всю память для работы, поэтому добавил возможные значения для функции.
Не смог починить судоку для javascript (NodeJS 12, в целом он у МЦСТ очень нестабильно работает, особенно если использовать его для сборки пакетов).
Использование одного ядра процессора для изоляции производительности отдельных языков и исключения влияния многопоточности также дает возможность оценить между собой две машинки, у которых разное количество ядер.
Для оценки погрешности каждый тест выполнялся по 10 раз. Высчитывалось среднее значение и отклонение от среднего. Это необходимо для получения более точной оценки производительности, также это косвенно является свидетельством того, что не было запущено никаких фоновых процессов, способных повлиять на результаты тестов.
Как заверяют авторы оригинального проекта, каждый тест для каждого языка программирования был написан без каких-либо оптимизаций на уровне кода при помощи сторонних библиотек, для того, чтобы оценить "сам язык".
Тесты выполнялись без каких-либо фоновых процессов, которые могли бы "подъесть" ресурсы процессора и памяти.
Тесты запускались на разрабатываемой нами операционной системе CollabOS.
Описание тестов
Programming Language Benchmark включает в себя четыре теста, предназначенных для оценки производительности различных языков программирования в разных сценариях использования:
Bedcov (Поиск перекрытий). Этот тест измеряет скорость поиска перекрывающихся интервалов между двумя массивами размером 1 000 000 элементов. Алгоритм использует структуры данных, подобные неявным интервальным деревьям, и выполняет частые обращения к массиву с помощью метода, похожего на бинарный поиск. Таким образом тест оценивает эффективность работы с большими массивами данных и алгоритмами поиска.
Matmul (Умножение матриц). Данный тест измеряет производительность умножения двух квадратных матриц размером 1500x1500. Тест фокусируется на вычислительной мощности и эффективности работы с математическими операциями, типичными для научных вычислений и задач линейной алгебры.
Nqueen (Задача о N ферзях). Этот тест оценивает скорость решения классической задачи о N ферзях для N=15. Алгоритм решения включает вложенные циклы и целочисленные битовые операции. Тест проверяет эффективность работы с рекурсивными алгоритмами, обработки битовых данных и общую производительность в задачах комбинаторного поиска.
Sudoku (Решение судоку): Этот тест измеряет скорость решения 4000 сложных головоломок судоку (20 уникальных головоломок, каждая решается 200 раз). Используемый алгоритм активно задействует небольшие массивы и сложные логические проверки. Тест предназначен для оценки производительности в задачах с интенсивным использованием ветвлений, рекурсии и обработки небольших структур данных.
Опись компиляторов и интерпретаторов
Все версии компиляторов и интерпретаторов указаны в основных табличках снизу.
В силу специфики процессоров Эльбрус, часть компиляторов была взята нами в бинарном виде у компании МЦСТ. Например, gccgo. Там сидит портированный go и портированный gcc. Также любопытства ради проверил gcc-fortran. Всё, что идет из этого пакета и используетмя в тестах, я обозначил go-1.17, c-gcc-12 и fortran-gccgo-12.
В рамках задачи по портированию Gnome на Эльбрус был портирован GJS. Вслед за этим возникла идея посмотреть его производительность на Эльбрусах.
В нашей команде много физиков-ядерщиков. Поэтому из чисто профессионального интереса попробовали затащить root (фреймворк, разработанный для обработки и анализа данных в физике высоких энергий) на е2к и протестировать его производительность. Он пока собран у нас только на е2кv5.
luajit (без jit компилятора, но разогнанный под е2к), nodejs-12 (судя по результатам тестов, спойлер, был также разогнан под е2к), nodejs-18 были взяты у МЦСТ.
Результаты тестов
В табличках результатов указаны 6 знаков после запятой, в силу того что погрешности очень малы.
Был взят логарифмический масштаб на общих графиках, потому что без него в общий график не влезают все результаты и картинка становится менее наглядной.
Ввиду того, что наш дистрибутив собирался исключительно на базе LCC 1.27 и Clang 13 (в очень редких случаях на 1.28), графики отражают только эти версии компиляторов.
На общих гистограммах я решил не отображать погрешности, т.к они очень малы - в пределах 1%. В качестве примера можно оценить график производительности для компилятора языка C - lcc-1.27 на 8СВ (для примера был приведен 1 график, остальные можно посмотреть тут и аналогично для 2с3):
Результаты выполнения тестов для Эльбруса 2с3
Все данные в виде markdown таблицы и json формате для 2с3, можно найти тут:
Language/Tests | matmul (sec) | bedcov (sec) | nqueen (sec) | sudoku (sec) |
---|---|---|---|---|
c-lcc-1.26 | 46.018477 ± 0.040755 | 6.554327 ± 0.013228 | 6.689998 ± 0.002236 | 11.995536 ± 0.006244 |
c-lcc-1.27 | 45.994242 ± 0.034278 | 6.619963 ± 0.008544 | 6.015713 ± 0.003316 | 8.606562 ± 0.005744 |
c-lcc-1.28 | 45.498233 ± 0.056595 | 6.559512 ± 0.005567 | 6.186297 ± 0.001732 | 8.788083 ± 0.004000 |
c-lcc-1.29 | 42.387339 ± 0.050149 | 6.547222 ± 0.007071 | 6.184709 ± 0.002828 | 8.747038 ± 0.003464 |
c-clang-13 | 41.851653 ± 0.057157 | 8.642329 ± 0.011747 | 6.802236 ± 0.003162 | 12.697818 ± 0.004690 |
go-1.17 | 94.441969 ± 0.152420 | 15.367779 ± 0.132393 | 20.853073 ± 0.007937 | 32.592677 ± 0.014899 |
c-gcc-12 | 86.795281 ± 0.041327 | 13.693220 ± 0.038832 | 16.618957 ± 0.007745 | 23.908464 ± 0.009327 |
fortran-lcc-1.26 | 0.914164 ± 0.036891 | N/A | 9.284363 ± 0.004472 | 14.037174 ± 0.027258 |
fortran-lcc-1.27 | 0.905918 ± 0.027549 | N/A | 7.701214 ± 0.018411 | 10.258484 ± 0.026229 |
fortran-lcc-1.28 | 1.002254 ± 0.030133 | N/A | 7.185685 ± 0.010198 | 10.431301 ± 0.008246 |
fortran-lcc-1.29 | 0.971973 ± 0.025000 | N/A | 8.778281 ± 0.003316 | 10.133026 ± 0.012288 |
fortran-gccgo-12 | 69.685218 ± 0.477292 | N/A | 33.878222 ± 0.288690 | 25.848793 ± 0.197517 |
rust-1.64 | 2.340310 ± 0.004123 | 11.070235 ± 0.032771 | 5.410572 ± 0.106808 | 12.566705 ± 0.021166 |
ruby-3.0.4 | 2709.607331 ± 1.185308 | 334.081656 ± 0.471810 | 1369.947277 ± 0.557911 | 1131.851663 ± 0.708690 |
perl-5.32 | 3234.667033 ± 3.431771 | N/A | 2345.188487 ± 1.365431 | 1457.432020 ± 1.323774 |
python3.9 | 4079.257241 ± 8.089523 | 668.885692 ± 4.851954 | 3033.103958 ± 0.760878 | 1363.447443 ± 0.382439 |
lua-5.1 | 1098.563232 ± 0.253872 | 385.296761 ± 1.397590 | 1825.991608 ± 0.149110 | 575.102830 ± 0.115490 |
luajit-2.1.17 | 299.184096 ± 0.135779 | 181.714955 ± 0.851839 | 531.698003 ± 0.030331 | 193.844777 ± 0.109585 |
java-8 | 2.476454 ± 0.024959 | 20.783304 ± 0.067638 | 8.335929 ± 0.034727 | 19.376261 ± 0.075808 |
java-11 | 3.638796 ± 0.019287 | 21.599611 ± 0.209442 | 10.766053 ± 0.720889 | 16.196009 ± 0.088960 |
java-21 | 11.431950 ± 22.693130 | 23.712936 ± 0.505195 | 12.262036 ± 1.922247 | 18.873130 ± 0.126043 |
nodejs-12 | 123.338045 ± 0.239576 | 54.898590 ± 0.247250 | 71.148237 ± 0.035749 | N/A |
nodejs-18 | 5409.541915 ± 0.233593 | 537.981153 ± 0.510904 | 1846.023857 ± 0.088718 | N/A |
gjs-1.68.6 | 6443.823039 ± 9.143257 | 1080.976614 ± 8.666105 | 2048.610810 ± 1.141611 | N/A |


Тут стоит отметить, что благодаря итерациям, как на 2с3, так и на 8с2, был выявлен интересный момент с Java 21 в тесте matmul. Примерно раз в несколько итераций, идет просадка по производительности в 20 раз (результаты для 100 итераций тут).



Результаты выполнения тестов для Эльбруса 8СВ
Все данные в виде таблички и json формате для 8СВ, можно найти тут:
Language/Tests | matmul (sec) | bedcov (sec) | nqueen (sec) | sudoku (sec) |
---|---|---|---|---|
c-lcc-1.26 | 50.107537 ± 0.667140 | 7.509413 ± 0.184808 | 8.365281 ± 0.005000 | 14.125620 ± 0.015000 |
c-lcc-1.27 | 45.371574 ± 0.469247 | 8.298999 ± 0.333499 | 8.023663 ± 0.002645 | 9.213383 ± 0.00105 |
c-lcc-1.28 | 40.955236 ± 0.345149 | 8.230248 ± 0.259353 | 8.141457 ± 0.005196 | 9.566796 ± 0.002000 |
c-lcc-1.29 | 41.049455 ± 0.241745 | 8.074247 ± 0.168330 | 8.139281 ± 0.002000 | 9.554030 ± 0.006000 |
c-clang-13 | 39.567161 ± 1.041576 | 7.953374 ± 0.022045 | 8.172353 ± 0.004582 | 9.258615 ± 0.003741 |
c-gcc-12 | 92.305233 ± 0.972707 | 14.405283 ± 0.118667 | 23.278070 ± 0.009433 | 30.298655 ± 0.021656 |
fortran-lcc-1.26 | 1.248038 ± 0.012569 | N/A | 11.489687 ± 0.003872 | 17.361722 ± 0.024576 |
fortran-lcc-1.27 | 1.237996 ± 0.012124 | N/A | 11.371770 ± 0.009949 | 11.489023 ± 0.019183 |
fortran-lcc-1.28 | 1.253795 ± 0.053000 | N/A | 11.594747 ± 0.005477 | 11.279128 ± 0.012961 |
fortran-lcc-1.29 | 1.320417 ± 0.012041 | N/A | 12.157142 ± 0.006164 | 11.125378 ± 0.019493 |
fortran-gccgo-12 | 71.728794 ± 0.500424 | N/A | 47.382606 ± 0.031543 | 34.709526 ± 0.034117 |
go-1.17 | 109.517956 ± 0.370909 | 17.113163 ± 0.304606 | 29.165977 ± 0.018761 | 45.227077 ± 0.044056 |
rust-1.64 | 3.369870 ± 0.045310 | 10.119645 ± 0.212652 | 7.018275 ± 0.003741 | 14.163828 ± 0.015905 |
ruby-3.0.4 | 2962.821228 ± 4.646511 | 389.381488 ± 2.069500 | 1514.496261 ± 7.839792 | 1269.840341 ± 2.069524 |
perl-5.30 | 2985.923036 ± 8.232634 | N/A | 2128.136913 ± 9.541525 | 1326.708822 ± 3.107975 |
python3.9 | 3961.747212 ± 13.633369 | 644.227212 ± 3.900435 | 2970.962179 ± 3.639336 | 1255.714441 ± 2.347436 |
lua-5.1 | 1269.630061 ± 1.870258 | 453.926534 ± 2.726461 | 2098.621698 ± 2.174081 | 702.495539 ± 1.167681 |
gjs-1.82.1 | 6296.648035 ± 4.253211 | 1071.168572 ± 5.486718 | 2055.754892 ± 1.025071 | N/A |
root-6.34.04 | 86.518659 ± 0.602703 | 350.379146 ± 1.674310 | 24.370312 ± 0.174186 | N/A |
luajit-2.1.17 | 331.189776 ± 0.197704 | 186.639189 ± 2.350201 | 479.280251 ± 0.530742 | 203.152896 ± 1.063096 |
php-8.0.30 | 1502.370196 ± 1.061911 | N/A | 473.953242 ± 0.160440 | N/A |
java-8 | 3.304341 ± 0.055865 | 23.330780 ± 0.673792 | 9.668329 ± 0.132109 | 16.858895 ± 0.079642 |
java-11 | 4.799246 ± 0.090609 | 29.248166 ± 0.273656 | 12.488790 ± 1.198363 | 17.025452 ± 0.067579 |
java-21 | 20.402550 ± 32.545856 | 24.561253 ± 0.678723 | 14.203131 ± 1.451524 | 20.962986 ± 0.750061 |
nodejs-18 | 5720.840235 ± 2.966322 | 557.281874 ± 0.658642 | 1907.709117 ± 0.996604 | N/A |




.
Общие графики результатов:
Также эти графики можно найти тут:




Без итогов
Целью данных тестов было показать, какой язык для разработки более предпочтителен на Эльбрусах, если важна скорость выполнения. Выводы делайте сами.
Лично я слегка удивлен результатами. Но, напоминаю, Эльбрусы очень чувствительны к оптимизации, поэтому отдельно я провел тестирование для оптимизированных тестов. Скоро опубликую результаты, поверьте, там есть над чем призадуматься.