Rustプログラムの複雑度を可視化する:サイクロマティック複雑度の測定と閾値設定
こんにちは、ファスト株式会社のYTです!
「脳に収まるコードの書き方」という本を読み、「サイクロマティック複雑度」を計測しようというプラクティスが興味深かったので、Rust製のプロジェクトに応用する際にどのように応用できそうか調査しました。
サイクロマティック複雑度とは
コードのなかの分岐の数を計測し、メソッドの複雑さを教えてくれます。
「脳に収まるコードの書き方」 p257 付録Aより引用
1つの関数内での分岐や、ループが増えるほど大きくなる値です。
以下、CCNと略します。
本書では、7を閾値とすることを提案していましたが、
- Rustのプロジェクトにそのまま閾値として使用できそうか
- プロジェクトの最初から設定する値で妥当そうな値はどれくらいか
- プロジェクトの途中から設定する値で妥当そうな値はどれくらいか
- プロジェクトの種類によって変わるのか
という観点で調査しました。
Rust製OSSのプロジェクトを測定してみた
lizard
というツールで測定しました。
使用方法は以下の記事を参考にさせていただきました。
以下のプロジェクトを測定させていただきました。
- https://github.com/denoland/deno
- https://github.com/alacritty/alacritty
- https://github.com/BurntSushi/ripgrep
計測する際に使用したコマンドは以下の通りです。
$ lizard -l rust --CCN={上限値} -L=100000
計測では、
- 7: 本書で提案されていた値
- 15:
lizard
でデフォルトで警告が出る値 - 40: 15 × 2 + α
の3種類の閾値を設定し、約何パーセントの関数、何個の関数が閾値を超えているかを計測しました。
計測結果は長くなるため、サマリーだけを記載しています。
deno
deno
はJavaScript, TypeScript, WebAssemblyのランタイムです。
言語処理系のプログラムであるため、複雑な関数が多いと予想します。
CCN7以上の計測
==========================================================================================
Total nloc Avg.NLOC AvgCCN Avg.token Fun Cnt Warning cnt Fun Rt nloc Rt
------------------------------------------------------------------------------------------
293325 27.8 2.7 141.7 6391 400 0.06 0.26
400 / 6391 ≒ 6.6% の関数が閾値超過となりました。
CCN15以上の計測
==========================================================================================
Total nloc Avg.NLOC AvgCCN Avg.token Fun Cnt Warning cnt Fun Rt nloc Rt
------------------------------------------------------------------------------------------
293325 27.8 2.7 141.7 6391 121 0.02 0.15
121 / 6391 ≒ 1.9% の関数が閾値超過となりました。
CCN40以上の計測
==========================================================================================
Total nloc Avg.NLOC AvgCCN Avg.token Fun Cnt Warning cnt Fun Rt nloc Rt
------------------------------------------------------------------------------------------
293325 27.8 2.7 141.7 6391 12 0.00 0.05
12 / 6391 ≒ 0.2% の関数が閾値超過となりました。
alacritty
Rust製のターミナルエミュレーターです。
デスクトップアプリケーションの場合はどのくらいの複雑度になるのでしょうか?
CCN7以上の計測
==========================================================================================
Total nloc Avg.NLOC AvgCCN Avg.token Fun Cnt Warning cnt Fun Rt nloc Rt
------------------------------------------------------------------------------------------
25443 14.8 2.5 117.3 1000 56 0.06 0.34
56 / 1000 ≒ 5.6% の関数が閾値超過となりました。
CCN15以上の計測
==========================================================================================
Total nloc Avg.NLOC AvgCCN Avg.token Fun Cnt Warning cnt Fun Rt nloc Rt
------------------------------------------------------------------------------------------
25443 14.8 2.5 117.3 1000 8 0.01 0.19
8 / 1000 ≒ 0.8% の関数が閾値超過となりました。
プロジェクト開始時からCIで注視すれば0にできそうですね。
CCN40以上の計測
==========================================================================================
Total nloc Avg.NLOC AvgCCN Avg.token Fun Cnt Warning cnt Fun Rt nloc Rt
------------------------------------------------------------------------------------------
25443 14.8 2.5 117.3 1000 2 0.00 0.16
2 / 1000 ≒ 0.2% の関数が閾値超過となりました。
ripgrep
Rust製のCLIツールです。
CLIツールの場合はどれくらいになるのでしょうか。
CCN7以上の計測
==========================================================================================
Total nloc Avg.NLOC AvgCCN Avg.token Fun Cnt Warning cnt Fun Rt nloc Rt
------------------------------------------------------------------------------------------
38093 13.3 1.9 70.1 1208 39 0.03 0.18
39 / 1208 ≒ 3.2% の関数が閾値超過となりました。
CCN15以上の計測
==========================================================================================
Total nloc Avg.NLOC AvgCCN Avg.token Fun Cnt Warning cnt Fun Rt nloc Rt
------------------------------------------------------------------------------------------
38093 13.3 1.9 70.1 1208 6 0.00 0.07
6 / 1208 ≒ 0.5% の関数が閾値超過となりました。
プロジェクト開始時からCIで注視すれば0にできそうですね。
CCN40以上の計測
==========================================================================================
Total nloc Avg.NLOC AvgCCN Avg.token Fun Cnt Warning cnt Fun Rt nloc Rt
------------------------------------------------------------------------------------------
38093 13.3 1.9 70.1 1208 1 0.00 0.01
1 / 1208 ≒ 0.0% の関数が閾値超過となりました。
まとめ
主観になりますが、以下のように感じました。
- デスクトップアプリケーションやCLIツールのようなアプリケーション開発であれば7か15を目安にすると良さそう。
- プロジェクトの途中から導入する場合は40を目安に導入し、徐々に値を下げていくことで徐々に複雑さを下げていくことができそう。
- 言語処理系のプログラムはアプリケーションよりも複雑度が高そう。
新しくRustでプログラムを作る際に、閾値を7に設定して導入してみようと思いました!
参考文献
-
脳に収まるコードの書き方
https://www.oreilly.co.jp/books/9784814400799/ -
循環的複雑度(wikipedia)
https://ja.wikipedia.org/wiki/循環的複雑度 -
lizard(github)
https://github.com/terryyin/lizard
Discussion