Как уменьшить размер двоичного файла WASM?

У меня есть проект, написанный на C++, и платформа, на которой он будет развернут, имеет ограничение размера двоичного файла в 256 КБ.

Набор инструментов — wasi-sdk-16.0 clang++, мы используем этот компилятор для компиляции исходного кода в двоичный файл в формате WASM. На этом этапе мы компилируем исходники со следующими CXX_FLAGS.

-fstack-protector-strong -Os -D_FORTIFY_SOURCE=2 -fPIC -Wformat -Wformat-security -Wl,-z,relro,-z,now -Wno-psabi

Затем мы strip бинарный файл с

strip -s output_binary.wasm

После описанных выше шагов размер скомпилированного двоичного файла на этом шаге составляет 254 КБ.

Затем мы используем wamrc в WAMR для компиляции двоичного файла WASM со средой выполнения AOT, команда показана ниже.

wamrc --enable-indirect-mode --disable-llvm-intrinsics -o output.aot output.wasm

размер выходного двоичного файла становится 428 КБ, что намного больше ограничения (256 КБ).


После того, как я погуглил, я использую wasm-opt, чтобы уменьшить размер,

wasm-opt -Oz output.wasm -o output.wasm

Размер стал на 4 КБ меньше. (почти бесполезно)


Я попытался подтвердить, насколько мой код влияет на размер двоичного файла, поэтому я пишу простой минимальный пример кода, который называется стандартной библиотекой C++, как показано ниже:

#include <vector>
#include <iostream>

int main() {
    std::vector<int> vec = {1,2,3,4,5};
    for (auto v: vec) {
        std::cout << v << " ";
    }
}

Размер двоичного файла после компиляции действительно стал 205 КБ.


Я также пытался использовать профилировщик размера двоичного файла (twiggy), чтобы отслеживать размер каждой части бианри, но этот инструмент не смог открыть этот двоичный файл.


Поэтому я хочу спросить

  1. В то время как просто включение двух стандартных заголовков С++ приводит к тому, что размер двоичного файла достигает ограничения размера, как я могу удалить стандартную библиотеку С++ с помощью функции, которую я действительно использую (я не могу использовать флаг удаления неиспользуемой функции, потому что мой проект является библиотекой, предоставляемой другим), или действительно ли стандартная библиотека С++ повлияла на размер двоичного файла?

  2. Существуют ли какие-либо другие флаги компилятора, флаги полосы или любые другие инструменты оптимизации, которые могут значительно уменьшить размер двоичного файла?

C++ iostream довольно громоздкий, включает много библиотечного кода. Очень важно, какие два заголовка вы использовали; например #include <algorithm> будут только шаблонными функциями, поэтому вы будете платить только за то, что используете. То же самое с #include <vector>; это довольно легкий вес.

Peter Cordes 07.12.2022 21:08

Попробуйте построить все с включенной LTO (Link Time Optimization). Помимо обычно более быстрого кода, это также часто приводит к меньшим двоичным файлам.

Jesper Juhl 08.12.2022 02:35

Компоновщик также может выполнять некоторые собственные оптимизации (помимо LTO). Вы можете включить их с помощью -Wl,-O, и это иногда также может уменьшить размер двоичного файла (немного).

Jesper Juhl 08.12.2022 02:44

Также обратите внимание на опцию компоновщика --gc-sections. Это может быть полезно для вас.

Jesper Juhl 08.12.2022 02:48

Хотя предполагается, что параметр компилятора -Os оптимизирует размер, он не всегда дает наименьший возможный двоичный файл. Попробуйте также поэкспериментировать с -Og, -O1, -O2 и -O3.

Jesper Juhl 08.12.2022 03:00

Часто вы можете сэкономить место, переупорядочив элементы struct/class таким образом, чтобы уменьшить количество отступов, которые компилятор должен вставлять между членами. Это часто может сэкономить много места для нетривиальных программ. См., например, ibm.com/docs/en/i/…

Jesper Juhl 08.12.2022 03:19

Еще одна вещь, которую вы можете сделать, это убедиться, что только функции/типы, которые вам нужно экспортировать, имеют не скрытую видимость. См. gcc.gnu.org/wiki/Visibility

Jesper Juhl 08.12.2022 03:25

Объявление structs и classs, которые вам не нужны/не хотят наследовать от as final, также может в некоторых случаях помочь оптимизатору компилятора генерировать более быстрый, а иногда и меньший код.

Jesper Juhl 08.12.2022 03:28

Предполагается, что параметр компилятора -Oz более агрессивно оптимизирует размер, чем -Os — попробуйте.

Jesper Juhl 08.12.2022 03:41

Также проверьте инструмент sstrip, чтобы, возможно, сбрить еще несколько байтов из ваших файлов.

Jesper Juhl 08.12.2022 03:47

Вместо того, чтобы редактировать ответ в вопросе, опубликуйте свои ответы как правильный ответ на вопрос. Вы даже можете щелкнуть галочку «принять» в своем собственном ответе, чтобы показать будущим читателям, что это сработало для вас.

Peter Cordes 08.12.2022 17:05
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
4
11
197
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Одна из вещей, которые я не увидела в вашем посте, это wabtwasm-strip. Я недостаточно знаком с ним, чтобы знать, делает ли он что-то большее, чем простая команда strip, но, возможно, стоит попробовать. Вы можете установить его с помощью apt install wasm-strip в системе Debian.

Судя по нескольким микротестам, которые я видел в Интернете, двоичные файлы C++ wasm имеют большие накладные расходы. Для совершенно ненаучного слайда вы можете посмотреть это.

Если по какой-либо причине вы не можете работать с языком для создания двоичных файлов меньшего размера, вы можете попытаться оптимизировать также на уровне ссылок, как это делает busybox.

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

Я решаю эту проблему, просто заменяя iostream и fstream на cstdio. Он уменьшает размер с 254 КБ до 85 КБ, потому что в iostream содержится слишком много шаблонов.

Использование iostream Количество функций (с readelf -Ws) Размер двоичного файла Да 685 254 КБ Нет 76 85 КБ

Хотя указание флагов компилятора, таких как -Oz, также немного уменьшает размер, но основной фактор заключается в том, что слишком много кода было сгенерировано из шаблонов. Таким образом, не используйте потоковый API C++ (и любые другие библиотеки общего назначения с большим количеством шаблонов), когда существуют ограничения на размер двоичного файла. (В библиотеки C всегда стоит верить.)

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

user253751 07.01.2023 06:06

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