概要
STM32のCortex-M系のCMSIS DSPのarm_fir_fast_q31のパフォーマンスがおかしかったので、原因と対処方法についてメモを残しておきます。
過去のこちらを参考にさせていただき、今回改めて調査して対応した内容をまとめてみました。
条件
- STM32のCortex-M4/M7系のマイコン (確認済みはSTM32F446,G431,L432,L4R5,H743)
- CubeIDE 1.16.1
- GNU Tools for STM32 (12.3rel1)
- CMSIS DSP 1.16.2
- CMSIS DSPの
Include
,PrivateInclude
,Source
をプロジェクトフォルダ内に配置 - 最適化オプションは-O3または-Ofastを使用 (->どちらでも結果は同じ)
- Predefineに
ARM_MATH_CM4(or7)
,ARM_MATH_LOOPUNROLL
をセット
原因
Include/dsp/none.h
内の以下のmultAcc_32x32_keep32_R
マクロのコンパイル結果がSMMLAR
にならないことが原因です。
#define multAcc_32x32_keep32_R(a, x, y) \
a = (q31_t) (((((q63_t) a) << 32) + ((q63_t) x * y) + 0x80000000LL ) >> 32)
デバッグのDisassemblyでarm_fir_fast_q31内のマクロ使用箇所を確認すると、SMMLAR
が使用されていません。
ちなみにSMMLAR
は丸め有りの32bitx32bit=32bit(H)の積和演算(参照)です。
過去の参照記事では、-O3または-Ofast設定で最適化をかけることでSMMLARが使用される、といった内容が書かれていますが、そうなっていないので今回記事にしてみた次第です・・・
対応方法
none.h
の問題のマクロをSMMLAR
のinlineアセンブラに置き換える。
#define multAcc_32x32_keep32_R(a, x, y) \
__ASM ("smmlar %0, %1, %2, %3" : "=r" (a): "r" (x), "r" (y), "r" (a) )
SMMLARは4つのレジスタ
を指定する必要がありますので、マクロの引数に合わせてレジスタを記述します。特に今回の場合、1つ目の出力先のレジスタ%0
と4つ目の累積値を格納するレジスタ%3
が同じレジスタであることに注意してください。また、__ASMの後ろにvolatileをつけても良いかもしれません(今回は無くても動作しています)。
デバッグのDisassemblyでマクロ使用箇所を確認するとSMMLAR
が使用されていることが確認できます。
結果
STM32G431を使用し、36tapsのフィルタ、240samplesの入力データ数でarm_fir_fast_q31を実行したCYCCNT測定結果は以下の通りです。
CYCNT数 | |
---|---|
対応前 | 53187 |
対応後 | 24774 |
よって、50%以上の負荷削減となりました。
考察
今回はSMMLARがコンパイル時に使用されないことが原因でしたが、同様にnone.hで定義されているmultSub_32x32_keep32_R(SMMLSRのマクロ),SMMULR(mult_32x32_keep32_Rのマクロ)などでも同じ問題が発生すると考えられます。よって、これらも今回と同じ方法(inlineアセンブラで置き換え)をすべきと考えます。
ただし、そもそもコンパイルでマクロが想定通りのinstructionになればこの対応は必要ないので、スマートにコンパイルの設定で解決する方法を探しています・・・