- «Искусственные люди» Samsung Neon уже работают на смартфонах
- Пользователи получат их до Рождества
- ARM NEON скининг
- Что это?
- Зачем?
- Оригинальный код на C для скининга (Matrix palette skinnig).
- ASM with NEON(xCode style)
- Результаты
- Вопросы-Ответы
- Samsung представил Neon. Как он будет работать и зачем нам свои цифровые профили
- Что такое чат-бот Neon?
- Пранав Мистри, генеральный директор Neon
- Что Neon может сделать для нас
- Как будут появляться Neon?
- Как технически устроены Neon
- Кто занимается разработкой Neon?
- Будут ли данные храниться в безопасности?
- Когда я смогу зарегистрироваться в Neon?
- Обзор инструкций ARM NEON для тех, кто знаком с MMX/SSE/AVX
- Под что вообще пишем?
- Это же ужас?
- Знакомство с NEON
- Справочник интринсиков
- Подходящая задача
- NEON-версия
- Оптимизация чтения
- Разбор сгенерированного кода
- Решение на SSE
- Моё впечатление
«Искусственные люди» Samsung Neon уже работают на смартфонах
Пользователи получат их до Рождества
На выставке CES 2020 Samsung анонсировала продукт под названием Neon, который южнокорейский производитель окрестил «искусственным человеком». В описании на официальном сайте говорится, что это «виртуальное существо, созданное с помощью вычислений, которое выглядит и ведет себя как мы». Теперь же стало известно, что Neon будет доступен на смартфонах до конца года.
Объявление было сделано в Twitter Пранавом Мистри (Pranav Mistry), генеральным директором STAR (Samsung Technology and Advanced Research) Labs. Он поделился фотографиями устройства Neon Frame, который «переносит Neon в реальный мир».
После чего известный инсайдер под ником Ice Universe, который часто публикует достоверные сведения о новинках Samsung, спросил Пранава Мистри, когда Neon появится на смартфонах. В ответ на это Мистри ответил, что Neon уже работает на его смартфоне. В данный момент проект Neon проходит стадию тестирования, а рядовые пользователи смартфонов смогут познакомиться с «искусственными людьми» лично ещё до Рождества.
Neon — не голосовой помощник или робот. Скорее, это видеочат с компьютерным человеком, который выглядит максимально реалистично. Разработчики отмечают, что общение с подобными «искусственными людьми» будет выведено на совершенно новый уровень по сравнению с традиционными персональными помощниками, а время на поиск информации и правильного ответа у Neon составляет всего несколько миллисекунд.
Источник
ARM NEON скининг
Что это?
Что такое ARM NEON? – ARM® NEON™ это SIMD движок … – другими словами это расширенный набор инструкций наподобие x86 CPU SSE/SSE2 но для процессоров с ARM архитектурой.
Зачем?
Всё и так было хорошо пока я не добавил поддержку FSAA. После этого фпс просел ниже чем 15.
После оптимизации у меня опять было около 25 FPS. Но в памяти засела одна функция которая потребляла 10% времени на кадр в которой я уже не знал что можно оптимизировать.
Благодаря одному моему другу, который время от времени задавал вопрос типа «А не хочешь ли ты задействовать NEON в своем движке» я таки решился (с его поддержкой) переписать эту функцию на NEON.
Оригинальный код на C для скининга (Matrix palette skinnig).
Структуры:
Трансформация одного веса:
Трансформация одного вертекса:
Знающий человек сразу заметит, что я храню количество не нулевых весов для каждого вертекса. В моем случае около 30% вершин были с одним весом, что позволило выиграть немножко времени.
ASM with NEON(xCode style)
Код ниже это asm/С код оптимизированный с использованием ARM NEON.
Несколько ньюансов:
- Для ARM NEON все входящие данный должны быть выровнены на 16 байт. Из за этого требования я все свои входящие позиции и нормали расширил на Vec4f.
- Исходящие данные все еще могут быть выровнены по 4 байта. Это позволило мне заливать результат напрямую в вертекс буфер без лишних телодвижений. Для варианта с выровненными данными по 16 байт пришлось бы падить данные лишними 4+4 байтами и гонять их в вертекс буффер (а это происходит на каждом кадре).
Результаты
Я не делал никаких синтетических тестов — все проверял на рабочем проекте.
502ms(c++) против 307ms(arm neon) на
10 секундном интервале для iPhone 4 (на 39% быстрее чем на С).
Вопросы-Ответы
Попытаюсь ответить сразу на несколько вопросов?
Q: А почему не описано как работает и что такое ARM NEON?
A: Пересказывать спеки смысла нет.
Q: А почему не использовать шейдера?
A: OpenGL 1.1
Q: А почему не использовать OpenGL 2.0+?
A: Только после портации на Windows Phone 8 (там нет FF и как раз в этот момент я допишу «шейдерность» в движок и потом уже и на GL 2.0).
Q: А почему не использовать GL_OES_matrix_palette для FF?
A: Надо бить модель на группы по 11 (для iphone) матриц и на это нет времени — возможно в будущем.
Q: А где можно узнать больше и желательно с примерами?
A: Я советую посмотреть Тут. Осторожно там LGPL.
Q: А сколько это заняло?
A: Неделю — именно это и есть причина написания статьи (если кому то сэкономит время я буду счастлив).
Q: Я ничего не понял, а можно по подробнее?
A: Можно (зависит от комментариев), но я старался написать очень понятный код.
Источник
Samsung представил Neon. Как он будет работать и зачем нам свои цифровые профили
Компания Samsung представила на технологической выставке CES 2020 свой новый проект Neon, в рамках которого любой желающий сможет создать собственный цифровой профиль. Компания предлагает создавать виртуальных аватаров уже существующих людей: профили будут обладать искусственным интеллектом и смогут использовать функционал чат-ботов. «Хайтек» подробно рассказывает о проекте и его возможном будущем.
Читайте «Хайтек» в
Что такое чат-бот Neon?
По словам главы подразделения Neon в Samsung Пранава Мистри, лучший способ понять, для чего нужны технологии чат-бота, — это пойти от обратного. Он не предназначен для замены или улучшения технологий, которые сейчас используются в текущем поколении цифровых помощников Siri, Alexa или Google Assistant.
Другими словами, Neon не предназначен для получения ответов на простые вопросы о погоде или пробках на дорогах. Кроме того, он не может управлять устройствами системы умного дома, устанавливать напоминания, заводить будильники и включать музыку.
Neon также не является андроидом или копией реальных людей, хотя эти цифровые профили могут иметь некоторую физическую или психологическую схожесть с настоящими людьми, отмечает Пранав Мистри. Первая волна смоделирована по образу реальных людей, но в будущем инженеры планируют создавать цифровые профили не только по настоящим образцам, но и разрабатывать чат-ботов, не идентичных реальным людям.
Neon может выступать в качестве индивидуального учителя, личного финансового консультанта, поставщика медицинских услуг, консьержа или актера, в конце концов.
Пранав Мистри, генеральный директор Neon
Чат-бот Neon — это независимое виртуальное существо, которое может демонстрировать свои эмоции и учиться на разговорах при помощи встроенного ИИ. Он предназначен для общения с людьми, запоминания информации и обучения нейросетей.
Что Neon может сделать для нас
В презентации Samsung говорится, что чат-ботов Neon можно использовать в специализированных приложениях с учетом индивидуальных потребностей пользователей. Например, цифровой профиль человека с использованием ИИ сможет обучать пользователя йоге в специальном приложении или новым словам в сервисе изучения иностранных языков.
Чат-бот также сможет взять на себя роль психолога или друга, который будет хранить все секреты.
Как будут появляться Neon?
Neon не планирует превращать цифровые аватары пользователей в физических роботов, однако в будущем их можно использовать в качестве голограммы. Пользователи смогут создавать свои профили при помощи обычной камеры на смартфонах и компьютерах.
В дальнейшем компания собирается отказаться от разработки Neon с внешностью настоящих людей. Пользователи будут регистрироваться в системе, компания проанализирует всю доступную на момент регистрации информацию о человеке, находящуюся в открытом доступе, после чего представит клиенту изображение цифрового профиля. Пользователи не смогут самостоятельно выбирать, как будет выглядеть их цифровой профиль, по аналогии с игрой Sims.
В компании такой подход объясняют тем, что им не хочется, чтобы люди начинали полностью идентифицировать себя с цифровыми профилями. При этом изначально Neon будет копировать внешность реальных людей для ускорения запуска проекта.
Вероятно, боты будут продаваться по модели подписки: то есть человек не будет полностью владеть своим цифровым профилем, а только арендовать его. Компании смогут даже нанимать цифровых людей для специализированных задач, таких как перевод или преподавание, но они не смогут получить права на полноценное использование или изменение этой технологии.
Пока не ясно, сможет ли человек постоянно иметь при себе Neon либо он будет появляться только для конкретных задач, например, для перевода непонятных слов при просмотре сериала на Netflix.
Как технически устроены Neon
В основе Neon лежит ИИ, который обучается в зависимости от типа общения пользователя. Нейросеть анализирует предпочтения человека, его модель общения и потребностей. Работать искусственный интеллект будет на технологии Spectra, про которую сейчас практически ничего не известно. Компания обещает представить технологию ближе к концу 2020 года, но некоторые эксперты считают, что Spectra может быть мощным процессором от Samsung для работы ИИ в бытовых условиях.
Визуальную работу Neon будет поддерживать графический процессор Core R3 — именно его работа ответственна за естественные движения чат-бота, речь, эмоции и отображение этих процессов в режиме реального времени.
Кто занимается разработкой Neon?
Развитием Neon занимается исследователь Samsung Пранав Мистри. Проект цифровых профилей возник из Star Labs (она же Samsung Technology и Advanced Research Labs) и финансируется Samsung, но не является частью ИТ-гиганта. Бот не предназначен для замены основного цифрового помощника Samsung — компания не планирует предустанавливать его во всех продуктах.
Сейчас Neon занимается поиском сторонних инвесторов, поскольку в будущем, несмотря на поддержку Samsung, компании придется разработать систему монетизации.
Будут ли данные храниться в безопасности?
По словам разработчиков, вся языковая обработка будет проходить исключительно на устройстве владельца, а все записи о взаимодействии человека с Neon будут храниться в секрете. В компании заверяют, что никогда не будут передавать данные о пользователях третьим лицам, даже если в них говорится о каком-то преступлении.
Когда я смогу зарегистрироваться в Neon?
Пока никакой конкретной даты компания не анонсировала. Кроме того, уже точно известно, что сейчас технически Neon не может работать — для запуска ИИ в бытовых условиях компании необходимо как минимум запустить работу процессора Spectra, его презентация должна состояться до конца 2020 года.
В работе Neon также существует несколько неясных моментов — будет ли цифровой профиль человека сохраняться при переходе на другое устройство либо пользователю придется создавать и обучать его с нуля. Кроме того, пока функционал остается достаточно скудным, а его возможности — не до конца ясны.
Neon обещает презентовать новые возможности своего ИИ и разные системы взаимодействия с пользователем до конца 2020 года.
Источник
Обзор инструкций ARM NEON для тех, кто знаком с MMX/SSE/AVX
Мир изменился. Я чувствую это в воде, чувствую это в земле, ощущаю в воздухе.
«Властелин колец», Джон Рональд Руэл Толкин
Архитектура x86 долгие десятилетия была лидером по высокопроизводительным решениям. И этот факт позволял ей доминировать даже когда количество устройств на архитектуре ARM вокруг нас стало в несколько раз больше. Если вы пишете высокопроизводительный софт для серверов или рабочих станций, до недавнего времени вы могли обходиться лишь знанием x86. Но невозможно игнорировать события, которые могут поменять расклад сил уже совсем скоро. В связи с этим я решил немного поиграться с ARM и нарыл интересных фактов немного больше, чем на тред в твитере.
У меня нет цели рассказать всё с самого начала, я буду заострять внимание лишь на отличиях и интересных моментах, которые мне встретились. Подразумевается, что вы знаете, что такое инструкции процессора, SIMD, читаете ассемблер и Си на базовом уровне.
Мой опыт написания высокопроизводительного кода в основном связан с обработкой изображений в библиотеке Pillow-SIMD. Там я использовал интринсики в коде на Си чтобы добиться 6-8-кратного ускорения наиболее частых операций.
Под что вообще пишем?
Честно говоря, это самый высокий порог для вхождения в ARM архитектуру из тех, что будут. В x86 есть базовый набор команд, есть расширения (разные версии SSE, AVX, криптография или виртуализация) и есть разрядность (32 или 64 бита). В ARM же, загибайте пальцы:
Есть архитектуры, коих более 20. Называются они примерно так: ARMv7-M, ARMv8-R, ARMv8.3-A.
Есть микроархитектура. Например: Cortex-R4, Cortex-A76.
Есть профайл: Classic, Microcontroller, Real-time, Application.
Есть разные наборы команд! A32, A64, Thumb, Thumb2.
Наконец, расширения набора команд: SIMD, NEON, SVE.
Ну и никуда не делась разрядность: AArch32 и AArch64.
Не претендуя на полноту описания, я подсвечу основные моменты и укажу, что сейчас можно опустить. Все архитектуры соответствуют одному из четырех профайлов. Classic — это прям совсем классик, такое вы вряд ли встретите. Из трёх остальных самое ходовое — это Application. Все телефоны, сервера и рабочие станции это Application. Профайл всегда отражён в названии архитектуры в виде постфикса (A, M, R). Актуальных архитектур всего две — ARMv7 и ARMv8, зато у ARMv8 вышло уже 6 минорных версий, которые тоже называются архитектурами (например, ARMv8.2-A). Причём 64-битная разрядность появилась только в ARMv8. Однако ARMv8-A не гарантирует наличие 64-битного режима у процессора, а вот ARMv8.1-A уже гарантирует.
Если знание архитектуры чипа нужно нам, разработчикам софта, чтобы знать, какой минимальный набор функциональности возможно использовать, то микроархитектура уже нужна для разработчиков чипов, чтобы знать, сколько кеша нужно насыпать, сколько вычислительных блоков должно быть и какие опциональные технологи нужно включить в чип. Причем, бывает как микроархитектура от самой компании ARM (она обычно называется Cortex и следом снова постфикс профайла), так и кастомная, которая может называться Apple Firestorm, Neoverse N1 или никак не называться.
Набор команд A32 используется в 32-битном режиме AArch32, а A64 в 64-битном режиме AArch64. И казалось бы, зачем выделять такие очевидные вещи. Но дело в том, что A32 — не единственный набор команд, который может быть в 32-битном режиме. A32 и A64 всегда используют 32 бита для кодирования любой инструкции, а AArch32 вышел очень давно и многим казалось, что это расточительство и тогда появились альтернативные способы кодирования Thumb и Thumb2. В них часто используемые инструкции занимали 16 бит. Для AArch64 уже ничего такого не завезли, в нём любая инструкция занимает 32 бита.
Ну и наконец, расширения набора команд. Вообще, есть ещё расширения VFPv1-VFPv5 для работы с плавающей точкой, разницу между которыми я так и не смог понять. Как и в x86, в ARM плавающую точку завезли не сразу. В ARMv6 было добавлено расширение SIMD (так и называется), а в ARMv7 появился опциональный 128-битный advanced SIMD, он же ASIMD, он же NEON, по сути прямой аналог SSE последних версий. О нём я буду рассказывать больше всего. А вот аналога AVX в ARM нет, там пошли другим путём. Вместо того, чтобы каждые пять лет представлять новое расширение, под которые нужно будет всё переписывать, было разработано расширение Scalable Vector Extension (SVE), которое позволяет выполнять один и тот же код на чипах, реализующих разный размер векторов. Но на практике, как я понял, SVE реализован только в Fugaku supercomputer.
Это же ужас?
Ну, вообще, да, если вы собрались писать приложение, которое может быть выполнено на любом ARM процессоре, как это бывает с x86. Теоретически на нем может не оказаться не только NEON, но даже 64-битной арифметики с плавающей точкой. Вот только, к счастью, у ARM нет того наследия работающих систем, на которых могли бы запустить ваш код. Это в любом случае будет свежий процессор. И ещё, с максимальной вероятностью это всё же будет AArch64 система. А теперь следите за руками.
AArch64 появился только в ARMv8. ARMv8-A уже гарантирует наличие VFPv4 (64-битный FPU), NEON и криптографии. А SVE можно даже не проверять ещё пару лет. У NEON никаких версий нет. Так же остается только один набор инструкций: A64. А микроархитектура просто ни на что не влияет.
Получается, несмотря на огромное количество вариантов, в реальности писать код под ARM (точнее под AArch64) даже проще, чем под x86. Никакие проверки в рантайме не нужны, просто ставите `#ifdef __aarch64__` и пользуетесь всем, чем хотите.
Знакомство с NEON
Принципиальное устройство x86 и ARM мало чем отличаются. И там и там есть общие регистры и регистры для вычислений с плавающей точкой и SIMD. Для общей картины достаточно прочитать раздел General-purpose registers в AArch64 Instruction Set Architecture. И сразу после этого можно переходить к самому полному вводному гайду — Coding for Neon.
Первое, что бросается в глаза — инструкции загрузки и сохранения в память типизированные. И это даже имеет какое-то применение.
Тут надо уточнить, что сами регистры не типизированны, тип задается только на момент выполнения инструкции.
Очень приятно удивляет кол-во вариантов всяческих сдвигов. Например, есть вариант, когда сдвиг каждого элемента вектора задается в другом векторе.
В SSE, например, такого нет. Похожая функциональность появилась только в AVX2 с инструкциями vpsrlv[dq] , vpsllv[dq] , vpsravd . Но, во-первых, в NEON инструкции сдвигают в обе стороны, в зависимости от знака. Во-вторых, в AVX2 можно сдвинуть только 32-битные и 64-битные значения. На этом вкусности NEON не заканчиваются, из других вариантов сдвига есть:
Сдвиг и сложение с аккумулятором
Сдвиг вправо с округлением
Сдвиг с сатурацией
Сдвиг с уменьшением или увеличением разрядности
Причем некоторые варианты из списка можно комбинировать, и всё это работает с любым типом данных. Поправьте, но вроде SSE ничего из этого не может предложить.
Что касается интринсиков, в отличие от SSE/AVX, где типизированны только регистры для float и double ( __m128 и __m128d ), в NEON есть типы для всех целых типов и названия придерживаются конвенции stdint.h.
Тут первые две переменные имеют тип «16 беззнаковых int8», вторые — «4 беззнаковых int32». Но, так как это одни и те же регистры, их можно приводить друг к другу. Интересно, что есть типы вроде uint8x16x3_t — это три регистра подряд. В основном такие типы используются для загрузки и сохранения в оперативную память.
Справочник интринсиков
Если вы за последнее десятилетие писали SIMD-код для x86, вы наверняка пользовались Intel Intrinsics Guide. Это прекрасный справочник с интерактивным поиском и фильтром, понятным описанием и псевдокодом для каждой инструкции. И даже есть таблицы задержек и пропускной способности по разным поколениям процессоров Intel.
В нём можно не только искать нужное, но и изучать новое, просто выбирая какое-то поколение инструкций и читая всё подряд.
У ARM аналогом этого гайда служит Neon Intrinsics Reference. И это просто боль и унижение.
Нет никаких фильтров
Поиск работает с перезагрузкой страницы
На странице выводится только 30 функций, снизу есть постраничная навигация, тоже с перезагрузкой страницы
Первый экран занимают бесполезные три абзаца текста, которые нужно каждый раз проматывать. А проматывать приходится очень часто, ведь любой чих — перезагрузка страницы
По запросу «mul» находятся 50 страниц функций! То есть 1500 штук. Знаете, почему в результатах оказалась функция, показанная на скриншоте? Потому что в описании есть слово accumulate!
У каждой инструкции есть много флагов, которые чуть-чуть меняют поведение и почти каждая инструкция работает с несколькими типами данных, которых около 12. И для всего этого создаются отдельные функции. По факту описание и псевдокод написан не для функций, а для инструкции. То есть понять, чем одна функция отличается от другой, почти такой же, оочень сложно.
Вы вообще видели этот псевдокод? Он сам по себе очень избыточен и запутан.
Поэтому, пока я для себя нашел такой вариант: сначала я ищу что-то похожее на то, что мне нужно в алфавитном указателе инструкции с кратким описанием, а уже потом по названию инструкции ищу в справочнике интринсики.
Подходящая задача
Давайте попробуем NEON в деле. В качестве примера кода, на котором можно поэкспериментировать, я выбрал альфа-композитинг с premultiplied alpha. Его можно описать такой формулой:
Алгоритм идеально ложится на SIMD:
Все операции выполняются над целочисленными значениями. 1 — Sa превращается в 255 — Sa . Так как правую часть мы умножаем на байт, то и левую нужно умножить. А после сложения нужно поделить на 255, это делает макрос DIV255 путём нескольких сдвигов.
Запускать я буду на Raspberry Pi 4, естественно под AArch64. Чем богаты, тем и рады, как говорится. Причем в данном случае мне интересно посмотреть именно пиковую производительность, без влияния памяти. Для этого я буду тестировать на строке длиной 1000 пикселей, то есть всего будет задействовано 12 Кб данных за один вызов функции.
Я буду пользоваться компилятором Clang-9, т.к. он в большинстве случаев выдает более быстрый код, чем GCC. Для начала интересно, как быстро работает чистый код, без векторизации.
Время указано в секундах для 20 тысяч прогонов функции с длиной строк 1000 пикселей. То есть можно сказать, что скорость работы примерно 105 МПх/с. И вообще-то это очень мало, даже для Raspberry Pi. Если включить автоматическую векторизацию, результат будет чуть лучше.
Ускорение в 2.3 раза существенно, но это не всё, на что можно было бы рассчитывать. Посмотрим, что можно сделать вручную.
NEON-версия
Первое, что нужно сделать, чтобы начать программировать на NEON — подключить заголовочный файл arm_neon.h . Согласитесь, это приятнее, чем каждый раз гуглить ничего не значащие имена заголовочных файлов, вроде smmintrin.h .
1. Загрузка/сохранение. Хотя видно, что код хорошо векторизуется внутри цикла, можно сразу пойти чуть дальше и читать из памяти 128-битный вектор целиком и работать с четырьмя пикселями.
Тут пока что Rx4 считается намеренно неправильно. Но зато код запускается и уже можно прикинуть, сколько работает код на NEON, если он ничего не делает:
8 мс или 2500 МПх/с! Вот мы и ускорили код с помощью NEON в 25 раз.
2. Выделение альфа-канала. Следующая задача — нужно из вектора Sx4 отдельно вытащить все компоненты альфа-канала. Они находятся на 3, 7, 11 и 15 позиции. Причем желательно их сразу размножить на все соседние байты этого же пикселя. Несмотря на множество команд перемешивания байтов, я не нашел ничего специального и сделал через векторный поиск в таблице. Дальше нужно вычесть полученное значение из 255.
Интересно, что все компиляторы при оптимизации заменяют операцию вычитания из 255 на побитовое отрицание, что логично.
3. Умножение. Дальше нужно все элементы Sx4 умножить на 255, а элементы Dx4 на соответствующие элементы альфы из Sax4 . Все значения 8-битные.
В NEON есть два вида умножения: либо это обычные функции vmulq_* , которые не меняют разрядность и отдают нижнюю часть результата, либо это vmull_* и vmull_high_* , которые делают операцию только над половиной вектора, но зато увеличивают разрядность и отдают результат целиком.
Для умножения Dx4 можно было бы использовать эти же функции, но у них есть ещё одна разновидность, которая тут больше подходит. Это умножение и сложение с аккумулятором. Действительно, нам нужно будет складывать результат умножения, так почему бы не сделать это в одну операцию.
4. Деление на 255. Ну что, пришла пора опробовать в деле крутые сдвиги. В Си-версии это происходит так:
Первое, что нужно рассмотреть — сдвиги с округлением. Для этого до сдвига прибавляется константа в один бит правее сдвига. По сути это уже сильно упрощает описание деления:
Дальше следует обратить внимание на конструкцию ROUND_SHR(a, 8) + a . Это же сдвиг с аккумулятором vrsraq_n_u16 , помните? Ну а последний сдвиг можно сделать так, чтобы он заодно уменьшал разрядность результата, ведь тот не должен превышать 255. Кроме того, можно уменьшить разрядность не только в нижнюю половину вектора, но и в верхнюю ( vqrshrn_high_n_u16 ).
Ну и наконец, можно порадоваться результату:
Это в 1,75 раз быстрее, чем автовекторизованная версия и в 4 раза быстрее, чем версия совсем без векторизации (кстати, для GCC ускорение получается 5,5 раза).
Оптимизация чтения
Полученный результат полностью доказывает жизнеспособность программирования под NEON, однако, от использованной техники (чтение и обработка 4 пикселей, 8 умножений за инструкцию) можно было ожидать больший прирост скорости. Давайте посмотрим, что можно улучшить.
Код выполнялся на процессоре Broadcom BCM2835, который хоть и относительно современный, но супербюджетный. Можно ожидать, что на таком процессоре могут быть существенные задержки даже для доступа к кешу L1. При этом никакие инструкции предвыборки не помогут, т.к. данные уже лежат в самом близком кеше. Зато может помочь предварительное чтение. Для этого нужно на каждом шаге класть «в карман» данные, которые понадобятся на следующем шаге. А из кармана доставать то, что было выбрано на предыдущем.
При этом нужно не забыть, что цикл нужно уменьшить на одну итерацию, чтобы не прочитать данные за пределами указателя.
Гипотеза оказалась верной, это дало прирост ещё 25%. Итого NEON работает ровно в 5 раз быстрее, чем код без векторизации.
Разбор сгенерированного кода
Ну что, пора посмотреть, что напридумывал компилятор.
Это вывод уже отформатированный, с некоторыми переименованными регистрами и с комментариями оригинального кода:
Хочу напомнить, что q0 и v0 — это разные названия одного регистра. Первое, что стоит отметить — компилятор верно понял задумку с упреждающим чтением и, несмотря на то, что в коде мы использовали копирование из старых переменных в новые, в выводе ничего такого нет. Чтение происходит сразу после того, как данные регистров v0 и v1 больше не используются. Хоть из-за этого загрузка и сместилась с начала итерации в середину, тут стоит положиться на компилятор, т.к. скорее всего он решил так сделать основываясь на латентности операций чтения.
Далее стоит обратить внимание на последовательности umull и umlal . Тут произошло странное, зачем-то компилятор заменил umull2 на ещё один umull . Для этого ему понадобилось в строчке №8 сделать лишнее извлечение верхней части v0 во временный регистр v6. Формально мы использовали функцию vget_low_u8 , которая это и подразумевает. Однако это было сделано только для того, чтобы привести переменную к нужному типу. Если посмотреть, что генерируют компиляторы для такого кода, то видно, что они не очень понимают, что нижняя часть регистра — это и есть сам регистр. Ну а GCC вообще творит какую-то дичь: создает два разных регистра с константами, делает три копирования.
На этом странности не заканчиваются. Для Rx2lo переставлены местами vmull_u8 и vmlal_u8 . Формально это ни на что не влияет, но все равно не понятно, зачем.
Ну и последнее, на что можно обратить внимание — это странная работа с индексами. Если для команды str q2, [x21, x9] в качестве смещения используется регистр, то для ldr смещение вычисляется заранее, причем два раза, хотя очевидно, что можно было вычислить x9 + 16 и использовать это значение в обеих загрузках.
Пробуем всё это исправить:
Есть ещё 14% прироста. Итого ускорение 5,7 раз.
Было бы интересно также посчитать, сколько тактов уходит на этот цикл. Имеем 1800МГц (тактов/с), 0.033190 с/запуск и 250 * 20 * 1000 циклов/запуск. Итого: 1800000000 * 0.033190 / (250 * 20 * 1000) ≈ 12 тактов/цикл. Учитывая, что в цикле 17 инструкций, это прекрасный результат. Я не думал, что такой простой процессор может работать так эффективно.
Решение на SSE
Думаю, будет нелишним решить ту же задачу на SSE и сравнить усилия. Загрузка и выгрузка данных ничем не отличается. Дальше нужно размножить альфа-канал пикселей-источников на все остальные каналы. Тут можно сделать полностью аналогично NEON-версии.
Существенное отличие только в том, что порядок байтов у _mm_set_epi8 инвертирован.
Дальше нужно 8-битное умножение. В SSE есть такое, это интринсик _mm_maddubs_epi16 . И кстати, он же увеличивает разрядность результата и даже делает сложение соседних элементов. Можно было бы подумать, что дело в шляпе.
Количество инструкций умножения уменьшилось вдвое по сравнению с NEON-версией. Но, к сожалению, _mm_maddubs_epi16 принимает только первый 8-битный аргумент как целое без знака, а второй аргумент считается со знаком, поэтому результат будет неверным. Функции, в которой оба аргумента были бы без знака, нет. А значит, нужно использовать 16-битное умножение и распаковывать каждый аргумент с пустым регистром, что существенно увеличивает и запутывает код.
Деление на 255 и запаковку приходится делать, описывая каждый шаг, SSE никак не помогает.
За вычетом загрузок/сохранений, констант и приведений типов, я насчитал 23 интринсика в SSE версии против 10 в NEON. Предложения по улучшению преветствуются.
Моё впечатление
NEON произвел впечатление очень продуманной и эффективной системы команд. Я нашел для себя такие плюсы:
В отличие от SSE, есть консистентность типов данных, с которыми работают разные инструкции.
Порадовало большое количество опций сдвигов, которые оказались полезными на практике.
Можно встраивать NEON-код в любое место приложения без проверок рантайм.
Использование NEON дает ощутимый прирост производительности, примерно равный такому от использования SSE.
Очень понятный ассемблер с типизированными аргументами.
Были опасения, что будет сильно не хватать инструкции _mm_madd_epi16 , делающей 8 умножений и 4 сложения. Однако её функциональность покрывается парой vmull_* / vmlal_* , не требующих подготовки данных.
Минусы я бы отметил следующие:
Абсолютно неюзабельный справочник интринсиков. Это не минус самого NEON, но это то, с чем придется столкнуться.
Производительность сильно зависит от компилятора, возможно придется залочиться на clang.
Имена некоторых интринсиков напоминают читы в играх: vqrshrn_n_u16 , vqdmulh_s16
Источник