MP3-кодек LAME — продолжаем исследования


Чем дальше в лес, тем больше дров

Итак, как мы уже видели, скорость работы Lame зависит не только от железа, но и от того, какую версию этой программы вам удалось найти на просторах интернета. Таким образом можно сказать, что «черные ящики» бывают разной черноты :) В этом материала мы попробуем разобрать ящик под названием Lame и узнать, что у него там есть внутри интересного для нас (и вообще, стоит ли в него соваться).

К сожалению, авторы Lame сами распространяют только его исходные тексты, так что все версии для Windows являются в некотором смысле «самодельными» и уж конечно практически отсутствует полное и исчерпывающее описание, как конкретно была скомпилирована данная lame.exe. Пользователям *nix немного проще, поскольку принято считать, что проекты с открытым кодом в первую очередь развиваются с прицелом под эти ОС. Для них есть и специальный скрипт (на целых 400 KB) который может настроить оптимальные (по мнению авторов) параметры компиляции для данной платформы.

Если же вы используете ОС от компании Microsoft, то поиск может привести на mitiok.cjb.net и www.hot.ee/smpman/mp3. По этим адресам можно скачать уже скомпилированный файл кодека lame.exe. Кроме того, есть некая версия и на www.mp3-tech.org.

Что касается информации, о том, что же представляют собой эти программы, то ее не так уж и много. В первом случае это файл about, в котором написано следующее:

  This file was downloaded from
     http://mitiok.cjb.net/
            mirrors:
      http://mitiok.ma.cx/
  Compiled with icl 4.5 & nasm
ACM compiled with MSVC 6.0 & NASM
 Please do not delete this file.
 exe & dll were compressed with
     UPX executable packer
  http://upx.sourceforge.net/
    mitiok (kuts@atrus.ru)

Для второго варианта информации (в этот раз в файле file_id.diz) не больше:

Lame 3.93.1 Stable  Win32 executable
Compiled by Intel C++ 4.5 compiler
MMX, SSE & 3DNow! code optimizations using Netwide-assembler
Downloaded from:
http://www.hot.ee/smpman/mp3/

Третья версия в принципе не имеет какого либо упоминания о том, как, чем и кем она была создана.

При этом программы внешне практически одинаковы, при запуске все рапортуют о версии «LAME version 3.93 MMX (www.mp3dev.org)» (вторая правда дополнительно замечает, что она именно с сайта www.hot.ee/smpman/mp3).

Попробуем сравнить, отличаются ли они в работе (тестовый файл 70 МБ, опции -m j -q 0 -V 4 --silent):

  • mitiok 2:46 мин
  • smpman 2:47 мин
  • mp3-tech 3:13 мин.

(Здесь и далее если не указано противное, тестирование проводится на компьютере P4 2.4/SiS645/512 MB DDR333)

Как мы видим (по крайней мере, на нашей платформе) отличие в скорости есть. Между первыми двумя версиями оно не очень значительно, однако определенно существует (тест мы проводили несколько раз и разница в 1 секунду, что составляет 0,6%, сохранялась). Кстати, сами полученные mp3 файлы совпадают до бита, так что вопрос о качестве можно не поднимать. Третий участник значительно отстает от конкурентов. Файл получается у него на несколько десятков байт больше. Если сравнить его побайтово с предшественниками, то мы увидим, что отличия начинаются примерно с середины. Так что налицо и разная работа алгоритмов.

В отличие, например, от «счетных» программ, где результат (обычно) должен сохраняться вне зависимости от используемого компилятора, оптимизации и других параметров, в данном случае мы имеем дело с неоднозначным алгоритмом сжатия звука с потерями. Эта особенность вносит в процесс тестирования очень неприятный момент называемый «субъективность». Качество работы кодека не определяется численно, его нельзя объективно проверить, сравнить, понять, где лучше, а где хуже (это, конечно, не совсем верно, но суть проблемы отражает). Так что даже на примере уже этих, скомпилированных, кодеков мы сталкиваемся с вопросом «А может у более медленного варианта гораздо лучше качество?».

Поскольку мы сейчас рассматриваем кодек с точки зрения быстродействия, то вопрос качества оставим на его совести, поскольку вносить еще один неопределенный параметр в и так большой набор настроек было бы мягко говоря опрометчиво.

В принципе на этом можно и остановиться: мы нашли три варианта кодека, попробовали их в работе, выбрали аутсайдера.

Однако существование заметно более быстрого кодека GOGO (основанного на Lame) и туманные намеки об использовании различных оптимизаций (например, компилятором под SIMD или даже наличие руками написанных на ассемблере кусков кода), заставляет нас двигаться дальше и попробовать разобраться, откуда могли появиться найденные варианты и можно как-то еще улучшить скорость такого замечательного Lame.

Первым шагом, конечно, будет получение исходников. Архив объемом около 1МБ можно скачать, например на www.sourceforge.org. Конечно немного смущает, что программа так и остановилась на версии 3.93.1 в далеком (по меркам компьютерной индустрии) декабре 2002 года.

Однако, памятуя о том, что старое не означает плохое, смело распаковываем архив и начинаем изучать файлы readme и install. И здесь начинает светить слабый лучик надежды — оказывается, есть готовый Makefile для MSVC. Ну что ж, попробуем быстрый вариант — не залезая в его дебри просто скомпилировать проект на Microsoft Visual.NET 2003… Всего 59 секунд (на P4 2.4) и (о чудо!) в текущей директории мы видим файл lame.exe. С замиранием сердца пробуем его в работе… 3:13 на тех же установках. Ну что ж, неплохо для начала (забавно, но mp3 файл снова с точностью до бита совпадает с первыми двумя, что были получены ранее). Итак, теперь можно написать свой file_id.diz и смело выкладывать (не забывая файлы COPYING, LICENSE, README и USAGE из исходного дистрибутива) архив в сеть!

В общем, процесс получения своей версии lame.exe оказался не так уж и сложен. Однако мысль о том, что у кого-то получилось лучше, тревожит и заставляет углубиться в изучение Makefile.MSVC в котором можно найти очень много интересного.

Первым шагом по оптимизации является изменение компилятора на Intel (использовалась версия 7.1). Ух сколько полезло warningов… Ничего! Прорвемся! Итак, вторая попытка — 3:28. Мрачно, даже хуже, чем MSVC :(. Отметим, что mp3 файл уже другой. На слух разницы, правда, нет. Отличия начинаются не с самого начала, а где-то со смещения в 0x30000 байт (всего файл объемом около 7МБ, то есть только первые 2% — одинаковые).

Пробуем выбрать «заточку» на процессор на этапе компиляции (также, не меняя ничего в исходных файлах) — теперь для Pentium III. Получили уже 2:58. Что заметно лучше прошлых вариантов, но до лучшего времени в 2:46 не дотягивает. Отметим, что полученный файл также отличается от всех предшественников.

На следующем шаге, наконец, подправим немного сам Makefile.MSVC в связи с «последними решениями ВЦСПС», учитывая, что у нас есть процессор Pentium 4 (конечно эта версия на Pentium 3 и Athlon XP не будет работать). Просто добавляем еще один вариант настройки на процессор и…. Ура! Мы почти достигли конкурентов! Теперь у нас всего 2:51.

Однако поскольку опций в принципе у компилятора ну очень много то, теоретически, можно проверить все варианты, однако уж очень это грустное и не сильно перспективное занятие. Тем более что для разных процессоров могут быть оптимальными разные наборы. Хотя если скриптик написать… :)

Ну что ж, пожалуй, на компиляторах C можно закончить. Нам практически удалось достичь лучшего результата, однако только с использованием оптимизации под Pentium 4, что не очень корректно. Но остается еще один шанс — использование ассемблерных вставок. Для их использования необходимо скачать компилятор NASM, благо это всего 214 КБ в архиве.

Напомним, что до этого момента мы использовали только чистый C код и «играли» опциями компиляторов через командную строку при компиляции и немного меняли Makefile. Теперь же попробуем использовать и ассемблерный код. В Lame он используется несколькими способами. Первый (и самый простой и практически ясный) — это использование MMX для выполнения вспомогательных действий с одной из рабочих таблиц. Для этого нужно скомпилировать программу с использованием ассемблера NASM. Больше ни для чего MMX (имеется ввиду ассемблерный код, а не результат работы того же компилятора Intel) в Lame не используется.

Пробуем варианты MSVC+ASM и MSVC+ASM+MMX: 3:13 и 3:05.

Видно, что просто само по себе использование ассемблера ничего не меняет, поскольку в этом случае никакие процедуры на нем не используются. Однако первый шаг — замена одной из процедур на аналог, написанный на ассемблере с использованием MMX, дает дивиденды.

Смотрим далее и … находим файл на ассемблере и «3dn» в имени. Ура! Вот уже что-то. При ближайшем рассмотрении «это» оказывается процедурой FHT, написанной специально для набора команд 3Dnow!. Идем дальше… Находим также файлы fftfpu.nas и fftsse.nas, радуемся, что и про Intel не забыли. Однако дальнейшее исследование показывает, что эти файлы просто не используются! Строки в Makefile про них закомментированы с указанием «not yet coded». На этом месте стоит сильно расстроиться. Поскольку продвигаться дальше можно только очень медленно и осторожно. Дело в том, что для полного разбирательства придется лезть в исходники. А когда их много и они еще и не твои и практически без комментариев… Что-либо найти очень сложно. А про исправить я уже и не говорю.

Кроме непосредственно оптимизированных функций на ассемблере написана и процедура определения возможностей процессора по использованию различных наборов SIMD. Именно она и используется для вывода красивой строки вида «CPU features: i387, MMX (ASM used), SIMD, SIMD2». Поскольку в принципе вариант FHT для 3DNow! предусмотрен авторами, то стоит попробовать и его. По логике программы он должен автоматически использоваться если при компиляции использовался ассемблер, а процессор, на котором запущен Lame, умеет работать с этим набором команд. Проверить на P4 работу оптимизации под 3DNow! не представляется возможным, так что достаем из под стола запылившийся Opteron 244 и пробуем разные версии на нем в надежде, что 3DNow! тоже будут использоваться:

  • mitiok 2:43
  • smpman 2:44
  • MSVC 3:12
  • MSVC+ASM 3:12
  • MSVC+AMS+MMX 3:09
  • Intel 2:47

Увы, чуда не произошло. Скорость работы MSVC версии от использования ассемблера практически не изменилась. Заметим, что при запуске этих модулей на Opteron программа рапортовала в том числе и о «3DNow! (ASM used)». Тем не менее, толку от этого никакого не было. На всякий случай попробуем отключить 3DNow! с использованием ключа командной строки, но и это не помогло. В общем такое поведение достаточно странно. Также интересным выглядит результат с компилятором Intel. На Pentium 4 этот вариант был самым медленным, а теперь он всего на 4 секунды отстает от лидера.

Попробуем теперь компилятор Intel с ассемблерными вставками. Проверка на Pentium 4 дала следующие результаты:

  • Intel + ASM(MMX): 3:15
  • Intel P3 + ASM(MMX): 2:51
  • Intel P4 + ASM(MMX): 2:41

В случае использования компилятора Intel мы видим значительный эффект от использования оптимизированной процедуры на ассемблере. При этом оптимизация компилятором кода на C также дает значительный эффект.

Все полученные варианты lame.exe ведут себя практически случайным образом, так что совершенно непонятно, на чем и почему стоит остановиться.

В целом мы видим, что использование компилятора Intel положительно сказывается на быстродействии, особенно если использовать оптимизацию под SIMD различных процессоров. Что касается ассемблера, то в оригинале предусмотрено всего две возможности — MMX для вспомогательной процедуры (дает положительный эффект) и 3DNow! для FHT (что в наших примерах совершенно ничего не дало).

Увы, дальнейшее исследование очень осложнилось всякими глупостями, которые устроили авторы в исходниках. Например — индикация использования (или просто наличия, с первого раза не понятно) разных SIMD и ассемблерных вставок при «пустом» запуске lame.exe не производится. Обязательно нужно начать кодирование какого-либо файла.

К сожалению понять, что, когда и как используется просто не представляется возможным — уж очень там все запущено. Можно конечно раскопать, что же именно значит надпись «MMX», которая появляется время от времени при запуске программы (причем иногда с комментарием «ASM used»). Но… это уже совсем далеко от нашей темы.

Тем не менее, разобраться хочется, так что собираем все силы — и снова в бой!

Итак, для начала разберемся с названиями, версиями и прочей информацией, которую lame печатает при запуске. Начнем с первой строки: «LAME version 3.93 MMX (www.mp3dev.org)». Эта строка формируется одним из модулей и, к сожалению, не содержит третий номер подверсии (т.е. последнюю «1» в реальной версии 3.93.1). Видимо авторы решили, что это не сильно нужно. Так что точно узнать версию по готовому исполняемому файлу невозможно. Идем дальше и встречаем загадочную запись «MMX». В данном случае она показывает, что при сборке использовался ассемблер и одна из процедур оптимизирована под команды MMX.

Вторая строка, «CPU features», выводится на экран в момент запуска теста в работу и отражает текущее представление о наличии в процессоре различных наборов команд. При этом ограничения командной строки (опция –noasm {набор}) накладываются на реальные возможности процессора. Что в целом немного странно выглядит, поскольку кажется, что SSE в процессоре можно отключить простой опцией lame.exe :).

Теперь попробуем включить закомментированные варианты ассемблерной оптимизации под i387 и SSE. После тяжелой непродолжительной работы получаем файл, в котором предусмотрено использование этих ассемблерных вставок. Отметим, что поскольку в оригинале они не использовались, то, возможно, их работоспособность не гарантируется. (Кстати отметим, что в этих исходных файлах есть надписи, указывающие на их родство GOGO-no coda). К сожалению простого варианта выбора процедуры FHT не предусмотрено, поэтому вариант i387/FPU получается из SSE отключением последнего в командной строке при запуске Lame.

Получаем (снова на Pentium 4):

  • Intel + ASM(SSE): 3:33
  • Intel P3 + ASM(SSE): 3:05
  • Intel P4 + ASM(SSE): 2:55
  • Intel + ASM(FPU): 2:35
  • Intel P3 + ASM(FPU): 2:15
  • Intel P4 + ASM(FPU): 2:07

Мда… Результаты прямо скажем обескураживающие. Еще раз понимаем, что ничего не понимаем. Неприятное ощущение.

Отметим, что эти варианты генерировали файлы, отличающиеся по размеру от предшествующих участников. Тем не менее, на слух и по продолжительности они не отличались. Если же поставить lame параметр для генерации файла с CBR, то отличие в длине исчезает, но файлы, тем не менее, останутся разными по содержимому.

В общем нам удалось получить вариант кодека, который быстрее представленных в сети и работает на большинстве современных процессоров. Это версия Intel + ASM(FPU). Версии с оптимизацией под P3/P4 даже быстрее, однако менее универсальны. К сожалению вопрос об их реальном использовании остается открытым. Во — первых, мы проверили скорость только на одной платформе. Во-вторых, только с одним вариантом настроек. Ну и в третьих — файл, который они генерируют, отличается от варианта кодеков от mitiok и smpman, так что есть потенциальная проблема с качеством.

Что касается представленных на разных сайтах готовых версий для win32, то можно предположить, что они получены с использованием ассемблерных вставок из комплекта поставки Lame (только MMX и 3DNow!), а для компиляции программ на C использовался продукт от компании Intel, который в свою очередь также использовал SIMD (видимо только MMX/SSE и с возможностью альтернативного исполнения на процессорах, их не поддерживающих).

Итого, суммируем полученную информацию:

  • Авторы Lame, к сожалению, увлеклись качеством работы кодека и оставили без внимания вопрос его быстродействия. С другой стороны если была поставлена задача написания портируемого кода, то она была выполнена.
  • Использование ассемблерных вставок с оптимизированными процедурами может изменить быстродействие кода.
  • Однако непосредственно величина и даже направление этого изменения зависят и от используемых ключей при запуске Lame.
  • Вопрос о сравнении качества работы кодеков напрямую мы не рассматривали, однако упомянем, что использование различных оптимизаций может повлиять на качество.
  • Тестирование программ, поставляемых в исходных кодах, является занятием для людей, у которых очень много времени и желания этим заниматься. Поскольку мало того, что результаты зависят от железа, настроек программы, исходных файлов так есть еще значительная зависимость от:
    • версии программы
    • компилятора
    • версии компилятора
    • ключей оптимизации

Конечно, подобрать самый быстрый вариант из серии «Вот версию взять не последнюю, а прошлую альфу, и на компиляторе Borland и еще библиотечку ту спросить у Васи и на Атлоне тогда просто летает!» технически возможно. Но вот нужно ли это? :)

Тем не менее, мне не сильно жаль времени, потраченного на проведенные тесты. Обидно конечно, что не получилось красивого варианта «а вот мы круче!», где путем несложных действий с компиляторами и их ключами получить ускорение работы раза так в полтора-два, но… не в этом суть. По крайней мере получен бесценный опыт общения с OpenSource проектами :) Пока единственным "красивым" вариантом использования таких тестов представляется работа со стандартным компилятором (возможно разным для разных ОС) и разумным минимумом оптимизаций.

PS Кстати, в сети можно найти и исходники GOGO-no-coda 3.12… и они даже компилируются MSVC+NASM. Как вы думаете, за какое время эта версия осилит наш пример? …. 0:31!




Дополнительно

iXBT BRAND 2016

«iXBT Brand 2016» — Выбор читателей в номинации «Процессоры (CPU)»:
Подробнее с условиями участия в розыгрыше можно ознакомиться здесь. Текущие результаты опроса доступны тут.

Нашли ошибку на сайте? Выделите текст и нажмите Shift+Enter

Код для блога бета

Выделите HTML-код в поле, скопируйте его в буфер и вставьте в свой блог.