Одно из преимуществ работы на компанию, занимающуюся производством программ, заключается в том, что вам часто представляется возможность протестировать прототипы нового железа. Однако не в данном случае – я купил себе Raspberry Pi4 потому, что она очень дешёвая!
На Raspberry Pi4 стоит четырёхъядерный ARM Cortex A72, до 4 ГБ памяти и гигабитный порт Ethernet – и всё это всего за $35.
На Raspberry Pi4 есть ОС Raspbian (на основе Debian), и готовая библиотека продуктов, поэтому я вставил в неё SD-карточку, чтобы побыстрее загрузиться. Я искал syslog и заметил, что и ядро, и все пользовательские программы скомпилированы как armv7 – то есть, для 32-битной памяти.
Я знаю, что Raspberry Pi4 поддерживает 64 бита, поэтому я не захотел запускать на ней 32-битную ОС. Я взял другую карту памяти и поставил на неё Debian. Debian, не содержащий ничего лишнего, скомпилированный как aarch64 – что означает 64-битную память.
Загрузив 64-битную ОС, я заинтересовался, насколько лучше она работает 32-битной, поэтому провёл несколько тестов.
Синтетические тесты скорости
Первое, что пришло мне в голову, был старый тест dhrystone, существующий с начала времён. Эту программу написали в 1988 году, и она занимается математическими вычислениями. Она вряд ли способна симулировать современную нагрузку, и мы можем использовать её только для того, чтобы сохранить некую связность со старым железом и программами.
Современное приложение, обрабатывающее цифры, лучше симулировать вычислением хэшей, поэтому я захотел провести тест с SHA1. К сожалению, утилита sha1sum скомпилирована без поддержки libssl или криптографических функций ядра, поэтому мне пришлось компилировать её из исходников.
Чтобы избежать узких мест в I/O, я подсчитываю хэш файла размером 2 Гб с опцией truncate -s 2GB, так что никакого ввода и вывода данных с карты не было:
SHA1 – более реалистичный тест, чем dhrystone, поскольку этот алгоритм используется в большом количестве приложений – торрентах, git, и т.п.
RAM
64-битная система обеспечивает доступ к памяти по 8 байт на одно чтение/запись. Я написал простую программу, размещающую большой буфер – она его пишет, а потом читает. Чтобы гарантировать реальное выделение памяти я использовал mlock(). В данном тесте объём буфера равен 2 ГБ: буфер в 3 ГБ работал в 64-битном режиме, а в 32-битном выдавал ошибку «недостаточно памяти».
Кодирование аудио
Я обратил внимание на то, что многие пользователи Raspberry Pi4 используют компьютер в качестве медиацентра, поэтому я запустил задачи на кодирование аудио с двумя самыми популярными кодеками.
Я кодировал композицию Pink Floyd «Echoes», потому что это довольно длинный трек, и с него можно получить измеряемые значения. Чтобы избежать I/O задержек, исходник и конечный файл хранились на ramfs:
Сетевые замеры скорости
Ещё один вариант использования Raspberry Pi4 – в качестве VPN или файервола. Не рекомендую использовать такие системы в подобных целях, но у многих людей всё ещё есть медленное подключение к интернету (менее 100 Мб), поэтому они могут не обращать внимания на медленную работу Raspberry Pi4.
Первый вопрос: сколько трафика способна обработать Raspberry Pi4? Нам нужно измерить чистую сетевую мощность компьютера, без ограничений физических интерфейсов, поэтому я запустил сессию iperf3 между двумя контейнерами. Однако контейнеры обмениваются данными через пару veth, а veth ускоряет трафик посредством ложных разгрузок.
Разгрузка подсчёта контрольной суммы IP осуществляется просто отказом от её подсчёта, а разгрузка сегментации TCP – отказом от сегментации и повторной сборки трафика: большой кусок данных на 64К просто передаётся в память как есть.
Чтобы избежать таких моментов, я запретил разгрузку командой
ethtool -K veth0 tx off rx off tso off gro off gso off
Firewall
Самое быстрое, на что способно сетевое оборудование – отбрасывать часть трафика, а быстрее всего это делать через правило TC. Чтобы не доходить до максимально возможной скорости, я использовал минимальный размер фрейма Ethernet, 64б.
Хотя обе системы не дошли до максимальной скорости передачи (1,5 Мб/с), 64-битное ядро показало чуть большую скорость, чем 32-битное. Если вы хотите использовать Raspberry Pi4 в качестве файервола, обязательно используйте ядро на 64 бита.
VPN
Ещё один частый вариант использования Raspberry Pi4 – VPN-сервер, а точнее, OpenVPN. Я же предпочитаю WireGuard, поэтому я проверил обе программы, поскольку они обе отличаются простотой установки:
Как и ожидалось, OpenVPN в 10 раз медленнее WireGuard. Чего не ожидалось, так это того, что OpenVPN работает с одинаковой скоростью на 32 и 64 б. WireGuard почти насыщает гигабитный порт в обоих случаях – возможно, мы достигли предела NIC.
Чтобы узнать, не может ли WireGuard работать ещё быстрее, я провёл ещё один тест с двумя контейнерами, не задействующий физический Ethernet. Единственная проблема была в том, что и клиент, и сервер iperf3 работали на Raspberry Pi4, загружая два ядра.
Как и ожидалось, OpenVPN и 32-битный WireGuard, ограниченный CPU, отработали хуже, а 64-битный WireGuard отработал лучше.
Заключения
Я часто читаю заявления типа «оно того не стоит», «ты выиграешь несколько миллисекунд», и т.д., просто из-за того, что Raspberry Pi4 не особо мощный компьютер. Это не так! Как знает любой человек, занимающийся встраиваемым оборудованием, на медленном железе оптимизация софта ещё важнее, чем на быстром.
Я уже знал, что 64-битная ОС будет лучше работать на Raspberry Pi4, но я не знал, насколько лучше. Поэтому я провёл все эти тесты. Надеюсь, вам понравилось!
Goron_Dekar
Забыли главное: 64 битный указатель в 2 раза больше. И поэтому современные программы, обильно внутри себя усеянные указателями, начинают занимать существенно больше оперативной памяти.
maaGames
С одной стороны — правда. С другой — из-за выравнивания данных объём занимаемой памяти может и не поменяться. Смотря как компилировалось.
mistergrim
Больше — да, существенно — вряд ли.
uanet
Визуально сравниваем бинарники 32 и 64бит (i386 vs amd64) — и часто видим разницу порядка 1,5 раза. Поэтому, на 64бит фре порой приходилось выбирать: если нужна скорость — комиплим и используем 64бит бинарники, иначе (много-много непроцессорозависимых процессов) — 32бит. 64бит было быстрее благодаря большему количеству доступных регистров проца (это очень существенно), новым инструкциям. Очевидно, если бы эти фишки были доступны в 32бит приложениях — разница в скорости была бы меньше.
Выравнивание — нередко означает снижение эффективной пропускной способности памяти. Грубо говоря, если для доставания двух 32-бит аргументов физически нужно прочитать 2 участка по 64бит — мы ничего не выигрываем, а если они ещё и выравниваться все должны — то расход памяти для данных растёт вдвое.
klirichek
Я видел, как это широко используется в solaris. Разные системные бинари, типа chmod, passwd и прочее нересурсоёмкое — 32-битное. При этом ядро 64-битное. При сборке своей программы надо всего лишь указывать флажок в gcc.
А с выравниванием — отдельная тема. Надо понимать, что оно не всегда безопасно. Если на x86 можно положить 32-битное целое по нечётному адресу и это не вызовет проблем, кроме замедления, то уже атомик того же размера так не положишь. В подавляющем случае за этим всем следит компилятор. Но если вдруг стоит задача управлять этим вручную (например, компактно сериализовать данные без промежутков), то там кроме кастомного выравнивания обычно заодно применяются и другие штуки, вроде vbl-сжатия.
redsh0927
amd64 — закостыленный монстр, где инструкции раздуваются за счёт размазывания опкода и операндов по этим уродливым префиксам.
Врядли имеет смысл сравнивать их с арм/арм64 с фиксированной длиной команды.
khim
А почему нет? В x86-64 префиксы добавили, в ARM64 (официальное название — Aarch64), наоборот — убрали.
Теперь ввесто префикса нужна отдельная команда перехода.
Конечный результат-то всё равно один: 64-битный код немного больше. Но не в 1.5 раза, это какая-то анамалия.
slonopotamus
Допустим, но при чём здесь указатели?
Goron_Dekar
Проверить легко. Качаем Virtualbox, ставит 2 инстанса (32,64), качаем по Firefox/Chrom и смотрим потребление при открытии 4-5 страничек gmail/docs в виртуалках. Попробуйте, делов на 1.5 часа. Результат того стоит. У меня в тестах разница в потребляемой памяти была в 1.8 раз, но это было в 2014. Сейчас должно вырасти больше.
mistergrim
Ну если нам не хватает четырёх Гб на Pi4, то, наверное, мы вообще выходим за пределы возможностей малинки. А при достаточном объёме памяти — автор демонстрирует нам преимущества.
Goron_Dekar
На 4-х гигах вернее отбросить ~700MByte памяти и ставить 32 бита, чем ставить 64. Только это я и хотел сказать.
И, да, при условии когда у данной малинки важнее не свободная память, а скорость работы с 64-битными целыми или float, вероятно, всё-таки вернее ставить 64бит ось. Но этот случай гораздо реже при использовании малины, чем нехватка памяти и уход в свап. И уж точно ближе к формулировке «выходим за пределы возможностей малинки»
mistergrim
khim
Лучше всего вспомнить про первые 64-битные Sparc'и и использовать 64-битное ядро с 32-битными программами.
Впрочем там была шибко уважительная причина: поддержка 64-битных программ была небезопасной из-за ошибок в железе (в поздних stepping'ах вроде поправили, но подход на несколько лет остался).
Goron_Dekar
Если не упадём в swap. Если упадём — про производительность можно забыть.
MaM
devblogs.microsoft.com/oldnewthing/20031008-00/?p=42223 или нет, ктож его знает
blackjack88
Зато регистры стали в два раза толще, что может спасти от лишних обращений к памяти.
mistergrim
Если рассматривать конкретно x86, то регистров стало ещё и больше.
khim
В ARM их тоже стало больше.