Почему сборка пакета Rust для aarch64 занимает в 2 раза больше времени, чем для x86-64?

Контекст: на работе я компилирую набор пакетов для Intel64 и ARM64 и объединяю их в пакеты Linux (.rpm, .deb, .apk). Для этого я создаю целый конвейер, который будет использоваться в нашей установке Artifactory.

Мы создаем автономные собственные версии ARM64 для нашей системы GitHub Enterprise Server, которые будут работать вместе с нашими существующими автономными версиями Intel64. Наши бегунки построены на базе инстансов Amazon EC2. Оба оптимизированы для вычислений.

ЦП Арка Тип экземпляра виртуальные процессоры Память uname -mamd64/x86_64c5.2xlarge 8 16 Гб x86_64arm64/aarch64c6g.2xlarge 8 16 Гб aarch64arm64/aarch64c7g.2xlarge 8 16 Гб aarch64

Хосты — это кластеры EKS, на которых работает Amazon Linux 2, оптимизированный для EKS.

Наши «бегуны» — это модули K8S/EKS (~Docker-контейнеры), которые умирают/возрождаются после каждого отдельного рабочего процесса. Образ Docker — это многоплатформенный образ: то же программное обеспечение, та же конфигурация, несколько процессоров. Контейнерная ОС — Ubuntu «Focal Fossa» 20.04 LTS.

Используя наши собственные средства запуска GitHub Actions, я написал рабочий процесс GHA для загрузки исходного кода проекта Rust и его компиляции — один раз на процессоре Intel и один раз на процессоре ARM64. Я запускаю uname -m и вывожу результат как первый шаг рабочего процесса и вижу то, что ожидаю. Я также запускаю file скомпилированный двоичный файл и вижу то, что ожидаю.

(Я очень стараюсь провести здесь сравнение яблок с яблоками.)

Я создаю https://github.com/lycheeverse/lychee как тестовый проект конвейера. Я (пока) не тестировал другие компиляции, но это показалось мне достаточно сложным, чтобы протестировать новые бегуны ARM64.

Вот скрипт сборки (${ARCH} — это x86-64 или aarch64, в зависимости от ситуации):

sudo apt-get -y update
sudo apt-get -y install --no-install-recommends \
  build-essential \
  ca-certificates \
  curl \
  file \
  git \
  gpg \
  gpg-agent \
  gzip \
  libssl-dev \
  openssh-client \
  pkg-config \
  software-properties-common \
  tar \
  wget \
  ;

# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source "${HOME}/.cargo/env"

# shellcheck disable=2154
wget --header "Authorization: Bearer ${GITHUB_TOKEN}" \
    "https://github.com/lycheeverse/lychee/archive/refs/tags/v0.15.1.tar.gz"
tar zxvf "v0.15.1.tar.gz"

### Start measuring time

cd "lychee-0.15.1/" || true
cargo fetch --target = "${ARCH}-unknown-linux-gnu" --locked
cargo install cargo-auditable --locked

# The `mold` linker is pre-installed.
mold -run cargo auditable build --timings --frozen --release

sudo install -Dm755 target/release/lychee -t "/usr/local/bin/"

### Stop measuring time

Бегунок Intel на одно поколение старше бегуна Graviton/ARM64. Те же виртуальные процессоры, тот же объем доступной памяти. За измеренный промежуток времени (сценарий выше) я вижу следующие результаты (в среднем из 5 сборок):

  • Intel (c5): 6 минут 18 секунд (базовый уровень)
  • Гравитон 2 (c6g): 12 м 2 с (~ 2 раза)
  • Гравитон 3 (c7g): 9 минут 36 секунд (~ 1,5 раза)

Я ожидал паритета между двумя архитектурами ЦП или, может быть, небольшого преимущества для ARM64, поскольку экземпляр Graviton на одно поколение новее. Я также знаю, что такие языки, как Haskell, все еще работают над внедрением ARM64, и мне интересно, верно ли то же самое для Rust.

Для любителей Rustace: есть ли части конвейера сборки Rust, которые еще не оптимизированы для ARM64 в Linux на базе glibc?

Далее я собираюсь попытаться создать важный проект на Go, просто чтобы попробовать другой язык, который, как я знаю, оптимизирован для ARM64, и попытаться исключить проблемы с процессорами Graviton. Я также собираюсь создать еще один представительный проект Rust, чтобы посмотреть, получу ли я другие результаты.


Обновление (в тот же день): я выполнил тот же тест в проекте Go (OpenTofu). Он содержит более 300 000 строк кода, а также зависит от нескольких внешних зависимостей, которые необходимо загрузить и скомпилировать.

  • Intel (c5): 3 мес 35 с (базовый уровень)
  • Гравитон 3 (c7g): 2 мин 18 с (~0,64x)

Здесь arm64 было улучшение на 36 % по сравнению с экземпляром Intel второго поколения. Поэтому я не думаю, что моя проблема с Rust связана с ложью Amazon о соотношении цены и качества Graviton. Я думаю, это связано с чем-то конкретно о Расте или личи.

связывание может быть тяжелым для памяти, вы можете нажать на своп, если он у вас есть.

Stargateur 17.06.2024 23:32

Перед этим я тестировал экземпляр m6g.2xlarge. Но для модулей/раннеров на базе Docker для CI мы ограничили 4 ГБ ОЗУ. Вы предполагаете, что в сборке Arm64 есть что-то, что требует больше памяти, чем сборка x86_64?

Ryan Parman 18.06.2024 00:34

Кроме того, я использую компоновщик mold, который должен работать намного быстрее, чем lld, gold или ld.

Ryan Parman 18.06.2024 01:34

Я сказал «может», логичное объяснение, если вы не нажмете кнопку подкачки памяти, заключается в том, что она работает медленнее.

Stargateur 18.06.2024 15:23
Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
4
96
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Чтобы повысить производительность Rust на Graviton, вам следует указать использование расширений для больших систем (LSE) через RUSTFLAGS перед созданием проекта. LSE включен в архитектуру Armv8.1 и повышает общую пропускную способность системы. Все Graviton 2 и последующие версии, основанные на линейке процессоров Neoverse, включают функцию LSE.

Включите LSE с помощью следующей строки кода перед командой cargo build --release:

export RUSTFLAGS = "-Ctarget-feature=+lse"

Итак, после установки sudo apt-get ваш окончательный код должен выглядеть так:

# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
source "${HOME}/.cargo/env"

# shellcheck disable=2154
wget --header "Authorization: Bearer ${GITHUB_TOKEN}" \
    "https://github.com/lycheeverse/lychee/archive/refs/tags/v0.15.1.tar.gz"
tar zxvf "v0.15.1.tar.gz"

### Start measuring time

cd "lychee-0.15.1/" || true
cargo fetch --target = "${ARCH}-unknown-linux-gnu" --locked
cargo install cargo-auditable --locked

# Set RUSTFLAGS to enable LSE
export RUSTFLAGS = "-Ctarget-feature=+lse"

# The `mold` linker is pre-installed.
mold -run cargo auditable build --timings --frozen --release

sudo install -Dm755 target/release/lychee -t "/usr/local/bin/"

### Stop measuring time

В итоге я отследил это на github.com/aws/aws-graviton-getting-started/blob/main/rust.m‌​d примерно в то же время, когда вы ответили. Но я очень благодарен за вашу помощь. Спасибо! Graviton 2 был примерно на 100% медленнее, чем его эквивалент x86_64. Гравитон-3 был примерно на 50% медленнее. Если вы установите это значение, в трех отдельных проектах скорость будет в среднем примерно на 27 % медленнее. Данные говорят мне, что компиляция на aarch64 сейчас просто медленнее. Меня это устраивает, теперь, когда у меня есть объяснение.

Ryan Parman 25.06.2024 03:37

Другие вопросы по теме