Ошибка Cortex A9 NEON против VFP
Я пытаюсь создать библиотеку для процессора Cortex A9 ARM (более OMAP4), и я немного замешана в отношении того, когда\когда использовать NEON vs VFP в контексте с плавающей запятой операций и SIMD. Следует отметить, что я знаю разницу между двумя аппаратными сопроцессорами (как также указано здесь, на SO), у меня просто есть недоразумение относительно их правильного использования.
В связи с этим я использую следующие флаговые компиляции:
GCC
-O3 -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=softfp
-O3 -mcpu=cortex-a9 -mfpu=vfpv3 -mfloat-abi=softfp
ARMCC
--cpu=Cortex-A9 --apcs=/softfp
--cpu=Cortex-A9 --fpu=VFPv3 --apcs=/softfp
Я прочитал документацию по ARM, много wiki (как этот), форумы и сообщения в блогах, и все, кажется, согласитесь, что использование NEON лучше, чем использование VFP
или, по крайней мере, смешение NEON (например, использование instrinsics для реализации некоторых альго в SIMD), а VFP - не такая хорошая идея; Я не уверен на 100%, если это применимо в контексте всей библиотеки приложений\или только к определенным местам (функциям) в коде.
Поэтому я использую neon как FPU для своего приложения, так как я также хочу использовать встроенные функции. В результате у меня немного неприятности, и я смутился о том, как лучше использовать эти функции (NEON vs VFP) на Cortex A9, только углубляется дальше, а не проясняется. У меня есть код, который выполняет бенчмаркинг для моего приложения и использует некоторые пользовательские таймерные классы
в которых вычисления основаны на двойной точности с плавающей запятой. Использование NEON в качестве FPU дает совершенно неулокальные результаты (попытка распечатать эти значения приводит к печати в основном inf и NaN, тот же код работает без сбоев при построении для x86). Поэтому я изменил свои вычисления на использование одинарной точности с плавающей запятой, поскольку документировано, что NEON не обрабатывает плавающую точку с двойной точностью. Мои тесты по-прежнему не дают правильных результатов (и, что хуже всего, теперь это больше не работает на x86, я думаю, это из-за потери точности, но я не уверен). Поэтому я почти полностью потерял: с одной стороны, я хочу использовать NEON для возможностей SIMD и использовать его, поскольку FPU не дает правильных результатов, с другой стороны, смешивание его с VFP не кажется очень хорошей идеей.
Любые советы в этой области будут очень благодарны!
Я нашел в статье в вышеупомянутой wiki сводку того, что должно быть сделано для оптимизации с плавающей запятой в контексте NEON:
"
- Использовать только одинарные точки с плавающей запятой
- Используйте NEON intrinsics/ASM, когда вы найдете функцию FP с узким местом. Вы можете сделать лучше, чем компилятор.
- Свернуть условные ветки
- Включить режим RunFast
Для softfp:
- Встроенный код с плавающей запятой (если только его очень большой)
- Передавайте аргументы FP через указатели вместо значения и выполняйте целочисленные операции между вызовами функций.
"
Я не могу сильно использовать для float ABI, поскольку я не могу связываться с библиотеками, которые у меня есть.
Большинство рекомендаций имеют смысл для меня (за исключением "режима runfast", который я не понимаю точно, что должно было делать, и того факта, что в этот момент я мог бы сделать лучше, чем компилятор), но я продолжаю получать противоречивые результаты и Я ничего не знаю прямо сейчас.
Может ли кто-нибудь пролить свет на то, как правильно использовать плавающую точку и NEON для Cortex A9/A8 и какие флаговые компиляции использовать?
Ответы
Ответ 1
Я думаю, что этот вопрос должен быть разделен на несколько, добавив некоторые примеры кода и детализированную целевую платформу и версии используемых программных цепочек.
Но чтобы покрыть одну часть путаницы:
Рекомендация "использовать NEON как FPU" звучит как недоразумение. NEON - это SIMD-движок, VFP - это FPU. Вы можете использовать NEON для операций с плавающей запятой с одинарной точностью с одновременным до четырех значений с одной точностью, что (когда это возможно) подходит для производительности.
-mfpu=neon
можно рассматривать как сокращенное обозначение -mfpu=neon-vfpv3
.
Подробнее см. http://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html.
Ответ 2
... форумы и сообщения в блогах, и все, похоже, согласны с тем, что использование NEON лучше, чем использование VFP или, по крайней мере, смешение NEON (например, использование instrinsics для реализации некоторых algos в SIMD), а VFP - не такая хорошая идея
Я не уверен, что это правильно. Согласно ARM на Представляем статью NEON Development | Регистры NEON:
Банк регистрации NEON состоит из 32 64-битных регистров. Если оба Продвинуты SIMD и VFPv3, они делят этот регистр банка. В этом случае VFPv3 реализуется в форме VFPv3-D32, которая поддерживает 32 с плавающей запятой с двойной точностью. Эта интеграция упрощает поддержку поддержки контекста контекста, поскольку те же подпрограммы, которые сохраняют и восстанавливают контекст VFP, также сохраняют и восстановить NEON-контекст.
Модуль NEON может просматривать тот же банк регистров, что и:
- шестнадцать 128-битных регистров квад- рала, Q0-Q15
- тридцать два 64-битных регистра двойных слов, D0-D31.
Регистры NEON D0-D31 совпадают с регистрами VFPv3 D0-D31 и каждый из регистров Q0-Q15 отображается на пару регистров D. На рисунке 1.3 показаны различные виды общих NEON и VFP зарегистрировать банк. Все эти взгляды доступны в любое время. Программного обеспечения не нужно явно переключаться между ними, поскольку Используемая инструкция определяет соответствующий вид.
Регистры не конкурируют; скорее они сосуществуют в качестве взглядов на банк регистров. Невозможно отключить механизм NEON и FPU.
В связи с этим я использую следующие флаговые компиляции:
-O3 -mcpu=cortex-a9 -mfpu=neon -mfloat-abi=softfp
-O3 -mcpu=cortex-a9 -mfpu=vfpv3 -mfloat-abi=softfp
Вот что я делаю; ваш пробег может отличаться. Он получен из mashup информации, собранной с платформы и компилятора.
gnueabihf
говорит мне, что платформа использует жесткие поплавки, что может ускорить процедурные вызовы. Если есть сомнения, используйте softfp
, потому что он совместим с жесткими поплавками.
BeagleBone Black:
$ gcc -v 2>&1 | grep Target
Target: arm-linux-gnueabihf
$ cat /proc/cpuinfo
model name : ARMv7 Processor rev 2 (v7l)
Features : half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpd32
...
Таким образом, BeagleBone использует:
-march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=hard
CubieTruck v5:
$ gcc -v 2>&1 | grep Target
Target: arm-linux-gnueabihf
$ cat /proc/cpuinfo
Processor : ARMv7 Processor rev 5 (v7l)
Features : swp half thumb fastmult vfp edsp thumbee neon vfpv3 tls vfpv4
Итак, CubieTruck использует:
-march=armv7-a -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard
Banana Pi Pro:
$ gcc -v 2>&1 | grep Target
Target: arm-linux-gnueabihf
$ cat /proc/cpuinfo
Processor : ARMv7 Processor rev 4 (v7l)
Features : swp half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt
Таким образом, Banana Pi использует:
-march=armv7-a -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard
Малина Pi 3:
RPI3 уникален в том, что его ARMv8, но работает под 32-разрядной ОС. Это означает его эффективную 32-битную ARM или Aarch32. Там немного больше, чем 32-бит ARM против Aarch32, но это покажет вам флаги Aarch32
Кроме того, RPI3 использует Broadcom A53 SoC, и он имеет NEON и дополнительные инструкции CRC32, но не имеет дополнительных расширений Crypto.
$ gcc -v 2>&1 | grep Target
Target: arm-linux-gnueabihf
$ cat /proc/cpuinfo
model name : ARMv7 Processor rev 4 (v7l)
Features : half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
...
Таким образом, малина Pi может использовать:
-march=armv8-a+crc -mtune=cortex-a53 -mfpu=neon-fp-armv8 -mfloat-abi=hard
Или он может использовать (я не знаю, что использовать для -mtune
):
-march=armv7-a -mfpu=neon-vfpv4 -mfloat-abi=hard
ODROID C2:
ODROID C2 использует Amlogic A53 SoC, но использует 64-битную ОС. ODROID C2, он имеет NEON и дополнительные инструкции CRC32, но не имеет дополнительных расширений Crypto (аналогичная конфигурация для RPI3).
$ gcc -v 2>&1 | grep Target
Target: aarch64-linux-gnu
$ cat /proc/cpuinfo
Features : fp asimd evtstrm crc32
Таким образом, ODROID использует:
-march=armv8-a+crc -mtune=cortex-a53
В приведенных выше рецептах я изучил процессор ARM (например, Cortex A9 или A53), проверив данные. В соответствии с этим ответом на Unix и Linux Stack Exchange, который расшифровывает выходные данные из /proc/cpuinfo
:
Часть процессора: номер детали. 0xd03 указывает процессор Cortex-A53.
Таким образом, мы сможем найти форму данных в базе данных. Я не знаю, существует ли он или где находится.
Ответ 3
Я бы держался подальше от VFP. Это точно так же, как режим Thmub: он предназначен для компиляторов. Нет смысла оптимизировать для них.
Это может показаться грубым, но я тоже не вижу смысла в неонах NEON. Это больше проблем, чем помощь - если есть.
Просто инвестируйте два или три дня в базовую сборку ARM: вам нужно только узнать несколько инструкций для управления/завершения цикла.
Затем вы можете начать писать собственные коды NEON, не беспокоясь о том, что компилятор делает что-то астральное, выплевывая множество ошибок/предупреждений.
Обучение инструкциям NEON менее требовательно, чем все эти макросы intrinsics. И все выше, результаты намного лучше.
Полностью оптимизированные исходные коды NEON обычно работают более чем в два раза быстрее, чем хорошо написанные сопоставления с внутренними интерфейсами.
Просто сравните версию OP с моим в приведенной ниже ссылке, тогда вы поймете, что я имею в виду.
Оптимизация преобразования RGBA8888 в RGB565 с помощью NEON
рассматривает