Интринсиками как Рамиль выше написал.
Вот что в мане lcc есть с этим связанное: Опции для имитации поведения gcc под x86 -mmmx Включить предопределённый макрос __MMX__ Остальная часть описания касается всех опций из данной группы В gcc данные опции отвечают за то, что в x86 называется "расширение системы команд". Это операции, которые формально НЕ включены в состав базовой системы команд и находятся в статусе "расширения". Это означает, что набор операций может отсутствовать на некоторых моделях процессора, даже в том случае, если на других моделях процессоров данной серии набор операций присутствует. Опции выполняют две функции: разрешают компилятору самостоятельно использовать набор операций из соответствующего расширения и включают соответствующий предопределённый макрос Когда речь идёт о процессорах "Эльбрус", то в них нет такого понятия, как "расширение системы команд", поэтому нет необходимости управлять компилятором в вопросе расширений. Таким образом, для компилятора "Эльбрус" данные опции не имеют никакого смысла. Они поддержаны только ради того, чтобы не спотыкаться об уже имеющиеся опции, которые по умолчанию включены в систему сборки некоторых программ. Напрямую сравнивать поведение опций в lcc и в gcc нельзя, т.к. поведение некоторых опций в gcc зависит от конкретной сборки gcc. Зависимость растёт от того, под какой минимальный процессор x86 сконфигурирован gcc. В зависимости от этого часть данных опций будет включена по умолчанию. А поведение некоторых опций зависит от того, включена опция по умолчанию, или нет. В случае lcc компилятор настроен таким образом, что все опции данной группы по умолчанию включены. Т.е. поведение lcc соответствует поведению некоторой абстрактной версии gcc, которая сконфигурирована под максимально возможный процессор x86. Обращаем внимание на то, что действие включающих и выключающих опций НЕ симметрично (аналогично тому, как это имеет место быть на gcc под x86) Мы НЕ рекомендуем использовать опции -mno-* в качестве средства для борьбы с ошибками сборки программ. Если опция -mno-* помогла в сборке, то это в первую очередь говорит о том, что программа написана неаккуратно. Т.е. под соответствующий макрос был внесён какой-то код, напрямую к данному макросу отношения не имеющий. Более правильным вариантом было бы исправить код. Фактически это является частью процесса портирования под "Эльбрус" программы, изначально реализованной под x86 ....
В любом случае, в патчах Блендера https://github.com/ilyakurdyukov/e2k-ports/blob/main/blender-2.93.2-e2k.patch очень странно выглядят изменения: +# elif defined(__e2k__) +# define __KERNEL_SSE__ +# define __KERNEL_SSE2__ +# define __KERNEL_SSE3__ +# define __KERNEL_SSSE3__ +# define __KERNEL_SSE41__ +# if defined(__KERNEL_SSE41__) && defined(__KERNEL_SSE__) && !defined(__e2k__) return _mm_cvtss_f32(_mm_dp_ps(a, b, 0x7F)); # else return a.x * b.x + a.y * b.y + a.z * b.z; И это условие все время повторяется. То есть при существовании определения e2k будут использоваться неоптимизированные вычисления. Или предполагается что сам компилятор сможет правильно соптимизировать?
Я тоже не люблю, когда жёстко прописывают такие значения. Предпочитаю, перед подтверждением наличия у е2к всяких MMX/SSE/AVX и т.п. сначала проверить, а возведён ли соответствующий флаг у компилятора mcst-lcc. Это несколько увеличивает код, но помогает избежать ситуации, когда в целях отладки отключают все макросы MMX/SSE/AVX, а ПО всё равно компилируется с Intel Intrisics. Пример: - https://github.com/AcademySoftwareFoundation/openexr/pull/862/files#diff-34fbf0620a7405fc82efe2f145e980e1b9aa3cfe8d0d338b36c1d1bb1c32cbdd
Да, иногда говорят бывает лучше использовать Си реализацию, чем Intel Intrinsics (mcst-lcc лучше заоптимизирует). Надо смотреть по ситуации.
Согласен, часто бывает необходимо сравнить классическую версию и на интринсиках, особенно, если где-то вдруг закралась ошибка.
Кстати, есть подозрение, что x86 интринсики просто даже чаще выливаются в плохие результаты (из-за неполного соответствия). И единственный способ более-менее гарантированно выжать результат - это писать на родных e2k интринсиках, но это уже при наличии времени и реального смысла так делать. А ещё появляется смысл проверять, что компилятор справился с накруткой/раскруткой цикла и так далее.
Все равно я ничего не понял. Если есть определение __e2k__, то в условии ... && !defined(__e2k__) вместо интрисинка _mm_cvtss_f32(), который компилятор lcc должен представить в виде Эльбрусовых векторных инструкций, будут обычные вычисления с умножением и сложением. За счет чего тогда срабатывает оптимизация в Blender?
Если не e2k, то используется одна инструкция - это самый быстрый и эффективный способ на интеле. Если Эльбрус, то эквивалентной инструкции здесь нет и самый быстрый способ - руками написать код на Си, который компилятор умеет переваривать. Что важно: руками написанный код не содержит лишних проверок и упаковок. Откуда им взяться? Инструкция выполняет вычисления под маской 7F. Вот так интринсик описан на e2k, если его использовать как есть:
Обсуждают сегодня