Эльбрус/портирование: различия между версиями
(→SIMD) |
м (→SIGILL вместо ожидаемого сигнала: спасибо Илье за уточнение) |
||
(не показано 59 промежуточных версий 5 участников) | |||
Строка 1: | Строка 1: | ||
= Перенос ПО на платформу Эльбрус = | = Перенос ПО на платформу Эльбрус = | ||
''Почему? -- на e2k не будут работать программы, скомпилированные для других архитектур, например, x86 или ARM<ref>точнее, для x86 есть [[эльбрус/rtc|бинарный транслятор]], но это средство совместимости, а не нормальное явление</ref>. Зачем? -- см. [[эльбрус/faq|ЧаВо]].'' | |||
При сборке существующих программ порой возникает ряд типичных проблем и вопросов, которые отчасти систематизированы ниже (см. тж. [[эльбрус/lcc|страничку по компилятору]]). | При сборке существующих программ порой возникает ряд типичных проблем и вопросов, которые отчасти систематизированы ниже (см. тж. [[эльбрус/lcc|страничку по компилятору]]). | ||
Строка 5: | Строка 7: | ||
В [[spec|ALT RPM]] реализован макрос <tt>%e2k</tt>, рекомендуемый к применению в <tt>%ifarch</tt>. | В [[spec|ALT RPM]] реализован макрос <tt>%e2k</tt>, рекомендуемый к применению в <tt>%ifarch</tt>. | ||
Также обратите внимание: прописанные сборочные зависимости далеко не всегда минимальны, порой при сложностях стоит посмотреть, а не лучше ли на первом этапе оторвать (скажем, перенеся под <tt>%ifnarch %e2k</tt>) необязательную зависимость или отключить "ручку" (<tt>--without jit</tt>), лишившись части функциональности на конкретной архитектуре, но привнеся остальную часть. | |||
<div id="configure"></div> | |||
== configure: error: cannot guess build type; you must specify one == | == configure: error: cannot guess build type; you must specify one == | ||
Строка 10: | Строка 15: | ||
cp -aLt . -- /usr/share/automake/config.{guess,sub} | cp -aLt . -- /usr/share/automake/config.{guess,sub} | ||
autoreconf -fisv | |||
В %changelog можно добавить, например<ref>поскольку затрагивает и riscv64, и обычно aarch64</ref>: | В %changelog можно добавить, например<ref>поскольку затрагивает и riscv64, и обычно aarch64</ref>: | ||
Строка 15: | Строка 21: | ||
- fix build on newer arches | - fix build on newer arches | ||
Если апстрим без [http://github.com/microsoft/DirectXShaderCompiler/pull/4690#issuecomment-1263787487 GPL3-фобии] -- стоит предложить обновить сразу там. | |||
<div id="boost"></div> | |||
== configure: error: invalid value: boost_major_version='<i></i>' == | |||
Препроцессор из [[lcc]] 1.25 может добавить лишний пробел в препроцессированный исходник, что ломает тест версии библиотеки boost, входящий в некоторые применяющие autoconf программы с типичной диагностикой (mcst#6826): | |||
checking for Boost's header version... | |||
configure: error: invalid value: boost_major_version='' | |||
Обход -- смягчение регулярного выражения перед запуском {{cmd|configure}} (либо в {{path|m4/boost.m4}} или ином его источнике перед запуском {{cmd|autoreconf}}; поправьте команду по месту): | |||
# lcc's cpp adds an extra space breaking this regex | |||
sed -r -i 's,\^boost(.)lib(.)version,boost\1lib\2version,' configure m4/boost.m4 | |||
<div id="64LE"></div> | |||
== тесты на порядок байтов/битность == | == тесты на порядок байтов/битность == | ||
Нередко попадаются программы, которые интересует только длина указателей (размер integer) и, возможно, endianness; поскольку e2k -- [[эльбрус/архитектура|64-разрядная LE-архитектура]], ищем подстроку вроде <tt>__amd64__</tt>, читаем контекст, добавляем аналогично <tt>__e2k__</tt>. | Нередко попадаются программы, которые интересует только длина указателей (размер integer) и, возможно, endianness; поскольку e2k -- [[эльбрус/архитектура|64-разрядная LE-архитектура]], ищем подстроку вроде <tt>__amd64__</tt>, читаем контекст, добавляем аналогично <tt>__e2k__</tt>. | ||
Про невыровненный доступ к памяти на версиях архитектуры [http://ftp.altlinux.org/pub/people/mike/elbrus/docs/elbrus_prog/html/chapter6.html#reference-prefetch до пятой включительно] ("Эльбрус-8СВ"<!-- http://sdelanounas.ru/blogs/136426/ -->) известно, что он [http://ftp.altlinux.org/pub/people/mike/elbrus/docs/elbrus_prog/html/chapter9.html#id6 достаточно дорогой]; поэтому про unaligned access интересующемуся коду на <e2kv6 можно сообщить, что таковой отсутствует. | |||
Размер строки кэша L1 -- 32 байта, L2+ -- 64 байта; судя по патчам МЦСТ и поведению <tt>-fprefetch</tt>, указывать следует именно 64. | |||
Размеры типов можно глянуть в [http://bug1554614.bmoattachments.org/attachment.cgi?id=9174114 патче к nspr]. | |||
=== cmake === | |||
В альтовых пакетах на cmake исправления проверок битности порой выглядят примерно так<ref>либо можно задействовать <tt>%_libsuff</tt></ref>: | В альтовых пакетах на cmake исправления проверок битности порой выглядят примерно так<ref>либо можно задействовать <tt>%_libsuff</tt></ref>: | ||
Строка 28: | Строка 59: | ||
- fixed build on 64-bit architectures | - fixed build on 64-bit architectures | ||
=== boost === | |||
%ifarch %e2k ppc64le riscv64 | В проектах на boost порой попадается тот [https://www.gnu.org/software/autoconf-archive/ax_boost_base.html {{path|ax_boost_base.m4}}], где в проверку на lib64 забит список архитектур; его придётся поправить перед запуском {{cmd|autoreconf}}<ref>или найти этот фрагмент в уже сгенерированном configure, что несколько сложней</ref> как-то так: | ||
sed -i 's,aarch64,&| | |||
%ifarch %e2k ppc64le riscv64 loongarch64 | |||
sed -i 's,aarch64,&|e2k|ppc64le|riscv64|loongarch64,' m4/ax_boost_base.m4 | |||
%endif | %endif | ||
...после чего не забываем {{cmd|autoreconf -fisv}} (или {{cmd|%autoreconf}}). | |||
Обратите внимание: характерная диагностика <tt>configure: error: Could not link against -l...</tt> может наводить на ложный след, если сбоит тест boost (но не приводит к останову), а вываливается тест последующей библиотеки. | |||
В альтовом пакете {{pkg|autoconf-archive}} это исправлено начиная со сборки 2019.01.06-alt1.1 в p9_e2k и с [http://github.com/autoconf-archive/autoconf-archive/pull/222 2021.02.19-alt1] -- в p10_e2k и sisyphus_e2k. | |||
== SIMD == | == SIMD == | ||
Строка 42: | Строка 79: | ||
# заменяем <tt>defined __x86_64__</tt> на <tt>defined __x86_64__ || defined __e2k__</tt>; | # заменяем <tt>defined __x86_64__</tt> на <tt>defined __x86_64__ || defined __e2k__</tt>; | ||
# если попадается динамическая проверка наличия MMX/SSE, то указываем, что у нас всё есть до SSE4.1<ref>расширения системы команд SSE4.2 и AVX1 в каком-то виде также поддержаны в компиляторе, но, возможно, быстрее не будет</ref>; | # если попадается динамическая проверка наличия MMX/SSE, то указываем, что у нас всё есть до SSE4.1<ref>расширения системы команд SSE4.2 и AVX1 в каком-то виде также поддержаны в компиляторе, но, возможно, быстрее не будет</ref>; | ||
# к asm-вставкам нужно творчески подходить, но чаще проще готовый generic-вариант кода использовать. | # к asm-вставкам нужно творчески подходить<ref><tt><ilyakurdyukov> Такое:</tt> | ||
<source lang="c"> | |||
inline int getCSR() | |||
{ | |||
int result; | |||
asm volatile("stmxcsr %0" : "=m" (result)); | |||
return result; | |||
} | |||
inline void setCSR(int a) | |||
{ | |||
int temp = a; | |||
asm volatile("ldmxcsr %0" : : "m" (temp)); | |||
} | |||
</source> | |||
<tt>лучше делать так:</tt> | |||
<source lang="c"> | |||
inline int getCSR() { return _mm_getcsr(); } | |||
inline void setCSR(int a) { _mm_setcsr(a); } | |||
</source> | |||
<tt>но надо добавить заголовок для работы интринсиков:</tt> | |||
<source lang="c">#include <xmmintrin.h></source> | |||
</ref>, но чаще проще готовый generic-вариант кода использовать. | |||
См. тж. проект [https://github.com/simd-everywhere/simde/pull/700 SIMD Everywhere]. | |||
{{note|несмотря на некоторую аппаратную поддержку выполнения SIMD-инструкций, по сути они реализуются в компиляторе и осмысленность задействования может отличаться от проекта к проекту -- возможно замедление, особенно на AVX* и [[эльбрус/архитектура|архитектурах]] ранее e2kv5.}} | {{note|несмотря на некоторую аппаратную поддержку выполнения SIMD-инструкций, по сути они реализуются в компиляторе и осмысленность задействования может отличаться от проекта к проекту -- возможно замедление, особенно на AVX* и [[эльбрус/архитектура|архитектурах]] ранее e2kv5.}} | ||
{{note|если собираемый код замечает {{path|arm_neon.h}} и радостно пытается собрать ARM-ассемблер -- сообщите ему в явном виде, что NEON нет: в любом случае этот набор интринсиков хуже конвертируется в итоговые вектора на e2k.}} | |||
== компилятор/архитектура== | |||
__e2k__ | Имейте в виду при выписывании <tt>#ifdef</tt>: | ||
— это архитектура | * <tt>__e2k__</tt> — это [http://github.com/a1batross/predef/blob/master/Architectures.md#elbrus архитектура]; | ||
__LCC__ | * <tt>__LCC__</tt> — [http://github.com/a1batross/predef/blob/master/Compilers.md#mcst-lcc компилятор]; | ||
— компилятор. | * <tt>__MCST__</tt> — поставщик и того, и другого (макрос взводится с ветки lcc 1.25). | ||
Во-первых, lcc есть не только для e2k (привет sparc), поэтому если делается патч под особенности lcc | Во-первых, lcc есть не только для e2k (привет sparc), поэтому если делается патч под особенности lcc, то правильнее использовать <tt>__LCC__</tt> (скорее даже <tt>__EDG__</tt> для фронтенда edg, можно проверить на старых ICC тем же [http://godbolt.org/ Compiler Explorer]); обратите внимание, порой уже есть <tt>#ifdef __ICC</tt> либо <tt>#if defined(__INTEL_COMPILER)</tt>, в каковом случае можно либо пропатчить, расширив список, либо взвести проверяемое: | ||
__LCC__ | |||
. | |||
%add_optflags -D__ICC | |||
Поэтому мне представляется правильным архитектурно-зависимые изменения в e2k заворачивать, а компиляторо-зависимые в LCC. Понятно, что в реальной жизни их отличить не всегда просто. - @bircoph | Во-вторых, со временем на e2k появляются и другие компиляторы, например, clang через соответствующий бэкенд на основе lcc. И у них уже может не быть макроса <tt>__LCC__</tt>, а вот <tt>__e2k__</tt> есть. | ||
Поэтому мне представляется правильным архитектурно-зависимые изменения в e2k заворачивать, а компиляторо-зависимые в LCC. Понятно, что в реальной жизни их отличить не всегда просто. - @bircoph<ref>соображения коллег из МЦСТ (#8192#22):<br/><tt>lcc: __EDG__ __GNUC__ __LCC__ __MCST__<br/> | |||
clang: __clang__ __LCC__ __MCST__<br/> | |||
gcc: __GNUC__ __MCST__</tt><br/> | |||
...плюс архитектурнозависимый</ref><!-- А мб лучше не __LCC__ , а __LCC_MCST__ ) // ну это точно не сюда... mike@ --> | |||
<div id="makecontext"></div> | |||
== отсутствие makecontext() == | == отсутствие makecontext() == | ||
<!-- 2320, А.Ф. --> | <!-- 2320, А.Ф.; 5696, Д.К. --> | ||
На Эльбрусах <tt>makecontext_e2k()</tt> выделяет память под дополнительные стеки, поэтому если просто заменить <tt>s/makecontext/makecontext_e2k/</tt>, в программе может появиться утечка памяти. Нужно ещё поставить вызов <tt>freecontext_e2k()</tt> там, где выделенный для <tt>makecontext_e2k()</tt> <tt>ucp.uc_stack</tt> перестаёт использоваться под данный контекст, т.е. где: | На Эльбрусах <tt>makecontext_e2k()</tt> выделяет память под дополнительные стеки, поэтому если просто заменить <tt>s/makecontext/makecontext_e2k/</tt>, в программе может появиться утечка памяти. Нужно ещё поставить вызов <tt>freecontext_e2k()</tt> там, где выделенный для <tt>makecontext_e2k()</tt> <tt>ucp.uc_stack</tt> перестаёт использоваться под данный контекст, т.е. где: | ||
# <tt>ucp.uc_stack</tt> освобождается через <tt>free()</tt>; | # <tt>ucp.uc_stack</tt> освобождается через <tt>free()</tt>; | ||
# <tt>ucp.uc_stack</tt> переиспользуется, например, под другой контекст. | # <tt>ucp.uc_stack</tt> переиспользуется, например, под другой контекст. | ||
Должна стоять проверка на makecontext < 0: makecontext_e2k() возвращает значение int, а не void. Значение вызова необходимо проверять на статус ошибки (< 0). | |||
Если речь про coroutines, надо уходить с fcontext на портабельную вещь, поддерживающую ucontext-e2k (например, [https://github.com/taisei-project/koishi koishi]). <!-- со слов Игоря Молчанова по мотивам упоминания проблемы сборки kicad --> | |||
<div id="cpuid"></div> | |||
== отсутствие cpuid.h == | == отсутствие cpuid.h == | ||
: ''См. тж. [http://wiki.elbrus.ru/CPU_id wiki.elbrus.ru/CPU_id], mcst#6511, mcst#8456'' | |||
Обуславливаем соответствующий <tt>#include</tt> и обращения к функциям так: | Обуславливаем соответствующий <tt>#include</tt> и обращения к функциям так: | ||
Строка 76: | Строка 145: | ||
При необходимости подробного различения процессоров "Эльбрус" обратите внимание на <tt>__builtin_cpu_is()</tt>; в [[lcc]] от 1.23.23 и 1.24.10 должны быть доступны более удобные <tt>__builtin_cpu_name()</tt> и <tt>__builtin_cpu_arch()</tt> (#4484). | При необходимости подробного различения процессоров "Эльбрус" обратите внимание на <tt>__builtin_cpu_is()</tt>; в [[lcc]] от 1.23.23 и 1.24.10 должны быть доступны более удобные <tt>__builtin_cpu_name()</tt> и <tt>__builtin_cpu_arch()</tt> (#4484). | ||
Есть альтернативный способ -- через чтение регистра IDR, что позволяет определить модель процессора если код скомпилирован под другой процессор и даже если недоступен <tt>/proc/cpuinfo</tt>. | |||
Для него написана [https://git.mentality.rip/OpenE2K/e2k-libs/src/branch/master/cpuid/e2k_cpuid.h маленькая библиотека под WTFPL]. | |||
<div id="rdtsc"></div> | |||
== rdtsc == | |||
Вместо ассемблерных вставок для обращения к TSC [http://github.com/ilyakurdyukov/e2k-ports#rdtsc применяйте интринсик]: | |||
<source lang="c"> | |||
#include <x86intrin.h> | |||
uint64_t time = __rdtsc(); | |||
// same: unsigned aux; uint64_t time = __rdtscp(&aux); | |||
</source> | |||
<div id="sigsegv"></div><div id="SIGILL"></div> | |||
== SIGILL вместо ожидаемого сигнала == | |||
Обратите внимание, что на e2k в некоторых случаях можно получить SIGILL (Illegal instruction) вместо ожидаемого SIGSEGV (Segmentation fault), SIGBUS (Bus error) или SIGFPE (Floating point exception). | |||
Не тот сигнал приходит, как правило, либо в результате работы оптимизаций, либо при ручном написании кода на ассемблере или ассемблерных вставках<ref>Пример недопустимых инструкций: чтение с помощью apb по невыровненному адресу (причём проявляется только при попытке использовать значение в другой инструкции), попытка записи диагностического значения в память или использование регистра с диагностическим значением не в спекулятивном режиме. Обычно это всё невозможно отследить до исполнения (например, адрес приходит как аргумент функции). -- Дмитрий Щербаков</ref>. Если по каким-то причинам нужно поймать именно тот сигнал, который бы поймался на других архитектурах, то следует использовать режим с отключением оптимизаций, задействующих полуспекулятивный режим исполнения: | |||
* <tt>-O0</tt> -- режим компиляции без оптимизаций; | |||
* <tt>-O1</tt> -- минимальный набор оптимизаций; | |||
* <tt>-fcontrol-spec</tt> -- запрет полуспекулятивных обращений к памяти (для сохранения сингалов SIGSEGV и SIGBUS); | |||
* <tt>-fno-fp-spec</tt> -- запрет полуспекулятивных вещественных операций (для сохранения сигнала SIGFPE; в lcc >= 1.26 -- <tt>-fno-spec-fp</tt>). | |||
См. тж. [http://ftp.altlinux.org/pub/people/mike/elbrus/docs/elbrus_prog/html/chapter2.html#sigill Руководство] и {{path|posix_signals.html}}<ref>в составе {{pkg|lcc1.25-doc}} или аналогичного пакета на ОС Альт, по пути {{path|/opt/mcst/doc/posix_signals.html}} при установленной системе программирования в Эльбрус Линукс</ref>. | |||
<div id="cmake"></div> | |||
== наивные тесты в cmake == | |||
Если в каком-либо проекте на cmake вылезает "неизвестная опция", как в [https://github.com/KhronosGroup/glslang/blob/master/CMakeLists.txt#L162 glslang]: | |||
if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.0.0") | |||
add_compile_options(-Werror=deprecated-copy) | |||
endif() | |||
-- это прибитая гвоздями зависимость от gcc/clang; нужно: | |||
# проверить доступность опции и выставить переменную, см. [http://cmake.org/cmake/help/latest/module/CheckCXXCompilerFlag.html здесь]; пример: [http://github.com/erthink/libmdbx/blob/master/cmake/compiler.cmake#L37 раз], [http://github.com/erthink/libmdbx/blob/master/cmake/compiler.cmake#L231 два]; | |||
# добавить опцию, если выставлена переменная; [http://github.com/erthink/libmdbx/blob/master/cmake/compiler.cmake#L551 пример]. | |||
<div id="io.h"></div> | |||
== отсутствие sys/io.h == | |||
Данный заголовок [http://lists.altlinux.org/pipermail/devel/2018-July/204841.html существует] для небольшого количества архитектур: alpha, arm, ia64, x86, x86_64 и определяет inline-работу с портами ввода-вывода, которые только на них в этом виде и наличествуют. На всех других архитектурах (включая aarch64 и ppc64le) соответствующий код подлежит усечению (при возможности) либо переработке, если на bit banging завязана ключевая функциональность программы. | |||
<div id="waf"></div> | |||
== waf == | |||
Если для сборки предлагается старый безэльбрусный waf, добавьте в waflib/Tools/c_config.py строчку | |||
'__e2k__' : 'e2k', | |||
после описания <tt>xtensa</tt>. | |||
<div id="set_gcc_version"></div> | |||
== <tt>%set_gcc_version</tt> == | |||
Альт-специфика: если какой-либо пакет не собирается (или перестал собираться после его обновления) с чем-то вроде | |||
E: Couldn't find package gcc12-c++ | |||
-- видимо, майнтейнер обошёл слом сборки этой версии пакета на другой версии gcc в [[сизифе]] таким образом; поскольку [[lcc]] представляет из себя ''другой'' компилятор (и прикидывается, как правило, на несколько версий более старым gcc, чем в сизифе) -- на [[e2k]] есть смысл такие ограничения убрать: соберётся и заработает -- хорошо, нет -- ну нет. | |||
На том же примере [http://packages.altlinux.org/igraph igraph] это выглядит так: | |||
<source lang="diff"> | |||
+%ifarch %e2k | |||
+%define gcc_ver %nil | |||
+%else | |||
%define gcc_ver 12 | |||
+%endif | |||
</source> | |||
<source lang="diff"> | |||
+%if 0%gcc_ver | |||
export CC=gcc-%gcc_ver | |||
export CXX=g++-%gcc_ver | |||
+%endif | |||
</source> | |||
= Ссылки = | = Ссылки = | ||
* '''[[эльбрус/lcc]]''' | * '''[[эльбрус/lcc]]''' | ||
* '''[http://ftp.altlinux.org/pub/people/mike/elbrus/docs/elbrus_prog/html/chapter3.html Руководство по эффективному программированию на платформе «Эльбрус». 3. Отличия в интерфейсах]''' | * '''[http://ftp.altlinux.org/pub/people/mike/elbrus/docs/elbrus_prog/html/chapter3.html Руководство по эффективному программированию на платформе «Эльбрус». 3. Отличия в интерфейсах]''' | ||
* [https://lvee.org/ru/abstracts/303 Free software porting on the Elbrus architecture] | * '''[http://github.com/ilyakurdyukov/e2k-ports/blob/main/README.md Шпаргалка по портированию]''' | ||
* [http://0x1.tv/20190827B Особенности портирования СПО на Эльбрус] (Андрей Савченко, OSSDEVCONF-2019) | ** [[эльбрус/ptr128|Заметки по защищённому режиму]] ''(ptr128)'' | ||
* Портирование Embox: [https://habr.com/ru/company/embox/blog/421441/], [https://habr.com/ru/company/embox/blog/447704/], [https:// | * Доклады из серии [http://0x1.tv/Категория:ALTLinux_на_Эльбрусе ALT на e2k]: | ||
** [https://lvee.org/ru/abstracts/303 Free software porting on the Elbrus architecture] | |||
** [http://0x1.tv/20190827B Особенности портирования СПО на Эльбрус] (Андрей Савченко, OSSDEVCONF-2019) | |||
* Портирование Embox: | |||
** [https://habr.com/ru/company/embox/blog/421441/ Embox начинает восхождение на Эльбрус 2018], | |||
** [https://habr.com/ru/company/embox/blog/447704/ Восхождение на Эльбрус — Разведка боем. Техническая Часть 1. Регистры, стеки и другие технические детали 2019], | |||
** [https://habr.com/ru/company/embox/blog/447744/ Восхождение на Эльбрус — Разведка боем. Техническая Часть 2. Прерывания, исключения, системный таймер 2019] | |||
** [https://habr.com/ru/company/embox/blog/485694/ Embox на процессоре Эльбрус. Или никогда не забывайте о том, что получили при разведке 2020] | |||
* [http://jug.ru/talks/jbreak-2016/java-on-elbrus/ Портирование Java на Эльбрус] | |||
* [https://habr.com/ru/company/jugru/blog/419155/ Портирование JS на Эльбрус] | * [https://habr.com/ru/company/jugru/blog/419155/ Портирование JS на Эльбрус] | ||
* [https://www.youtube.com/watch?v=f_hwK0zM9y8&t=29m17s Константин Трушкин: ответы на вопросы] (видео) | * [https://www.youtube.com/watch?v=f_hwK0zM9y8&t=29m17s Константин Трушкин: ответы на вопросы] (видео) | ||
* [https://www.youtube.com/watch?v=QC2OU5axEDI Yandex Day: 3. Компилятор для процессоров "Эльбрус". Алексей Маркин] (видео) | * [https://www.youtube.com/watch?v=QC2OU5axEDI Yandex Day: 3. Компилятор для процессоров "Эльбрус". Алексей Маркин] (видео) | ||
* [https://www.youtube.com/watch?v=IeT091Ej1lU Yandex Day: 4. Прикладное программирование на Эльбрусе. Антон Аникин] (видео) | * [https://www.youtube.com/watch?v=IeT091Ej1lU Yandex Day: 4. Прикладное программирование на Эльбрусе. Антон Аникин] (видео) | ||
* [http://habr.com/ru/company/gaijin/blog/533380/ Как мы переносили современные игры на процессор Эльбрус-8С] (Gaijin Entertainment) | |||
* [http://www.mcst.ru/files/52f220/590cd8/50136e/000004/ishin-loginov-vasilev-uskorenie_vychisleniy_s_ispolzovaniem_vysokoproizvoditelnyh_matematicheskih_i_multimediynyh_bibliotek_dlya_arhitektury_elbrus.pdf Ускорение вычислений с использованием высокопроизводительных математических и мультимедийных библиотек для архитектуры Эльбрус] ([[эльбрус/eml|EML]]) | |||
* [http://habr.com/ru/post/535926/ Это непростое условное выполнение] | |||
* [http://forum.elbrus.ru/viewtopic.php?f=48&p=7277 Узкие места производительности Эльбрусов] ''публичный сбор запросов на оптимизацию подсистем'' | |||
* [http://github.com/ilyakurdyukov/e2k-ports/blob/main/README.md Elbrus porting cheat sheet] | |||
* [[эльбрус/оптимизация]] | |||
* [http://vk.com/@erthink-net-softa-pod-elbrus Нет софта под Эльбрус?] | |||
* [http://habr.com/ru/company/ascon/blog/696740/ Опыт портирования геометрического ядра C3D на платформу «Эльбрус»] | |||
* [http://habr.com/ru/company/rostelecom/blog/562858/ История портирования Reindexer'а – как покорить Эльбрус за 11 дней] | |||
* [http://habr.com/ru/company/ashmanov_net/blog/469033/ Запуск на Эльбрусе платформы для нейросетей PuzzleLib] | |||
* [[эльбрус/отладка]] | |||
* неофициальное: [http://github.com/nrdmn/elbrus-docs elbrus-docs.git], [https://docs.google.com/spreadsheets/d/1p8K5DWBsXTpCDk3TdFwMaTFIadAFuxaH4zfnJXUxK94/edit e2k_opcodes] | |||
* [[Ports/loongarch64/Porting|коллеги с loongarch64]] | |||
= Примечания = | = Примечания = |
Текущая версия от 11:14, 5 июля 2024
Перенос ПО на платформу Эльбрус
Почему? -- на e2k не будут работать программы, скомпилированные для других архитектур, например, x86 или ARM[1]. Зачем? -- см. ЧаВо.
При сборке существующих программ порой возникает ряд типичных проблем и вопросов, которые отчасти систематизированы ниже (см. тж. страничку по компилятору).
В ALT RPM реализован макрос %e2k, рекомендуемый к применению в %ifarch.
Также обратите внимание: прописанные сборочные зависимости далеко не всегда минимальны, порой при сложностях стоит посмотреть, а не лучше ли на первом этапе оторвать (скажем, перенеся под %ifnarch %e2k) необязательную зависимость или отключить "ручку" (--without jit), лишившись части функциональности на конкретной архитектуре, но привнеся остальную часть.
configure: error: cannot guess build type; you must specify one
В архив исходников программы включены устаревшие копии этих файлов, поддержка e2k добавлена в gnu-config в 2015 году; достаточно обновить их вручную из свежей системной версии этого пакета или automake (который с большей вероятностью окажется под рукой) либо выполнить autoreconf -fisv:
cp -aLt . -- /usr/share/automake/config.{guess,sub} autoreconf -fisv
В %changelog можно добавить, например[2]:
- fix build on newer arches
Если апстрим без GPL3-фобии -- стоит предложить обновить сразу там.
configure: error: invalid value: boost_major_version=''
Препроцессор из lcc 1.25 может добавить лишний пробел в препроцессированный исходник, что ломает тест версии библиотеки boost, входящий в некоторые применяющие autoconf программы с типичной диагностикой (mcst#6826):
checking for Boost's header version... configure: error: invalid value: boost_major_version=
Обход -- смягчение регулярного выражения перед запуском configure (либо в m4/boost.m4 или ином его источнике перед запуском autoreconf; поправьте команду по месту):
# lcc's cpp adds an extra space breaking this regex sed -r -i 's,\^boost(.)lib(.)version,boost\1lib\2version,' configure m4/boost.m4
тесты на порядок байтов/битность
Нередко попадаются программы, которые интересует только длина указателей (размер integer) и, возможно, endianness; поскольку e2k -- 64-разрядная LE-архитектура, ищем подстроку вроде __amd64__, читаем контекст, добавляем аналогично __e2k__.
Про невыровненный доступ к памяти на версиях архитектуры до пятой включительно ("Эльбрус-8СВ") известно, что он достаточно дорогой; поэтому про unaligned access интересующемуся коду на <e2kv6 можно сообщить, что таковой отсутствует.
Размер строки кэша L1 -- 32 байта, L2+ -- 64 байта; судя по патчам МЦСТ и поведению -fprefetch, указывать следует именно 64.
Размеры типов можно глянуть в патче к nspr.
cmake
В альтовых пакетах на cmake исправления проверок битности порой выглядят примерно так[3]:
-%ifarch x86_64 +%if "%_lib" == "lib64" export LIB_SUFFIX=64 %endif
- fixed build on 64-bit architectures
boost
В проектах на boost порой попадается тот ax_boost_base.m4, где в проверку на lib64 забит список архитектур; его придётся поправить перед запуском autoreconf[4] как-то так:
%ifarch %e2k ppc64le riscv64 loongarch64 sed -i 's,aarch64,&|e2k|ppc64le|riscv64|loongarch64,' m4/ax_boost_base.m4 %endif
...после чего не забываем autoreconf -fisv (или %autoreconf).
Обратите внимание: характерная диагностика configure: error: Could not link against -l... может наводить на ложный след, если сбоит тест boost (но не приводит к останову), а вываливается тест последующей библиотеки.
В альтовом пакете autoconf-archive это исправлено начиная со сборки 2019.01.06-alt1.1 в p9_e2k и с 2021.02.19-alt1 -- в p10_e2k и sisyphus_e2k.
SIMD
Алгоритм портирования таких программ простой:
- ищем в исходниках макрос __x86_64__[5] или на худой конец i386; если они покрывают фрагменты кода с SIMD-интринсиками (функции, имена которых начинаются на _mm_), то нам повезло;
- заменяем defined __x86_64__ на defined __x86_64__ || defined __e2k__;
- если попадается динамическая проверка наличия MMX/SSE, то указываем, что у нас всё есть до SSE4.1[6];
- к asm-вставкам нужно творчески подходить[7], но чаще проще готовый generic-вариант кода использовать.
См. тж. проект SIMD Everywhere.
компилятор/архитектура
Имейте в виду при выписывании #ifdef:
- __e2k__ — это архитектура;
- __LCC__ — компилятор;
- __MCST__ — поставщик и того, и другого (макрос взводится с ветки lcc 1.25).
Во-первых, lcc есть не только для e2k (привет sparc), поэтому если делается патч под особенности lcc, то правильнее использовать __LCC__ (скорее даже __EDG__ для фронтенда edg, можно проверить на старых ICC тем же Compiler Explorer); обратите внимание, порой уже есть #ifdef __ICC либо #if defined(__INTEL_COMPILER), в каковом случае можно либо пропатчить, расширив список, либо взвести проверяемое:
%add_optflags -D__ICC
Во-вторых, со временем на e2k появляются и другие компиляторы, например, clang через соответствующий бэкенд на основе lcc. И у них уже может не быть макроса __LCC__, а вот __e2k__ есть.
Поэтому мне представляется правильным архитектурно-зависимые изменения в e2k заворачивать, а компиляторо-зависимые в LCC. Понятно, что в реальной жизни их отличить не всегда просто. - @bircoph[8]
отсутствие makecontext()
На Эльбрусах makecontext_e2k() выделяет память под дополнительные стеки, поэтому если просто заменить s/makecontext/makecontext_e2k/, в программе может появиться утечка памяти. Нужно ещё поставить вызов freecontext_e2k() там, где выделенный для makecontext_e2k() ucp.uc_stack перестаёт использоваться под данный контекст, т.е. где:
- ucp.uc_stack освобождается через free();
- ucp.uc_stack переиспользуется, например, под другой контекст.
Должна стоять проверка на makecontext < 0: makecontext_e2k() возвращает значение int, а не void. Значение вызова необходимо проверять на статус ошибки (< 0).
Если речь про coroutines, надо уходить с fcontext на портабельную вещь, поддерживающую ucontext-e2k (например, koishi).
отсутствие cpuid.h
- См. тж. wiki.elbrus.ru/CPU_id, mcst#6511, mcst#8456
Обуславливаем соответствующий #include и обращения к функциям так:
#if defined(__i386__) || defined(__x86_64__)
...не забывая добавить подходящую по смыслу заглушку вместо результата функции.
При необходимости подробного различения процессоров "Эльбрус" обратите внимание на __builtin_cpu_is(); в lcc от 1.23.23 и 1.24.10 должны быть доступны более удобные __builtin_cpu_name() и __builtin_cpu_arch() (#4484).
Есть альтернативный способ -- через чтение регистра IDR, что позволяет определить модель процессора если код скомпилирован под другой процессор и даже если недоступен /proc/cpuinfo. Для него написана маленькая библиотека под WTFPL.
rdtsc
Вместо ассемблерных вставок для обращения к TSC применяйте интринсик:
#include <x86intrin.h>
uint64_t time = __rdtsc();
// same: unsigned aux; uint64_t time = __rdtscp(&aux);
SIGILL вместо ожидаемого сигнала
Обратите внимание, что на e2k в некоторых случаях можно получить SIGILL (Illegal instruction) вместо ожидаемого SIGSEGV (Segmentation fault), SIGBUS (Bus error) или SIGFPE (Floating point exception).
Не тот сигнал приходит, как правило, либо в результате работы оптимизаций, либо при ручном написании кода на ассемблере или ассемблерных вставках[9]. Если по каким-то причинам нужно поймать именно тот сигнал, который бы поймался на других архитектурах, то следует использовать режим с отключением оптимизаций, задействующих полуспекулятивный режим исполнения:
- -O0 -- режим компиляции без оптимизаций;
- -O1 -- минимальный набор оптимизаций;
- -fcontrol-spec -- запрет полуспекулятивных обращений к памяти (для сохранения сингалов SIGSEGV и SIGBUS);
- -fno-fp-spec -- запрет полуспекулятивных вещественных операций (для сохранения сигнала SIGFPE; в lcc >= 1.26 -- -fno-spec-fp).
См. тж. Руководство и posix_signals.html[10].
наивные тесты в cmake
Если в каком-либо проекте на cmake вылезает "неизвестная опция", как в glslang:
if(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS "9.0.0") add_compile_options(-Werror=deprecated-copy) endif()
-- это прибитая гвоздями зависимость от gcc/clang; нужно:
- проверить доступность опции и выставить переменную, см. здесь; пример: раз, два;
- добавить опцию, если выставлена переменная; пример.
отсутствие sys/io.h
Данный заголовок существует для небольшого количества архитектур: alpha, arm, ia64, x86, x86_64 и определяет inline-работу с портами ввода-вывода, которые только на них в этом виде и наличествуют. На всех других архитектурах (включая aarch64 и ppc64le) соответствующий код подлежит усечению (при возможности) либо переработке, если на bit banging завязана ключевая функциональность программы.
waf
Если для сборки предлагается старый безэльбрусный waf, добавьте в waflib/Tools/c_config.py строчку
'__e2k__' : 'e2k',
после описания xtensa.
%set_gcc_version
Альт-специфика: если какой-либо пакет не собирается (или перестал собираться после его обновления) с чем-то вроде
E: Couldn't find package gcc12-c++
-- видимо, майнтейнер обошёл слом сборки этой версии пакета на другой версии gcc в сизифе таким образом; поскольку lcc представляет из себя другой компилятор (и прикидывается, как правило, на несколько версий более старым gcc, чем в сизифе) -- на e2k есть смысл такие ограничения убрать: соберётся и заработает -- хорошо, нет -- ну нет.
На том же примере igraph это выглядит так:
+%ifarch %e2k
+%define gcc_ver %nil
+%else
%define gcc_ver 12
+%endif
+%if 0%gcc_ver
export CC=gcc-%gcc_ver
export CXX=g++-%gcc_ver
+%endif
Ссылки
- эльбрус/lcc
- Руководство по эффективному программированию на платформе «Эльбрус». 3. Отличия в интерфейсах
- Шпаргалка по портированию
- Заметки по защищённому режиму (ptr128)
- Доклады из серии ALT на e2k:
- Free software porting on the Elbrus architecture
- Особенности портирования СПО на Эльбрус (Андрей Савченко, OSSDEVCONF-2019)
- Портирование Embox:
- Embox начинает восхождение на Эльбрус 2018,
- Восхождение на Эльбрус — Разведка боем. Техническая Часть 1. Регистры, стеки и другие технические детали 2019,
- Восхождение на Эльбрус — Разведка боем. Техническая Часть 2. Прерывания, исключения, системный таймер 2019
- Embox на процессоре Эльбрус. Или никогда не забывайте о том, что получили при разведке 2020
- Портирование Java на Эльбрус
- Портирование JS на Эльбрус
- Константин Трушкин: ответы на вопросы (видео)
- Yandex Day: 3. Компилятор для процессоров "Эльбрус". Алексей Маркин (видео)
- Yandex Day: 4. Прикладное программирование на Эльбрусе. Антон Аникин (видео)
- Как мы переносили современные игры на процессор Эльбрус-8С (Gaijin Entertainment)
- Ускорение вычислений с использованием высокопроизводительных математических и мультимедийных библиотек для архитектуры Эльбрус (EML)
- Это непростое условное выполнение
- Узкие места производительности Эльбрусов публичный сбор запросов на оптимизацию подсистем
- Elbrus porting cheat sheet
- эльбрус/оптимизация
- Нет софта под Эльбрус?
- Опыт портирования геометрического ядра C3D на платформу «Эльбрус»
- История портирования Reindexer'а – как покорить Эльбрус за 11 дней
- Запуск на Эльбрусе платформы для нейросетей PuzzleLib
- эльбрус/отладка
- неофициальное: elbrus-docs.git, e2k_opcodes
- коллеги с loongarch64
Примечания
- ↑ точнее, для x86 есть бинарный транслятор, но это средство совместимости, а не нормальное явление
- ↑ поскольку затрагивает и riscv64, и обычно aarch64
- ↑ либо можно задействовать %_libsuff
- ↑ или найти этот фрагмент в уже сгенерированном configure, что несколько сложней
- ↑ или же __amd64__
- ↑ расширения системы команд SSE4.2 и AVX1 в каком-то виде также поддержаны в компиляторе, но, возможно, быстрее не будет
- ↑ <ilyakurdyukov> Такое:
inline int getCSR() { int result; asm volatile("stmxcsr %0" : "=m" (result)); return result; } inline void setCSR(int a) { int temp = a; asm volatile("ldmxcsr %0" : : "m" (temp)); }
лучше делать так:
inline int getCSR() { return _mm_getcsr(); } inline void setCSR(int a) { _mm_setcsr(a); }
но надо добавить заголовок для работы интринсиков:
#include <xmmintrin.h>
- ↑ соображения коллег из МЦСТ (#8192#22):
lcc: __EDG__ __GNUC__ __LCC__ __MCST__
clang: __clang__ __LCC__ __MCST__
gcc: __GNUC__ __MCST__
...плюс архитектурнозависимый - ↑ Пример недопустимых инструкций: чтение с помощью apb по невыровненному адресу (причём проявляется только при попытке использовать значение в другой инструкции), попытка записи диагностического значения в память или использование регистра с диагностическим значением не в спекулятивном режиме. Обычно это всё невозможно отследить до исполнения (например, адрес приходит как аргумент функции). -- Дмитрий Щербаков
- ↑ в составе lcc1.25-doc или аналогичного пакета на ОС Альт, по пути /opt/mcst/doc/posix_signals.html при установленной системе программирования в Эльбрус Линукс