Ладно, скажете вы, дочитав до этого момента, и зададитесь вопросом: «а зачем тестировать скорость декодирования AV1 видео на Эльбрусе? К чему вообще этот тест?». Если вы хотите использовать Эльбрус как домашний компьютер, время от времени вы будете воспроизводить AV1 видео, т.к. в этом кодеке уже могут вещать и YouTube, и Netflix, и многие другие ресурсы. И проблема в том, что у видеокарт AMD до 6000-й серии не было ещё поддержки аппаратного ускорения декодирования AV1 видео. Соответственно, в случае с Эльбрусом, на котором пока тестировали только RX 500 серии и ниже (не 5000), задача по декодированию AV1 видео будет полностью лежать на процессоре (может быть, RX6000 заработает с Эльбрус, но не проверено). Интересно глянуть, как быстро Эльбрус с AV1 справится.
Для теста я воспользуюсь реализацией dav1d декодера (да, это рекурсивный акроним, decoder av1 dav1d) от VideoLAN (разработчики VLC плеера). Он имеет реализацию как на Ассемблере под x86 и ARM, так и на C.
Тест проведём на 3 сэмплах от Netflix:
Скриншот 90. Тест при помощи dav1d 0.5.2 из репозитория Альт Линукс 10.
Для начала, я попробовал прогнать тест при помощи варианта из репозитория Альт Линукс. Только там довольно старая версия, 0.5.2.
И проблема в том, что версия 0.5.2 очень медленная.
Скриншот 91. Сравнение версий dav1d 0.5.2, 0.8.2 и 0.9.2 при компиляции с флагами -O4 -ffast -mtune=elbrus-8c
Я попробовал скомпилировать dav1d сам из исходного кода, используя при компиляции опции -O4
и -ffast
, дабы компилятор старался в большей степени оптимизировать код, и у меня с dav1d 0.5.2, который я собрал сам, вышли примерно те же результаты, что и с тем уже собранным, что доступен из репозитория. Но вот версии 0.8.2 и 0.9.2 вышли значительно быстрее: по 1-му сэмплу они быстрее на 55%, по 2-му – на 75%, а по 3-ему – на 38%. Короче говоря, старая версия 0.5.2 из репозитория Альт Линукса примерно в 1.5 раза отстаёт от более свежих, которые мы можем скомпилировать сами.
Скриншот 92. Попытка задействовать опцию -fwhole при компиляции.
Я пытался воспользоваться при компиляции и опцией -fwhole, но сборщик meson её просто отказался принимать.
Скриншот 93. Попытка скомпилировать dav1d с опцией -fwhole-shared.
В вики Альт Линукса читал ещё про опцию -fwhole-shared
, но у меня с ней сборка не удалась, так что в итоге -fwhole
и -fwhole-shared
для большей оптимизации не смог задействовать.
Если помните, год назад я проводил этот же тест на Macbook Pro с Apple M1: тогда мы просто афигели от низкой эффективности Rosetta 2: всего 23% по 1-му сэмплу. Т.е. более чем в 4 раза у нас просела производительность при декодировании AV1 видео, когда мы использовали версию под x86 в трансляции вместо нативной версии под ARM. Но тут есть нюанс. Мы тогда оба варианта (и ARM, и x86) собирали с использованием Ассемблерного кода под каждую из этих платформ. И вот здесь то и собака зарыта: Ассемблерного кода в dav1d нету под E2K. Как нам тогда более-менее релевантно сравнить Эльбрус 8С с Intel и Apple в данной задаче?
Скриншот 94. Содержимое конфигурационного файла meson_options.txt в dav1d.
Решение простое. Возьмём и соберём dav1d из обычного C кода под все платформы. Чтобы у нас не использовался Ассемблер при сборке, достаточно у опции enable_asm в параметре value заменить значение true на false, и тогда даже если есть возможность собрать программу из Ассемблера, будет произведена сборка из C кода. Т.е. если в случае с ffmpeg и с Blender многое упиралось в то, насколько хорошо софт оптимизирован под Эльбрус, то здесь мы уже можем оценить его в равных условиях с остальными. Но, да, имейте в виду, что под x86 и ARM у куда большего числа приложений имеются крутые оптимизации с интринсиками, или и вовсе чистым Ассемблером, как в случае с dav1d.
Скриншот 95. Строки в файле meson.build, которые мы будем оптимизировать.
Я решил, что неплохо бы оценить результат при сборке с разными флагами для компилятора. Т.е. мы посмотрим ещё и на то, как меняется производительность при различных требованиях по оптимизации от компилятора. Для этого меж строк 286 и 287 мы добавим следующее:
optional_arguments += '-O4' optional_arguments += '-fwhole' optional_arguments += '-ffast
Да, я говорил, что опция -fwhole у меня не сработала, но я её оставляю на случай, если со следующими версиями dav1d она уже будет работать.
Чтобы автоматизировать процесс редактирования конфигурационных файлов и дальнейшей сборки я форкнул проект dav1d и написал небольшой скрипт. Что делает этот sh скрипт? Он создаёт папку av1, в неё выкачивает dav1d и подгоняет его под версию 6aaeeea6 с git VideoLAN, с которым я и проводил тесты (опционально можно воспользоваться версиями 0.5.2, 0.8.2 и 0.9.2 при помощи команды /bin/bash install.sh --version нужная_вам_версия). Почему я выбрал эту версию? Потому, что с самой свежей (на момент тестирования) версией у меня получились лучшие результаты на всех платформах. Как я понял из тэгов в Git проекта, версия 0.9.3 к релизу не планируется, дальше будет уже релиз 1.0.0 (на момент редактуры статьи, эта версия уже вышла). Ту предрелизную версию 1.0.0, с которой я проводил тесты, я решил обозначить условной 0.9.3 (точнее, 0.9.3-git-6aaeeea6).
Для x86 и ARM платформ мой скрипт загружает 5 копий dav1d. В трёх из них редактируются конфигурационные файлы так, чтобы на выходе мы имели сборку из C кода в 3 вариантах: без доп. опций, с оптимизацией -O3 при помощи meson (команда meson .. --optimization=3 --default-library=static), и с оптимизацией -O4 (правка файла meson.build, и плюс к этому команда meson .. --optimization=3 --default-library=static). В оставшихся двух генерируются версии из Ассемблера без доп. опций и с оптимизацией -O3 (также при помощи команды meson .. --optimization=3 --default-library=static). По моим тестам, между вариантами сборки из C кода -O3 и -O4 нет никакой разницы на x86 и ARM машинах, т.к. для компилятора gcc опции -O3 и -O4 равнозначны. Также нет разницы между Ассемблером без опции -O3 и с этой опцией, т.к., очевидно, когда дело касается Ассемблера, компилятор ничего особо не оптимизирует, это ведь не C и не C++ код. Поэтому на x86 машинах мы будем сравнивать результаты в 3 вариантах: без дополнительных оптимизаций компилятором, с ними (-O3 через meson), и с Ассемблером.
На E2K платформе (Эльбрус) мой скрипт загружает 3 копии dav1d. В первой мы производим сборку без каких-либо доп. оптимизаций, во второй мы редактируем файл meson.build, добавляя в опции сборки -O3, -ffast и -fwhole (эта опция не задействуется при сборке, но в скрипте оставил на будущее, мало ли что) и указываем сборку с оптимизацией -O3 ещё через meson (meson .. --optimization=3 --default-library=static), а в третьей мы делаем всё то же самое, только с заменой -O3 на -O4.
Итого мы будем сравнивать по 3 варианта сборки на каждой из архитектур: код на С, код на C с -O3 оптимизацией, и Ассемблер для x86 и ARM, а также код на С, код на С с оптимизациями -O3 и -ffast, и код на C с оптимизациями -O4 и -ffast для Эльбруса.
Кроме того, на Эльбрусе мы ещё взглянем на то, сколько производительности мы теряем при использовании каждого из вариантов для x86 через транслятор RTC. В общем, этот тест будет самым интересным.
Для сборки dav1d вам понадобятся следующие зависимости: git, meson и ninja (пакет ninja-build в Альт). Для x86 платформы нужен ещё nasm. Если у вас платформа не x86, для загрузки уже скомпилированной x86 версии для теста в трансляции вам понадобится ещё консольная утилита wget.
Если у вас Mac, вы можете установить все необходимые зависимости одним простым скриптом, который я для вас приготовил. Остальным же на Linux надо в своих дистрибутивах в репозиториях найти необходимые пакеты и установить их (ну не сделать мне под это универсальный скрипт).
Можете скрипт для установки зависимостей на Mac предварительно не загружать, а одной командой в Терминале сразу и загрузить, и выполнить:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/ZChuckMoris/dav1d/master/macos_deps.sh)"
В будущем, если bash уберут из macOS, просто замените /bin/bash на /bin/zsh. Пока что bash оставили даже в macOS 12 Monterey, просто c macOS 11 он не используется по умолчанию. Воспользоваться им по-прежнему можно. С bash я проверял и всё работает нормально, и с zsh у меня проблем тоже не было. Но мало ли что, я же zsh дотошно не тестил...
Далее на всех платформах, после установки нужных зависимостей, вам понадобится команда, чтобы загрузить и скомпилировать dav1d с разными опциями, и загрузить ещё собранные x86 варианты для теста в трансляции (если у вас не x86 архитектура), и ещё загрузить все 3 сэмпла для теста:
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/ZChuckMoris/dav1d/master/install.sh)"
После того, как вы выполните эту команду, если вы предварительно установили все необходимые зависимости, вас в папке av1 будут ждать собранный dav1d под вашу платформу с разными опциями, загруженные 3 сэмпла от Netflix, и версии dav1d для x86 платформы в папке x86 внутри av1 (это в том случае, если у вас платформа не x86, т.е. либо ARM, либо E2K).
Для теста на Linux (ARM/E2K/x86) и macOS (ARM/x86) я написал один единый набор команд. Если он вам не подойдёт, дайте мне знать об этом.
dav1dversion="0.9.3-git-6aaeeea6"; if [[ $(arch) =~ ^(arm|aarch64) ]]; then if [[ $OSTYPE =~ ^linux ]]; then if [[ -f /usr/bin/exagear ]]; then translator="exagear"; translatorargs=" -- "; function transver() { echo "ExaGear version: $(/opt/exagear/bin/ubt_x64a64_opt --version | grep -i Revision)"; }; fi; elif [[ $OSTYPE =~ ^darwin ]]; then translator="arch"; translatorargs="-x86_64"; else if [[ $(ps -p $$ | awk '{print $4}' | tail -n 1) =~ zsh ]]; then echo '?Trying to run on unsupported platform.'; return 1 2>/dev/null; else while true; do read -s -n 1 -p "Unsupported platform. Press Ctrl+C to quit."; done; fi; fi; fi; if [[ $(arch) =~ ^e2k ]]; then translator="/opt/mcst/rtc/bin/rtc_opt_rel_p1_x64_ob"; translatorargs="--path_prefix /mnt/shared/rtc/ubuntu20.04/ -b $HOME -b /etc/passwd -b /etc/group -b /etc/resolv.conf --"; function transver() { /opt/mcst/rtc/bin/rtc_opt_rel_p1_x64_ob --version | egrep -o "^.*lcc\s?[a-Z0-9.]*" }; fi; if [[ ! $(nproc) || $(nproc) == "" || $(nproc) -le 0 ]]; then cputhreads=$(getconf _NPROCESSORS_ONLN); else cputhreads=$(nproc); fi; if [[ $dav1dversion =~ ^(0\.9\.3-git|1\.) ]]; then threads="--threads $cputhreads"; else if [[ $cputhreads -ge 2 ]]; then threads="--framethreads $cputhreads --tilethreads $(( $cputhreads / 2 ))"; else threads="--framethreads 1 --tilethreads 1"; fi; fi; postargs="-o - $threads --muxer=null"; translate=""; folder1=""; folder2="x86_64"; if [[ ! $(arch) =~ ^((x|i[[:digit:]])86|amd64) ]]; then declare -a folders=("$folder1" "$folder2"); else declare -a folders=("$folder1"); fi; for folder in "${folders[@]}"; do cd ./$folder; if [[ $folder == $folder1 ]]; then translate=""; Mode="Native"; if [[ $(arch) == "e2k" ]]; then declare -a buildargsarr=("" "-O3" "-O4"); elif [[ $(arch) =~ ^(arm|aarch64) ]]; then buildargsarr=("" "-O3" "-asm"); fi; else echo ; if [[ $(type -t transver) == function ]]; then transver; fi; translate="$translator $translatorargs"; Mode="Translate"; declare -a buildargsarr=("" "-O3" "-asm"); fi; for buildargs in "${buildargsarr[@]}"; do dav1d="dav1d-$dav1dversion$buildargs/build/tools/dav1d"; echo ; eval $translate ./$dav1d --version; for testvideo in "Chimera-2397fps-AV1-10bit-1920x1080-3365kbps.obu" "Chimera-AV1-8bit-1920x1080-6736kbps.ivf" "Chimera-AV1-10bit-1920x1080-6191kbps.ivf"; do if [[ $OSTYPE =~ ^linux ]]; then /usr/bin/time -f "Elapsed: %E (%e secs). Mode: $Mode. dav1d$buildargs. Threads: $cputhreads. Video: $testvideo" /bin/bash -c "eval $translate ./$dav1d -i ./$testvideo $postargs &>/dev/null"; else shell="bash"; if [[ $(ps -p $$ | awk '{print $4}' | tail -n 1) =~ zsh ]]; then shell="zsh"; fi; /usr/bin/time /bin/$shell -c "eval $translate ./$dav1d -i ./$testvideo $postargs &>/dev/null"; fi; done; done; if [[ $folder != "" ]]; then cd ../; fi; done
Напомню, что эти наборы команд я писал уже после тестов (см. главу 4.1). То, что на скринах виден другой код, не имеет значения, т.к. разница лишь в визуале. В виде скрипта этот код сможете найти в репозитории на GitHub.
Скриншот 96. Тестирование dav1d на Эльбрус 8С с Эльбрус ОС 7.1 и компилятором lcc версий 1.26.09 и 1.25.19
Гистограмма 41. Сравнение dav1d на Эльбрус 8С при сборке разными версиями компилятора LCC (1.25.19 и 1.26.09).
Гистограмма 42. Сравнение dav1d на Эльбрус 8С при сборке разными версиями компилятора LCC (1.25.19 и 1.26.09).
Гистограмма 43. Сравнение dav1d на Эльбрус 8С при сборке разными версиями компилятора LCC (1.25.19 и 1.26.09).
Сперва я провёл тест на Эльбрус ОС 7.1 с разными версиями компилятора (1.26.09 из OSL 7.1 и 1.25.19 из OSL 6.0.1). С одинаковыми опциями (с -O3 или -O4, или без) выходит так, что программа dav1d, собранная старой версией компилятора, работает чуть быстрее. Там разница не велика, чуть менее 1%, если брать среднюю разницу по всем 3 сэмплам. Как я понял, основные изменения lcc 1.26 затрагивают совместимость с GCC более свежей версии (9.3.0 вместо 7.3) и оптимизации под более свежие процессоры серии Эльбрус. На 8С же разницы по скорости особо нет, так что, по большому счёту, на Альт Линукс и 10 и Эльбрус ОС 7.1 мы будем наблюдать примерно один и тот же уровень производительности на 8С.
На x86 и ARM платформах разницы между флагами -O3 и -O4 нет никакой, поэтому мы будем сравнивать -O4 на Эльбрусе с -O3 на x86/ARM. На Эльбрусе смысла сравнивать -O3 с другими не вижу, т.к. у него с любой версией компилятора в целом -O4 выходит быстрее, чем -O3. В случае с lcc 1.25.19 (старой версией) использование флага -O4 в сравнении с -O3 повышает производительность на 3.1% по 1-му сэмплу, 3.4% по 2-му и 6.3% по 3-ему. На более свежей версии lcc, 1.26.09, разница другая: 1% по 1-му сэмплу, 1.6% по 2-му и 10.9% по 3-му сэмплу. При любом раскладе в случае с dav1d лучше использовать -O4 вместо -O3 на Эльбрусе, так что при сравнении версий из C кода с оптимизациями мы будем на Эльбрусе подразумевать, что компилятору давали команду -O4, тогда как на других платформах использовали флаг -O3 (разницы между -O3 и -O4 на x86 нет).
Отвечу наперёд на вопрос, почему я не пытался из Ассемблера под x86 собрать dav1d на Эльбрусе: да это невозможно. Чистый Ассемблер под ту или иную платформу – это едва ли не готовый машинный код. Если с интринсиками компилятор вывезет, подставляя вместо команд для Intel аналоги от МЦСТ для Эльбруса, то с чистым Ассемблером (в данном случае – NASM) такой фокус не пройдёт. Поэтому соберём лишь из чистого C кода.
Короче, ладно, что там по тестам?
Скриншот 97. Тест dav1d (0.9.3-git-6aaeeea6) на Эльбрус 8С на Альт 10 с трансляцией RTC и без неё.
Гистограмма 44. Тест dav1d из C кода (без доп. оптимизаций компилятором) на Эльбрусе 8С в трансляции и без.
Гистограмма 45. Тест dav1d из C кода (без доп. оптимизациями компилятором) на Эльбрусе 8С в трансляции и без.
Сперва я провёл тест на Альт Линукс. И вот что меня, мягко говоря, удивило: если не использовать доп. оптимизации с помощью опций компилятора, то эффективность трансляции составляет 94% в среднем. По 1-му сэмплу просадка при трансляции 19%, по 2-му – 15%, а по 3-ему, наоборот, прирост производительности при трансляции составил 15%. И в среднем (т.е. просто среднюю арифметическую взять от разницы во всех 3 сэмплах), мы имеем эффективность в 94% как-раз.
Гистограмма 46. Тест dav1d из C кода (с -O4 и -ffast оптимизациями) на Эльбрусе 8С в трансляции и без.
Гистограмма 47. Тест dav1d из C кода (с -O4 и -ffast оптимизациями) на Эльбрусе 8С в трансляции и без.
Если сравнивать сборку из C кода на Эльбрусе с доп. оптимизациями при сборке компилятором (-O4 и -ffast) со сборкой из C кода на x86 также с доп. оптимизациями (-O3), эффективность трансляции снижается до 87% в среднем. Вернее, это не эффективность трансляции снижается, просто эти доп. оптимизации на x86 дают меньше выигрыша, чем на E2K, потому мы и видим увеличение разницы между нативом и трансляцией x86 варианта.
Гистограмма 48. Тест dav1d из C кода (с -O4 и -ffast оптимизациями) на Эльбрусе 8С и из Ассемблера в трансляции.
Гистограмма 49. Тест dav1d из C кода (с -O4 и -ffast оптимизациями) на Эльбрусе 8С и из Ассемблера в трансляции.
А теперь самое крышесносное: вариант на Ассемблере под x86 в трансляции через RTC быстрее в среднем на 104%, т.е. чуть более чем в 2 раза! Сами видите разницу по 3 сэмплам: 195%, 172% и 243%. Сравнивал тут результат при сборке с флагами -O4 и -ffast в нативе и результат при сборке под x86 из Ассемблерного кода. Это просто безумие. Это значит, что в определённых ситуациях, когда код хорошо вылизан под x86, он быстрее будет выполняться в трансляции на Эльбрусе, нежели обычный, не особо оптимизированный под Эльбрус, код на C.
Скриншот 98. Тест dav1d (0.9.3-git-6aaeeea6) на Эльбрус 8С на Эльбрус ОС 7.1 с трансляцией RTC и без неё.
Гистограмма 50. Тест dav1d из C кода (с -O4 и -ffast оптимизациями) на Эльбрусе 8С и из Ассемблера в RTC 4.1.
Гистограмма 51. Тест dav1d из C кода (с -O4 и -ffast оптимизациями) на Эльбрусе 8С и из Ассемблера в RTC 4.1.
Я протестировал то же на Эльбрус ОС 7.1 и результат вышел схож с lcc 1.26. Короче, в ряде задач RTC на Эльбрусе может ускорить работу в 2 раза!
Скриншот 99. Тест dav1d (0.9.3-git-6aaeeea6) на Эльбрус 8С с трансляцией Lintel 4.1 (Ubuntu 20.04.3).
Гистограмма 52. Тест dav1d из C кода (с -O4 и -ffast оптимизациями) на Эльбрусе 8С в Альт и в Ubuntu (Lintel).
Гистограмма 53. Тест dav1d из C кода (с -O4 и -ffast оптимизациями) на Эльбрусе 8С в Альт и в Ubuntu (Lintel).
С Lintel в Ubuntu 20.04.3 при использовании оптимизаций -O4 и -ffast у нас эффективность трансляции снижается с 87% до 60% в среднем.
Т.е. при использовании всё того же Ubuntu 20.04.3, только не base, а полноценного в Lintel, с трансляцией всех драйверов, всего ядра и в принципе всех обращений, и отведении 2 ядер под трансляцию (всего 6 остаётся под исполнение кода), у нас результаты выходят почти в 1.5 раза ниже в сравнении с RTC. Просто занимательный момент.
Гистограмма 54. Тест dav1d из C кода (с -O4 и -ffast оптимизациями) на Эльбрусе 8С и из Ассемблера в Ubuntu (Lintel).
Гистограмма 55. Тест dav1d из C кода (с -O4 и -ffast оптимизациями) на Эльбрусе 8С и из Ассемблера в Ubuntu (Lintel).
Если же сравнивать вариант на Ассемблере под x86 в трансляции с Lintel, он в среднем на 57% быстрее, чем вариант на C со всеми доступными оптимизациями компилятором при сборке в нативе на Alt Linux. Короче говоря, даже с Lintel, который намного менее эффективен, чем RTC, мы имеем прирост производительности в более чем в 1.5 раза в среднем за счёт того, что код dav1d на Ассемблере хорошо вылизан под x86, тогда как под Эльбрус имеется лишь Generic (базовая/общая) реализация на C.
Скриншот 100. Тест dav1d (0.9.3-git-6aaeeea6) на Xiaomi Mi Notebook Pro GTX с Ubuntu 20.04.3
Гистограмма 56. Тест dav1d из C кода (с -O4 и -ffast оптимизациями) на Эльбрусе 8С и Xiaomi Mi Notebook Pro GTX.
Гистограмма 57. Тест dav1d из C кода (с -O4 и -ffast оптимизациями) на Эльбрусе 8С и Xiaomi Mi Notebook Pro GTX.
Теперь мы наконец-то можем сравнить реализацию на C коде с оптимизациями при помощи компилятора в обоих случаях. Ну и тут в среднем мой ноутбук Xiaomi быстрее на 90% выходит в среднем. Это отставание уже далеко не в 3 и не в 4 раза, но примерно в 2 раза Эльбрус выходит медленнее в этой задаче. Почему так? Между ними разница в 2 года (Эльбрус 8С произведён в 2016-м году, а мой ноутбук был выпущен в 2018-м). Отсюда вытекает то, что i7 8550U задействует память DDR4, тогда как Эльбрус 8С довольствуется лишь DDR3 (только с Эльбруса 8СВ завезли DDR4), и процессоры эти произведены по разным нормам техпроцесса TSMC (28 нм против 14), и в итоге картина вырисовывается такой. Впрочем, вряд ли кто-то будет покупать сервер лишь с одним Эльбрусом. Обычно берут материнские платы сразу с 4 процессорами, да и уже с 8СВ, а не 8С, так что Эльбрус вполне себе может быть актуален в более свежих реализациях.
Гистограмма 58. Тест dav1d с Ассемблером под x86 в Ubuntu 20.04.3 на Эльбрус 8С (Lintel 4.1) и Xiaomi (i7 8550U).
Гистограмма 59. Тест dav1d с Ассемблером под x86 в Ubuntu 20.04.3 на Эльбрус 8С (Lintel 4.1) и Xiaomi (i7 8550U).
Если сравнивать версию с Ассемблером под x86, которая на Эльбрусе пашет только в трансляции (RTC 4.1), то да, разница в 4.04 раза в среднем. Примерно как разница между софтом, который на C в большей степени оптимизирован под Intel, чем под Эльбрус. На x86 же dav1d при сборке из Ассемблера использует и AVX инструкции, которые RTC не транслирует.
Скриншот 101. Тест dav1d (0.9.3-git-6aaeeea6) на Raspberry Pi 4 на Ubuntu 20.04.3 с трансляцией ExaGear и без.
Гистограмма 60. Тест dav1d (с оптимизацией -O3) на Raspberry Pi 4 с Ubuntu 20.04.3 в трансляции и без.
Гистограмма 61. Тест dav1d (с оптимизацией -O3) на Raspberry Pi 4 с Ubuntu 20.04.3 в трансляции и без.
Мне было интересно сравнить эффективность трансляции при помощи ExaGear с таковой у RTC. И получилась интересная картина: в случае, когда мы используем оптимизации -O3 при компиляции для обоих вариантов (x86 и ARM), эффективность составляет около 47% в среднем.
Гистограмма 62. Тест dav1d (x86 версия с -O3) на Эльбрус 8С (RTC 4.1) и Raspberry Pi 4 (Huawei ExaGear).
Гистограмма 63. Тест dav1d (x86 версия с -O3) на Эльбрус 8С (RTC 4.1) и Raspberry Pi 4 (Huawei ExaGear).
На Raspberry Pi 4 оптимизации компилятором (-O3) дают минимальный прирост по производительности, картина +- одинаковая. И эффективность трансляции всего 47%, в то время как без доп. оптимизаций у Эльбруса 8С с RTC эффективность трансляции составляла все 94%, а с оптимизациями – 87%. Я думаю, что ExaGear в тесте выходит менее эффективным, чем RTC из-за малой оперативной памятью на малине: всего 4 ГБ, тогда как Эльбрус у меня работает с 32 ГБ оперативной памяти (4 планки по 8 ГБ DDR3-1600).
Гистограмма 64. Тест dav1d (с оптимизацией -O4 и -ffast) на Эльбрус 8С с Альт 10 и Raspberry Pi 4 в Ubuntu 20.04.3.
Гистограмма 65. Тест dav1d (с оптимизацией -O4 и -ffast) на Эльбрус 8С с Альт 10 и Raspberry Pi 4 в Ubuntu 20.04.3.
Эльбрус на Альт 10 в сравнении с малиной на Ubuntu 20.04.3 в нативе с оптимизациями -O4 и -ffast для Эльбруса и -O3 для Raspberry Pi 4 быстрее на 47% в среднем (напомню, -O4 и -ffast на других платформах, кроме E2K, ничего не поменяют, поэтому сравниваем -O3 на ARM с -O4 на E2K).
Гистограмма 66. Тест dav1d (из С кода с оптимизацией -O3) на Эльбрус 8С (RTC 4.1) и Raspberry Pi 4 (ExaGear).
Гистограмма 67. Тест dav1d (из С кода с оптимизацией -O3) на Эльбрус 8С (RTC 4.1) и Raspberry Pi 4 (ExaGear).
Если сравнивать между собой результаты, которые мы имеем на Эльбрусе и малине в трансляции (RTC и ExaGear), то получается, что Эльбрус в среднем быстрее в 2.7 раза. О чём это говорит? Если софт доступен только под x86, и его нет ни под ARM, ни, уж тем более, под E2K, хотя он написан на C, скорее всего, при трансляции этого софта на обеих платформах, разница в пользу Эльбруса составит 2.7 раза.
А теперь попробуем сравнить нативную версию под Эльбрус из C кода с нативной версией под ARM из Ассемблерного кода.
Гистограмма 68. Тест dav1d на Эльбрус 8С (код на C и -O4 -ffast) и Raspberry Pi 4 (код на Ассемблере).
Гистограмма 69. Тест dav1d на Эльбрус 8С (код на C и -O4 -ffast) и Raspberry Pi 4 (код на Ассемблере).
Я сейчас скажу до невозможного банальную вещь: всё решает оптимизация. Даже маленький одноплатный микрокомпьютер размером с банковскую карту и со старыми 4 ядрами ARM Cortex-A72 может обогнать Эльбрус, если под него софт писать на Ассемблере, а под Эльбрус его писать только на языке C без каких-либо значимых оптимизаций, и всю задачу по оптимизации кода спихивать на компилятор, не используя ни Эльбрусовские интринсики, ни даже интеловские, ни вообще какие-либо преимущества архитектуры E2K. В таких условиях отрыв в пользу малины составляет 23%.
Гистограмма 70. Тест dav1d на Эльбрус 8С (RTC 4.1, x86 ASM) и Raspberry Pi 4 (ARM ASM).
Гистограмма 71. Тест dav1d на Эльбрус 8С (RTC 4.1, x86 ASM) и Raspberry Pi 4 (ARM ASM).
Как быть в ситуации, когда под E2K софт не оптимизирован, а под x86 вылизан великолепно на Ассемблере? Используйте транслятор RTC. Если сравнить dav1d в трансляции на Эльбрусе (вариант на Ассемблере) с dav1d в нативе на малине (вариант на Ассемблере), Эльбрус 8С быстрее на 69% в среднем. Я всё время пишу «в среднем», чтобы не озвучивать то, что вы и так перед глазами видите на гистограмме выше. Не буду же я сидеть и просто дублировать текстом проценты по каждому из сэмплов отдельно. Я надеюсь, что среди читателей нет людей, которые будут до этого докапываться. Я комментирую текстом уже итоговый средний результат.
Гистограмма 72. Тест dav1d (x86 версия на Ассемблере) на Эльбрус 8С (RTC 4.1) и Raspberry Pi 4 (ExaGear).
Гистограмма 73. Тест dav1d (x86 версия на Ассемблере) на Эльбрус 8С (RTC 4.1) и Raspberry Pi 4 (ExaGear).
Если же вы окажетесь в ситуации, когда и на Эльбрусе, и на малине нужно запустить x86 ПО, хорошо вылизанное на Ассемблере, результат будет примерно такой же, как и с софтом, скомпилированным из C кода под x86: Эльбрус быстрее в 2.8 раза.
Ладно, посмотрим далее на результаты на Raspberry Pi OS, чтобы вы мне не писали, что это с Ubuntu на малине что-то не так и, мол, на Raspberry Pi OS результаты будут иные.
Скриншот 102. Тест dav1d (0.9.3-git-6aaeeea6) на Raspberry Pi 4 на Raspberry Pi OS 64-bit (с графическим интерфейсом).
Гистограмма 74. Тест dav1d (версия для ARM из Ассемблерного кода) на Raspberry Pi 4 с Ubuntu и Raspberry Pi OS.
Гистограмма 75. Тест dav1d (версия для ARM из Ассемблерного кода) на Raspberry Pi 4 с Ubuntu и Raspberry Pi OS.
Ещё взглянув на скриншот, я понял, что ситуация на Raspberry Pi OS особо не отличается. Тут вся разница, как я полагаю, обусловлена наличием графического интерфейса на Raspberry Pi OS (я же ставил ОС, когда версия Lite ещё не была доступна). Ладно, с малиной всё понятно, идём дальше.
Скриншот 103. Тест dav1d (0.9.3-git-6aaeeea6) на Macbook Pro на Apple M1 (macOS 12.0.1) в нативе и с Rosetta 2.
И вновь огромная благодарность Рифату Фазлутдинову, подписчику моего Telegram-канала, за то, что откликнулся и помог мне, проведя тест с последней (на момент тестирования) версией dav1d на своём макбуке: тестировал как с Rosetta 2, так и без. Год назад я проводил этот тест, но собирал я тогда только версию из Ассемблера для обеих платформ (x86 и ARM), а сейчас мне понадобилось протестировать и вариант сборки из C кода, да и на последней версии, а не той, 0.8.2, что была раньше. Поскольку макбука у меня на руках уже давно нет, мне понадобилась помощь людей из моего Телеграм-канала. И за помощь вновь огромное спасибо Рифату.
Гистограмма 76. Тест dav1d (сборка из C кода без доп. оптимизаций) на Macbook Pro (Apple M1) с Rosetta 2 и без.
Гистограмма 77. Тест dav1d (сборка из C кода без доп. оптимизаций) на Macbook Pro (Apple M1) с Rosetta 2 и без.
Знаете, за прошедший год, то ли Rosetta 2 доработали, то ли изменения в dav1d привели к неожиданным результатам. Эффективность Rosetta 2 на маке при сборке dav1d из C кода без доп. оптимизаций под обе платформы составила 79% в среднем.
Почему я сперва решил рассмотреть результаты на сборке без доп. оптимизаций компилятором?
Что ж...
Гистограмма 78. Тест dav1d (сборка из C кода с оптимизацией -O3) на Macbook Pro (Apple M1) с Rosetta 2 и без.
Гистограмма 79. Тест dav1d (сборка из C кода с оптимизацией -O3) на Macbook Pro (Apple M1) с Rosetta 2 и без.
У Apple снова какие-то косяки с Rosetta 2. Если на 2-м и 3-ем сэмплах всё также, как и без доп. оптимизаций при сборке из C кода, то на 1-м сэмпле у нас на варианте с оптимизациями эффективность трансляции упала с 78% до 23%. Такого мы не видели ни с RTC от МЦСТ, ни с ExaGear от Huawei (ранее разработка велась в компании Eltechs, основанную сотрудниками отдела бинарной трансляции в МЦСТ). Просто у Apple какие-то косяки, которые хрен объяснишь и они сильно сказываются на производительности.
В общем, из-за этого я решил, что вариант с оптимизациями на Эльбрусе я буду сравнивать с вариантом без оптимизаций на Apple M1. Всё равно на 2-м и 3-ем сэмплах разница с оптимизациями и без у Apple минимальна выходит, что с Rosetta, что без, зато будем без «фокусов».
Гистограмма 80. Тест dav1d из C кода на Эльбрус 8С (с-O4 и -ffast) и на Macbook Pro (Apple M1, стандартная сборка).
Гистограмма 81. Тест dav1d из C кода на Эльбрус 8С (с-O4 и -ffast) и на Macbook Pro (Apple M1, стандартная сборка).
Сравнивая эффективность RTC 4.1 на Эльбрус 8С с Rosetta 2 от на Macbook Pro с M1 приходишь к выводу, что, вполне вероятно, у Эльбруса компилятор недоработан. Иначе как объяснить то, что в трансляции C версии 3-ий сэмпл выходит быстрее на 5%, чем при компиляции под E2K? За счёт этого у Эльбруса и выходит в среднем эффективность трансляции 87% (все 94%, если сравнивать с вариантом без доп. оптимизаций компилятором). У Apple же стабильно 79% в среднем на всех 3 сэмплах, что говорит о том, что компилятор на macOS и транслятор Rosetta 2 работают всегда примерно с одинаковой эффективностью. Но, опять же, Rosetta 2 иногда глючит, и мы видим просадки по эффективности с 78% до 23% на ровном месте. Мда...
Гистограмма 82. Тест dav1d из C кода на Эльбрус 8С (с-O4 и -ffast) и на Macbook Pro с Apple M1 (-O3).
Гистограмма 83. Тест dav1d из C кода на Эльбрус 8С (с-O4 и -ffast) и на Macbook Pro с Apple M1 (-O3).
В целом же, если смотреть на вариант сборки из C кода на обеих платформах с максимальной оптимизацией при помощи компилятора (опции -O4 и -ffast на Эльбрусе и опция -O3 через meson на Macbook Pro с Apple M1), выходит так, что Macbook быстрее в среднем в 4.1 раза. Ну, как бы Apple периодически не косячили с ПО, у них не отнять того, что инженеры там работают в самом деле гениальные и из своей платформы они вытягивают едва ли не максимум возможного. Я и год назад поражался тому, что может Macbook на Apple M1, и сейчас я также не меньше им восхищаюсь, как бы Apple там не лажали местами.
Гистограмма 84. Тест dav1d из C кода под x86 (-O3) в трансляции на Эльбрус 8С (RTC 4.1) и Macbook Pro с Apple M1.
Гистограмма 85. Тест dav1d из C кода под x86 в трансляции на Эльбрус 8С (RTC 4.1, -O3) и Macbook Pro с Apple M1.
Если сравнить в трансляции сборку под x86 с -O3 опцией на Эльбрусе и без этой опции на Mac (я бы сравнил и с ней, если бы не глюк Rosetta 2, приведший к падению производительности более чем в 3 раза), имеем разницу в 3.75 раза в пользу Macbook Pro с Apple M1. Да, разница велика.
Не вижу смысла смотреть на разницу Ассемблера под Apple M1 с C кодом на Эльбрусе, т.к. и так понятно, что там будет (если вам так интересно, пролистайте вверх и гляньте сами на скрины). Я же попробую сейчас глянуть, насколько велика будет разница при сравнении транслированного Ассемблерного кода под x86 на обеих платформах (Эльбрус 8С и Apple M1).
Гистограмма 86. Тест dav1d из под x86 (Ассемблер) в трансляции на Эльбрус 8С (RTC 4.1) и Macbook Pro с Apple M1.
Гистограмма 87. Тест dav1d из под x86 (Ассемблер) в трансляции на Эльбрус 8С (RTC 4.1) и Macbook Pro с Apple M1.
Я проверил: при трансляции сборки из Ассемблера под x86 на обеих платформах разница между ними выходит в 3.87 раза. В общем, примерно такая же разница, как и при трансляции сборки из C кода (если не брать в учёт глюк Rosetta 2 со сборкой из C кода с оптимизацией -O3).
Гистограмма 88. Тест dav1d на Macbook Pro с Apple M1 (из C кода с -O3 и из Ассемблера под x86 с Rosetta 2).
Гистограмма 89. Тест dav1d на Macbook Pro с Apple M1 (из C кода с -O3 и из Ассемблера под x86 с Rosetta 2).
Вот ещё, что меня интересовало: а у Apple их компилятор из C кода в машинный ARM код эффективнее, чем трансляция Ассемберного x86 кода?
Ну, да, тут отрыв в среднем в 91% у варианта из Ассемблера под x86, работающего в трансляции с Rosetta 2. У RTC на Эльбрусе был отрыв в 104% в пользу варианта с Ассемблером под x86. Т.е. в целом можно, наверное, говорить о том, что lcc компилятор у МЦСТ работает с эффективностью, схожей у gcc и clang в macOS 12 Monterey от Apple. Учитывая то, какие бюджеты у Apple и какие у МЦСТ... Это потрясает.
Последний вопрос, который в случае с маком у меня возник: а почему же Rosetta 2 в прошлом году была столь не эффективной в тесте dav1d?
Гистограмма 90. Результат Macbook Pro с Apple M1 в тесте свежей версии dav1d (Ассемблер) с Rosetta 2 и без.
Гистограмма 91. Результат Macbook Pro с Apple M1 в тесте свежей версии dav1d (Ассемблер) с Rosetta 2 и без.
Как видите, на макбуке эффективность Rosetta 2 с последней версией dav1d в среднем составила 60%. Это при тестировании версии, собранной из Ассемблерного кода для ARM и для x86. Но ведь в прошлом году мы наблюдали картину с падением производительности более чем в 4 раза при использовании Rosetta 2. Что же изменилось?
Гистограмма 92. Результат Macbook Pro с Apple M1 в тесте старой версии dav1d (0.8.2) с Rosetta 2 и без.
Я попросил Рифата также провести тест со старой версией dav1d 0.8.2, с которой я тестировал Macbook около года назад, пока писал на него обзор.
Гистограмма 93. Результат Macbook Pro с Apple M1 в тесте старой версии dav1d (0.8.2) с Rosetta 2 и без с macOS 11.2.
Я посмотрел на результаты: они соотносятся с тем, что я наблюдал около года назад. Но меня вот что напрягло: в версии 0.8.2 результат в трансляции у вариантов из C кода и из Ассемблера на 1-м сэмпле почти не отличается у Рифата. Вполне может быть, что и год назад при тестировании это не Rosetta 2 оказалась безумно неэффективной (я тогда ругался на просадку аж в 4.4 раза), а проявился просто баг тогдашней версии dav1d, из-за которого 1-ый сэмпл был декодирован неэффективным кодом. В новой версии dav1d проблему, видимо, исправили, да и в целом стало всё быстрее.
Скриншот 104. Тест dav1d (0.9.3-git-6aaeeea6) на Эльбрус 16С на Альт 10 с трансляцией RTC 4.1 (Ubuntu 20.04.3) и без.
Далее я провёл тесты на инженерном образце Эльбруса 16С. Я смутился, увидев результаты. Ранее мы видели отрыв примерно в 3 раза у Эльбруса 16С в сравнении с Эльбрус 8С. Но сейчас же разница была сопоставима с той, что мы видели ранее, только в трансляции с RTC 4.1. Почему же без трансляции отрыв у 16С оказался меньше?
Скриншот 105. Недогруженность Эльбруса 16С при тестировании dav1d.
Как оказалось, dav1d, собранный из C кода под 16С, оказался просто неспособен нагрузить полностью все 16 ядер процессора. Я пробовал и старые версии dav1d, и с ними процессор был ещё менее нагружен. Во дела.
Гистограмма 94. Результат инженерной версии Эльбрус 16С в тесте dav1d (-O4, -ffast) в трансляции (RTC 4.1) и без.
Гистограмма 95. Результат инженерной версии Эльбрус 16С в тесте dav1d (-O4, -ffast) в трансляции (RTC 4.1) и без.
Вот что меня напрягло на 16С: это то, что код на C со всеми доступными оптимизациями (-O4 и -ffast при компиляции под E2K и -O3 через meson при компиляции под x86) быстрее в трансляции в среднем на 17% в сравнении с нативным исполнением.
О чём это говорит? О том, что компилятор на 16С ещё сырой и он в случае с dav1d не задействует все возможности процессора. Хотя, подобная ситуация у нас была и на Эльбрус 8С. В идеале всё, что собирается нативно, должно быть намного быстрее трансляции аналога на x86, но это не всегда так в случае с Эльбрусом.
Как мне кажется, необходимо доработать компилятор для того, чтобы на 8С и 16С результаты стали намного выше. Но не мне судить, я не эксперт.
Гистограмма 96. Тест dav1d (из C кода, -O4 и -ffast) на Эльбрус 8С с Альт 10 и Эльбрус 16С с Альт 10.
Гистограмма 97. Тест dav1d (из C кода, -O4 и -ffast) на Эльбрус 8С с Альт 10 и Эльбрус 16С с Альт 10.
Видно, как сказалась недогруженность 16С при тестировании. Т.к. не все ядра были задействованы на тесте с C кодом, мы видим отрыв от 8С всего в 1.5 раза в среднем, тогда как в предыдущих тестах отрыв был почти в 3 раза.
Гистограмма 98. Тест dav1d (сборка под x86 из C кода с -O3) на Эльбрус 8С и Эльбрус 16С с RTC 4.1 (Ubuntu 20.04.3).
Гистограмма 99. Тест dav1d (сборка под x86 из C кода с -O3) на Эльбрус 8С и Эльбрус 16С с RTC 4.1 (Ubuntu 20.04.3).
Если же сравнить результаты в трансляции при использовании RTC 4.1 на Эльбрус 8С и Эльбрус 16С, разница выходит уже в 2 раза. Это ближе к тому, что мы видели в предыдущих тестах, но, я уверен, что можно больше выжать из 16С и это определённо сделают к релизу. Сейчас же мы смотрим на результаты инженерной версии 16С с частично отключенным кэшем, с ОС под предыдущие версии Эльбруса, со старой версией компилятора (к слову, RTC на 16С также собран старой версией lcc), и с пониженной частотой оперативной памяти (её ограничили до 2400 МГц).
Гистограмма 100. Тест dav1d (сборка под x86 из Ассемблера) на Эльбрус 8С и Эльбрус 16С с RTC 4.1 (Ubuntu 20.04.3).
Гистограмма 101. Тест dav1d (сборка под x86 из Ассемблера) на Эльбрус 8С и Эльбрус 16С с RTC 4.1 (Ubuntu 20.04.3).
Вот это уже больше похоже на то, что мы видели ранее. Если софт хорошо оптимизирован под x86 платформу, он при трансляции на 16С работает намного быстрее на 16С, нежели на 8С. Как видите, отрыв составил аж 2.61 раза в среднем. Это круто. В трансляции 16С довольно сильно вырывается вперёд. Но, конечно, хотелось бы, чтобы компилятор собирал софт так, чтобы на E2K он работал быстрее, чем в трансляции пахал тот же код, собранный под x86. Я имею в виду, чтобы стал лучше компилятор. Пожалуйста, не тролльте меня и не ухудшайте транслятор.
Касательно RTC: круто. Он по эффективности не уступает трансляции с Rosetta 2 от Apple. Я такого и не ждал. ExaGear, по идее, тоже должен быть крут, но, видимо, не хватает оперативной памяти на малине (4 ГБ).
Гистограмма 102. Сравнение результатов аппаратов в dav1d в нативе (сборка из C кода с -O4).
Свести все результаты в одну единую гистограмму, чтобы было нагляднее, не получится, т.к. данных я собрал чересчур много. Но если постараться самые основные данные рассмотреть, получится та картина, которую вы видите выше. Учитывая, что у Эльбруса 8С техпроцесс 28 нм, а у того же макбука – 5 нм, я просто диву даюсь производительности Эльбруса. Сравнивая инженерный образец 16С на 16 нм с моим i7 8550U (14 нм) на 25 Ватт в ноутбуке Xiaomi, я понимаю, что по производительности Эльбрус, хоть и уступает, но не сильно, и здесь всё упирается только в оптимизацию.
Гистограмма 103. Сравнение результатов аппаратов в dav1d при сборке из Ассемблера (на Эльбрусе трансляция RTC).
При сравнении результатов с dav1d при сборке из Ассемблера, я делаю вывод, что единственное, чего не хватает Эльбрусу, чтобы превзойти в производительности аналоги на том же техпроцессе – это оптимизация ПО.
Скриншот 106. Результат тестирования dav1d версий 0.9.3-git-6aaeeea6 и 1.0.0.
Уже после того, как я написал статью и отвёз Эльбрус 8С на студию, вышла обновлённая версия dav1d, 1.0.0. Я перепровёл тесты с ней на Эльбрус 16С по удалёнке. Первые 2 сэмпла декодируются за то же время (разница в пределах погрешности), что и с версией 0.9.3-git-6aaeeea6 (последняя, которая с Git была на момент теста), а вот по 3-ему сэмплу разница в 2 раза. Какой вывод тут можно сделать? В новой версии внесли серьёзные правки для ускорения декодирования ряда AV1 видео (в данном случае это был 10-бит сэмпл). Даже при сборке из C кода мы получили прирост в 2 раза. Однако dav1d по-прежнему не нагружает все 16 ядер Эльбрус 16С, и потому он всё ещё отстаёт от моего ноутбука Xiaomi. Т.е. в данном конкретном случае проблемы все связаны именно с тем, что код, который написан в dav1d на языке C, не лучшим образом распараллелен на процессорах с более чем 8 ядрами.
Короче говоря, картина +- та же с новой версией, только на одном из трёх сэмплов декодирование проходит в 2 раза быстрее. В целом dav1d не грузит 16С полностью, и потом результат не афигеть какой впечатляющий. Нужно хорошо распараллелить код, чтобы он быстро обрабатывался Эльбрусом.
На этом мы завершаем подглаву с dav1d и двигаемся дальше.