ESP32コンパイルオプションチューニング

概要

コンパイルオプションを変更して、ESP32のサイズとベンチマークの差を検証してみました。通常はサイズ優先になっていますが、速度優先に変更することも可能です。

コンパイルオプションとは?

最適化オプション

基本的には最適化なしの0から、最大限最適化する3までの数字で最適化の度合いを指定します。それ以外にデバッグ用のものと、サイズ優先の最適化オプションがあります。

オプション備考
-Ogデバッグ情報が追加されており、最適化なし
-O0最適化なし
-O1ちょっとした最適化
-O2(高速)バランスの取れた最適化
-O3(非推奨)最大限の最適化
-Os(デフォルト)サイズ優先の最適化

実際のオプションは上記の6種類となります。-Ogはデバッガを接続した場合などに利用する特殊なオプションになります。デフォルトは-Osで、最適化のレベル的には1.1ぐらいになると思います。そのため-O0と-O1はほぼ利用する必要はありません。-O2はサイズが少し大きくなりますが、バランスがよい最適化であり、-O3はサイズが大きくなりすぎるのでEspressif公式的には非推奨になります。

Arduinoでは-Osのサイズ優先がデフォルトで、通常は変更することができません。ESP-IDFは-Ogのデバッグがデフォルトで-Osのサイズ優先と、-O2の高速も選択可能になっています。

バージョンによる差分

ESP32はライブラリのバージョンによって利用しているビルドシステムが異なります。とはいえ、本質的なコンパイラオプションにはあまり関係ありませんが、ベンチマークをしてみたところ、バージョンが新しいものほど遅くなっていました。

arduino-esp32ESP-IDF
2.0.7(2023/02/21)4.4.4
2.0.6(2022/12/23)4.4.3
2.0.5(2022/09/17)4.4.2
2.0.4(2022/07/06)4.4.2
2.0.3(2022/03/30)4.4.1
2.0.2(2021/12/23)4.4
2.0.1(2021/11/09)4.4
2.0.0(2021/08/31)4.4
1.0.6(2021/03/26)3.3.5

最近のバージョン一覧ですが、arduino-esp32でいうと1系と2系があって、その中にはESP-IDFが使われていて3系と4系があります。まだarduino-esp32では対応バージョンがリリースされていませんがESP-IDF5系もリリース済みで、多分arduino-esp32の3系になります。

GitHub - tanakamasayuki/esp32-arduino-test
Contribute to tanakamasayuki/esp32-arduino-test development by creating an account on GitHub.

最新情報は上記リポジトリでわりと頻繁に更新しているので確認してみてください。

実際の差

SoCScoreOPSizeGlobal変数
ESP32187.47-O0284,32922,232
ESP32717.83-O1266,83722,128
ESP32973.95-O2266,45322,128
ESP321001.23-O3268,97722,128
ESP32744.97-Os264,89722,128
ESP32581.99-Og267,58122,128

上記が私が独自にCoreMarkでベンチマークした値になります。ベンチマーク自体は別の記事で書きたいと思います。スコアは大きいほど早いのを表します。サイズはビルドしたときの表示されるプログラムの大きさで、同じくグローバル変数も差があまりありませんが計測しました。

結果をみると-O0は大きくて遅いので、使う必要はありません。デバッグ情報付きの-Ogより遅いですね。-O1もサイズ優先の-Osに負けています。

OPScore備考Size
-Os744.97サイズ優先264,897
-O2973.95バランス型266,453
-O31001.23速度優先(非推奨)268,977

必要な情報だけを抜き出すと上記になります。デフォルトはスコア744.97の-Os(サイズ優先)ですが、あと2段階高速化できます。そして、サイズを見てみると思ったより変わっていないのがわかると思います。

これはArduino環境でビルドするとESP-IDF部分はコンパイル済みのため、ビルドオプションを変更しても反映しません。そのためベンチマーク部分の少ないプログラムだけが影響しています。

-O3は公式的には非推奨ですが、サイズが大きくなっても大丈夫な用途であれば利用可能だと思います。

https://www.espressif.com/sites/default/files/documentation/esp32_datasheet_en.pdf

ちなみにデータシート上だと環境が異なりますが994.26とありました。ESP-IDF4系の-O2でベンチマークしたところ、これぐらいの値になりました。-O3にすると1000を超える値になるので、非常にフェアな条件でのベンチマークだと思います。

コンパイルオプションの変更方法

Arduino環境

通常は変更することができませんが、platform.txtを書き換えることで変更が可能です。とはいえ、このファイルを直接書き換えるのはおすすめしないので、差分だけ設定できるplatform.local.txtを編集するのがよいと思います。

  • C:\Users\%username%\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.7

バージョンや環境によって異なりますが、上記のような場所にplatform.txtがあるはずです。同じフォルダにplatform.local.txtを作って設定を入れることで変更可能です。

# EXPERIMENTAL feature: optimization flags
#  - this is alpha and may be subject to change without notice
compiler.optimization_flags=-Os
compiler.optimization_flags.release=-Os
compiler.optimization_flags.debug=-Og -g3

上記が編集前の最適化オプションになります。-Osのサイズ優先ですが、デバッグ時だけは-Ogにしています。

# EXPERIMENTAL feature: optimization flags
#  - this is alpha and may be subject to change without notice
compiler.optimization_flags=-O2
compiler.optimization_flags.release=-O2
compiler.optimization_flags.debug=-Og -g3

つまり、-Osの部分だけを任意のオプションに変更すればいいのです。上記は-O2にしてみたところです。ただし、この設定はArduino IDEを起動した瞬間にしか読み込まれません。変更した場合にはIDEを終了して、再度立ち上げる必要があります。

ベンチマークでは変更しつつ再起動してビルドしてスコアを記録と結構面倒な作業が必要になります。最近はM5Burnerなどを使ってビルドしたあとのバイナリファイルを配布する方法が出てきていますので、-O2や-O3でビルドしたものを配布するのも手だと思います。

PlatformIO環境

こちらはplatformio.iniで指定するだけなので簡単です。

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
board_build.f_cpu = 240000000L       ;240M(WiFi OK), 160M(WiFi OK), 80M(WiFi OK), 40M, 20M, 10M
monitor_speed = 115200
build_unflags = -Os
build_flags = -O3

上記のように最初から指定されている-Osをbuild_unflagsで消してから、-O3など新しいフラグを指定するのが定番のようです。ただし-Oオプションは最後に指定したものが有効になるので、消す必要は本来無いはずです。そして-Osをbuild_unflagsとbuild_flagsの両方で指定した場合、build_unflagsの方が強いので結果指定なしになって-O0扱いになるので注意しましょう。

ESP-IDF環境

menuconfigからビルドオプションを変更することができます。

Bootloader

指定できる場所が2箇所あるので面倒なのですが、ブートローダーのところは変更しないほうが良いです。ブートローダーだけに影響するので変更しないほうがいいです。そして-O0とかにするとサイズが大きくなってブートローダー領域に入らなくなります。

通常

「Compiler opsions」から変更します。

一番上に最適化オプションがありますね。

デフォルトが-Ogで、サイズ優先の-Os、速度優先の-O2、最適化なしの-O0になります。-O1と-O3は選択することができません。

強制設定

menuconfig経由だと-O3が指定できなかったので、強制的にコンフィグオプションを上書きする方法になります。基本的には最後に指定したオプションが有効になるのを利用します。基本的には正規の方法ではないのでおすすめしません。

Kconfig.projbuild
menu "MyConfig"

config FLAGS_STR
    string "Optimization options (Overwrite)"
    default "-Os"

endmenu

menuconfigから変更できるようにKconfig.projbuildに設定を追加します。

MyConfigというメニューの中に最適化オプションが追加されており、自由な文字列が設定可能になりました。

CMakeLists.txt

先程のままだと文字列が追加されただけで、コンパイルオプションが渡されていません。

target_compile_options(${COMPONENT_LIB} PRIVATE ${CONFIG_FLAGS_STR})

CMakeLists.txtの最後に上記の行を追加しまし。既存のコンパイルオプションの最後にCONFIG_FLAGS_STRを追加しています。ここでは先程設定したFLAGS_STRが変数に入った名前であるCONFIG_FLAGS_STRをしています。これによって、既存コンパイルオプションの最後に上書きするオプションを指定できるようになりました。

まとめ

基本的にはサイズ優先の-Osがいいと思うのですが、どうしても処理が間に合わないときには-O2や-O3を指定するのも仕方ない場合があると思います。その場合にはプログラムの容量が大きくなってしまうので注意してください。

とくに今回はベンチマークとCPUをよく使うが、コード量があまりないものを題材にしています。そのため-O3を指定してもそれほどサイズに影響がありませんでした。大きめのプログラムの場合にはフラッシュに入り切らなくなる可能性があったりします。

また、処理によってはプログラムサイズが大きくなるとキャッシュに乗りにくくなるので、遅くなる場合もあるので、最適化オプションを変更する場合には各種データを取りながら変更をしたほうが安全だと思います。

コメント