flutter/docs/engine/Reduce-Flutter-engine-size-with-MLGO.md
Kate Lovett 1fbcbb73a0
[wiki migration] Leftover wiki pages and links (#148989)
This is waiting on 
- https://github.com/flutter/flutter/pull/148777
- https://github.com/flutter/flutter/pull/148790

After this PR lands, there will likely be 1-2 more clean up PRs, after which the migration will be done!

---

This moves the remaining wiki pages as planned in [flutter.dev/go/migrate-flutter-wiki-spreadsheet](https://docs.google.com/spreadsheets/d/1x65189ZBdNiLRygpUYoU08pwvXD4M-Z157c6pm8deGI/edit?usp=sharing) 

It also adds the team labels to the label bot for future PRs.

Changes to the content were only updating cross links, or links to refer to the main branch rather than master.
Remaining links to the wiki will be updated once all other pages have finished moving, they still work in the meantime.

Part of https://github.com/flutter/flutter/issues/145009
2024-05-28 15:12:10 +00:00

234 lines
9.1 KiB
Markdown

Mobile apps are very sensitive to their download sizes as every increase in KB
may result in a user number decrease. Flutter engine (`libflutter.so`) has to be
included in every Flutter app and it has a size of several MBs. So we'll try to
reduce its size by using [MLGO][MLGO]. It's different from the previous Flutter
attempt of reducing sizes as MLGO does not require any code or dependency
removals.
Reducing engine size with MLGO needs to 1) train a model once, 2) apply that
model to compile the Flutter engine. Note that model training does not need to
happen too frequently - the model should 'hold up' to code changes over
weeks/months. On Ubuntu, do the following:
- **Follow the [Setting-up-the-Engine-development-environment][engine setup]**.
To check if this step is successful, try [compiling for Android][compile
android]. For size comparisons, we recommend using the release Android build
`./flutter/tools/gn --android --runtime-mode=release --no-goma`. (Option
`--no-goma` is needed if you're not a Googler, or if you're using a custom
Clang as we'll do later with MLGO.)
- **Set up [MLGO] LLVM**. (The steps are adapted from [MLGO demo][MLGO demo].)
1. Prerequisites: `sudo apt-get install cmake ninja-build lld`.
2. Create a root directory for everything MLGO related `mkdir ~/mlgo &&
export MLGO_DIR=~/mlgo`
3. Clone MLGO repo
`cd $MLGO_DIR && git clone https://github.com/google/ml-compiler-opt.git`
4. Tensorflow dependencies
```bash
cd $MLGO_DIR
sudo apt-get install python3-pip
python3 -m pip install --upgrade pip
python3 -m pip install --user -r ml-compiler-opt/requirements.txt
TF_PIP=$(python3 -m pip show tensorflow | grep Location | cut -d ' ' -f 2)
export TENSORFLOW_AOT_PATH="${TF_PIP}/tensorflow"
mkdir $MLGO_DIR/tensorflow
export TENSORFLOW_C_LIB_PATH=$MLGO_DIR/tensorflow
wget --quiet https://storage.googleapis.com/tensorflow/libtensorflow/libtensorflow-cpu-linux-x86_64-1.15.0.tar.gz
tar xfz libtensorflow-cpu-linux-x86_64-1.15.0.tar.gz -C "${TENSORFLOW_C_LIB_PATH}"
```
5. Clone llvm-project
```bash
cd $MLGO_DIR && git clone https://github.com/llvm/llvm-project.git
export LLVM_SRCDIR=$MLGO_DIR/llvm-project
export LLVM_INSTALLDIR=$MLGO_DIR/llvm-install
```
6. Build LLVM
```bash
cd ${LLVM_SRCDIR}
mkdir build
cd build
cmake -G Ninja \
-DLLVM_ENABLE_LTO=OFF \
-DCMAKE_INSTALL_PREFIX= \
-DTENSORFLOW_C_LIB_PATH=${TENSORFLOW_C_LIB_PATH} \
-DCMAKE_INSTALL_RPATH_USE_LINK_PATH=On \
-C ${LLVM_SRCDIR}/clang/cmake/caches/Fuchsia-stage2.cmake \
${LLVM_SRCDIR}/llvm
ninja distribution
DESTDIR=${LLVM_INSTALLDIR} ninja install-distribution-stripped
```
- **Build Flutter engine for MLGO training**
```bash
# Set your engine dir appropriately if it's not in the default location
export ENGINE_DIR=~/flutter/engine/src
cd $ENGINE_DIR
sed -i \
's/cflags += lto_flags/cflags += lto_flags + ["-Xclang", "-fembed-bitcode=all"]/' \
build/config/compiler/BUILD.gn
sed -i \
"s/prefix = rebase_path(\"\/\/buildtools\/\$host_dir\/clang\/bin\", root_build_dir)/prefix = \"${LLVM_INSTALLDIR//\//\\/}\/bin\"/" \
build/toolchain/android/BUILD.gn
./flutter/tools/gn --android --runtime-mode=release --no-goma --no-lto
ninja -C out/android_release
```
- **Train the model**
```bash
export CORPUS=$MLGO_DIR/corpus
cd $MLGO_DIR/ml-compiler-opt
python3 compiler_opt/tools/extract_ir.py \
--cmd_filter="^-Oz$" \
--input=$ENGINE_DIR/out/compile_commands.json \
--input_type=json \
--llvm_objcopy_path=$LLVM_INSTALLDIR/bin/llvm-objcopy \
--output_dir=$CORPUS
export DEFAULT_TRACE=$MLGO_DIR/default_trace
export WARMSTART_OUTPUT_DIR=$MLGO_DIR/warmstart
export OUTPUT_DIR=$MLGO_DIR/model
rm -rf $DEFAULT_TRACE && \
PYTHONPATH=$PYTHONPATH:. python3 \
compiler_opt/tools/generate_default_trace.py \
--data_path=$CORPUS \
--output_path=$DEFAULT_TRACE \
--compile_task=inlining \
--clang_path=$LLVM_INSTALLDIR/bin/clang \
--llvm_size_path=$LLVM_INSTALLDIR/bin/llvm-size \
--sampling_rate=0.2
rm -rf $WARMSTART_OUTPUT_DIR && \
PYTHONPATH=$PYTHONPATH:. python3 \
compiler_opt/rl/train_bc.py \
--root_dir=$WARMSTART_OUTPUT_DIR \
--data_path=$DEFAULT_TRACE \
--gin_files=compiler_opt/rl/inlining/gin_configs/behavioral_cloning_nn_agent.gin
# The following will take about half a day.
rm -rf $OUTPUT_DIR && \
PYTHONPATH=$PYTHONPATH:. python3 \
compiler_opt/rl/train_locally.py \
--root_dir=$OUTPUT_DIR \
--data_path=$CORPUS \
--clang_path=$LLVM_INSTALLDIR/bin/clang \
--llvm_size_path=$LLVM_INSTALLDIR/bin/llvm-size \
--num_modules=100 \
--gin_files=compiler_opt/rl/inlining/gin_configs/ppo_nn_agent.gin \
--gin_bindings=train_eval.warmstart_policy_dir=\"$WARMSTART_OUTPUT_DIR/saved_policy\"
```
- **Build LLVM with the trained model**
```bash
cd $LLVM_SRCDIR
rm -rf llvm/lib/Analysis/models/inliner/*
cp -rf $OUTPUT_DIR/saved_policy/* llvm/lib/Analysis/models/inliner/
mkdir build-release
cd build-release
cmake -G Ninja \
-DLLVM_ENABLE_LTO=OFF \
-DCMAKE_INSTALL_PREFIX= \
-DTENSORFLOW_AOT_PATH=${TENSORFLOW_AOT_PATH} \
-C ${LLVM_SRCDIR}/clang/cmake/caches/Fuchsia-stage2.cmake \
${LLVM_SRCDIR}/llvm
export LLVM_INSTALLDIR_RELEASE=$LLVM_INSTALLDIR-release
ninja distribution
DESTDIR=${LLVM_INSTALLDIR_RELEASE} ninja install-distribution-stripped
```
- **Build Flutter engine using LLVM with the trained model**
```bash
cd $ENGINE_DIR
git stash # Undo previous changes for model training
sed -i \
's/cflags += lto_flags/cflags += lto_flags + ["-mllvm", "-enable-ml-inliner=release"]/' \
build/config/compiler/BUILD.gn
sed -i \
"s/prefix = rebase_path(\"\/\/buildtools\/\$host_dir\/clang\/bin\", root_build_dir)/prefix = \"${LLVM_INSTALLDIR_RELEASE//\//\\/}\/bin\"/" \
build/toolchain/android/BUILD.gn
./flutter/tools/gn --android --runtime-mode=release --no-goma --no-lto
ninja -C out/android_release libflutter.so
```
- **Compare**. To compare the engine size with or without MLGO, one can add or
remove the `["-mllvm", "-enable-ml-inliner=release"]` flags in
`build/config/compiler/BUILD.gn`, compile the engine, and check the size of
`out/android_release/lib.stripped/libflutter.so`. As end-users will download
zipped engine, we also recommend comparing its zipped size.
```bash
export ENGINE_LIB_DIR=$ENGINE_DIR/out/android_release/lib.stripped
cd $ENGINE_DIR
./flutter/tools/gn --android --runtime-mode=release --no-goma --no-lto
ninja -C out/android_release libflutter.so
cd $ENGINE_LIB_DIR
mv libflutter.so libflutter.ml_nolto.so
zip libflutter.ml_nolto.so.zip libflutter.ml_nolto.so
cd $ENGINE_DIR
./flutter/tools/gn --android --runtime-mode=release --no-goma
ninja -C out/android_release libflutter.so
cd $ENGINE_LIB_DIR
mv libflutter.so libflutter.ml_lto.so
zip libflutter.ml_lto.so.zip libflutter.ml_lto.so
# Remove the ML flags to disable ML.
cd $ENGINE_DIR
sed -i \
's/cflags += lto_flags + \["-mllvm", "-enable-ml-inliner=release"\]/cflags += lto_flags/' \
build/config/compiler/BUILD.gn
cd $ENGINE_DIR
./flutter/tools/gn --android --runtime-mode=release --no-goma --no-lto
ninja -C out/android_release libflutter.so
cd $ENGINE_LIB_DIR
mv libflutter.so libflutter.noml_nolto.so
zip libflutter.noml_nolto.so.zip libflutter.noml_nolto.so
cd $ENGINE_DIR
./flutter/tools/gn --android --runtime-mode=release --no-goma
ninja -C out/android_release libflutter.so
cd $ENGINE_LIB_DIR
mv libflutter.so libflutter.noml_lto.so
zip libflutter.noml_lto.so.zip libflutter.noml_lto.so
ls -l
```
Here's the table of size comparisons for engine version b9ecd8a.
| Flutter engine size comparison | ML_LTO | ML_NOLTO | NOML_LTO | NOML_NOLTO |
| --- | --- | --- | --- | --- |
| unzipped size (bytes) | 6270960 | 6338580 | 6312012 | 6577684 |
| zipped size (bytes) | 3586091 | **3577604** | 3606484 | 3689468 |
| unzipped size change over NOML_LTO | -0.65% | 0.4% | 0 | 4.21% |
| zipped size change over NOML_LTO | -0.57% | **-0.80%** | 0 | 2.3% |
| unzipped size change over NOML_NOLTO | -4.66% | -3.64% | -4.04% | 0 |
| zipped size change over NOML_NOLTO | -2.80% | -3.03% | -2.25% | 0 |
## Conclusion
As shown in the table above, for the zipped size, the winner here is the
ML_NOLTO version which is even smaller than the ML_LTO version. It has a 0.8%
reduction over our previous art of NOML_LTO.
The ML_LTO version is not very good because currently the model can only be
trained without LTO. [MLGO][MLGO] is planning to allow ThinLTO in their
training. Hopefully, it will help achieve the MLGO's normal reduction of 3%-5%
(e.g., ML_NOLTO vs NOML_NOLTO) when the training and final build are in the same
condition.
[MLGO]: https://github.com/google/ml-compiler-opt
[engine setup]: ./contributing/Setting-up-the-Engine-development-environment
[compile android]: ./contributing/Compiling-the-engine#compiling-for-android-from-macos-or-linux
[MLGO demo]: https://github.com/google/ml-compiler-opt/blob/main/docs/demo/demo.md