コンテンツにスキップ

A survey on metric of software complexity

アブストラクト

  • ソフトウェア開発の進化により、ソフトウェアの規模はますます大きくなり、ソフトウェア開発の難易度が増している
  • ソフトウェアの複雑性を測定するための指標がいくつか提案されている
  • ソフトウェアの複雑性指標を包括的に調査
    • コードの行数(LOC)、Halsteadの複雑さの尺度(HCM)、サイクロマティック複雑度(CCM)といった、古典的かつ効率的なソフトウェアの複雑性指標について解説・分析
    • 次に、これらの古典的な指標を基にした他のアプローチについても考察
    • さらに、これらのソフトウェアの複雑性指標を比較し、その関連性を示す

はじめに

  • ソフトウェアのライフサイクルにおいて、開発と保守はどちらも高いコストがかかる
    • ソフトウェアの複雑さが軽減されれば、ソフトウェアのコストも削減される
  • 1970年代初頭に、ソフトウェアの複雑さが注目を集めた
  • モジュールを活用したプログラミングとオブジェクト指向プログラミングは、どちらもソフトウェアの複雑さを緩和してきた
    • しかし、ソフトウェアの規模が大きくなるにつれ、ソフトウェアの複雑さを制御しにくくなっていった

ソフトウェアの複雑さの定義と分類

A. ソフトウェアの指標

  • ソフトウェアメトリクスとは、ソフトウェアの特性や属性と定量化するための、数値評価を用いた測定基準

B. ソフトウェアの複雑さの指標

  • ソフトウェアの複雑さの指標は、本質的複雑さ(Essential Complexity)、選択的複雑さ(Selecting Complexity)、付随的複雑さ(Incidental Complexity)の3つに分けられる
    • 本質的複雑さ: ソフトウェアが解決しようとする問題によって決まる
    • 選択的複雑さ: プログラミング言語、問題のモデリング手法、ソフトウェア設計手法によって決まる
    • 付随的複雑さ: 実装に関わる開発者の質(スキルレベルや経験)によって決まる
  • ソフトウェアの複雑さを測定する目的は、付随的複雑さを管理・削減し、ソフトウェアの開発プロセスとソフトウェアそのものを改善すること

C. ソフトウェアの複雑さの指標の分類

  • メトリクスは、ソフトウェアのライフサイクルで使用されるタイミングによって分類できる
  • 8つのメトリクスセットの分類は以下の表のようになる
メトリクス ライフサイクル
設計 実装 完成 保守
McCabeの複雑さの尺度
Halsteadの複雑さの尺度
コードの行数
エラー数
オブジェクト指向クラスメトリクス
ソフトウェアパッケージメトリクス
凝集度
結合度
  • また、計算基準によってメトリクスを分類すると以下の表のようになる
メトリクス 対象
論理構造 ソースコード ユーザーフィードバック
McCabeの複雑さの尺度
Halsteadの複雑さの尺度
コードの行数
エラー数
オブジェクト指向クラスメトリクス
ソフトウェアパッケージメトリクス
凝集度
結合度

ソフトウェアの複雑さの典型的な指標

  • 1960年代から1970年代にかけて、LOC、HCM、CCMという3つの古典的かつ重要な指標が考案された
    • これらはソフトウェアを長さ、体積、構造という3つの異なる側面から測定

A. コードの行数(LOC)

LOCの定義と計算

  • LOCはソフトウェアのソースコードの行数
  • 通常、LOCは実行分のみを考慮
  • LOCはプログラミング言語に依存せず、物理的な長さによってソフトウェアの複雑さを評価

調査結果

  • LOCが200未満のFORTRANモジュールを対象に行われた研究では、モジュールのLOCが小さいほど、モジュールのバグ密度が高くなることが分かった
  • Pascal、PL/S、アセンブリ言語で書かれたソフトウェアに関する研究では、LOCが500以上の場合、モジュールが大きいほどバグ密度が大きくなることが分かった
  • その後の研究では、LOCとバグ密度の間にU字曲線の関係があることが示された
    • UnisysのプロジェクトにおけるAdaモジュールを調査した研究では、LOCとバグ密度の間に凹状の関係があることが示された
    • LOCが250に近い場合、モジュールのバグ密度が最も小さくなる
    • 1997年の研究でも、LOCだけを考慮すると200〜400のLOCが最適であると示されている
    • LOCの最適な大きさはプログラミング言語の種類とは無関係

LOCの利点と欠点

  • LOCは理解しやすく、計算が速く、プログラミング言語に依存しない
  • しかし、LOCは各コード行の複雑さの違いを無視
    • 例えば、変数に数値に代入している行よりも、変数に関数の戻り値を代入している行の方が複雑
    • そのため、後者のようなバグが発生する可能性が比較的高いコードが軽視されるリスクがある
  • また、LOCは分岐やジャンプといったソフトウェアの構造を無視
    • 例えば、分岐やジャンプのない長いコードと、分岐の多いコードを比較すると、後者の方がLOCが小さくなる
    • しかし、プログラミングの観点から見ると、後者の方がより複雑
  • そして、LOCはプログラマーの能力差を考慮していない
  • LOCはコード行とバグ密度の関係に基づいているが、バグ密度はプログラマーの能力、習慣、スキルと密接に関係している
  • そのため、LOCの最適な範囲はプログラマーによって異なる

B. Halsteadの複雑さの尺度(HCM)

HCMの定義と計算

  • HCMは演算子と被演算子の数に基づいて計算される

HCMの利点と欠点

  • HCMはソフトウェアの論理構造を深く分析する必要がなく、計算も容易で、プログラミング言語に依存しない
  • また、バグ密度の予測にも使用できる
  • しかし、HCMはデータフローの複雑さのみを考慮し、制御フローの複雑さは無視
    • HCMを使用すると、コードの演算子と被演算子が分岐やジャンプの有無に関わらず計算される
    • しかし、分岐やジャンプを含むコードはより複雑
  • また、\(S^\ast\)は基本的に固定されており、開発者の能力を十分に考慮していない

HCMの種類とその分析

  • HCMの欠点を克服するために、改良された手法が導入されている
Weighted HCM(WHCM)
  • 異なる演算子や被演算子に異なる重みを与える
  • さらに、ループ内のコードのHCMにループ回数を掛け合わせる
  • しかし、コードの実行回数はソフトウェアの実行時にのみ判断できるため、静的解析でWHCMを用いてソフトウェアの複雑度を推定できない
  • また、コードの実行回数はソフトウェアの複雑度ではなく、アルゴリズムの複雑度に属するため、不適切であるという考え方もある
  • そして、 WHCMはソフトウェア能力成熟度モデル(SW-CMM)を用いてプロジェクトのドキュメントを分析し、HCMを修正するが、それによってコーディングよりもプロジェクトマネジメントの側面が大きくなるため、WHCMはソフトウェアの複雑さの指標として不適切
Data Stream and Control Stream Mode(DCM)
  • データフローと制御フローを組み合わせて複雑さを測定
  • \(DC={1\over \beta\lambda}\sum_{i=1}^{n}[w_{1}\times{H(i)\over {Max}_{j\in n}(H(j))}+w_{2}\times{M(i)\over {Max}_{j\in n}(M(j))}]\)
    • 各要素の意味
      • \(n\): ソフトウェア内のモジュール(関数やクラスなど)の数
      • \(H(i)\): モジュール\(i\)のHalstead複雑度(データの複雑さ)
      • \(M(i)\): モ ジュール\(i\)の循環的複雑度(制御フローの複雑さ)
      • \(\text{W}_{1}\)\(\text{W}_{2}\): データフローと制御フローの重み(\(\text{W}_{1} + \text{W}_{2} = 1\)
      • \(\beta\): CMM成熟度レベルに基づく開発者のスキルレベル(0.2, 0.4, 0.6, 0.8, 1.0のいずれか)
      • \(\lambda\): 開発ツールの効率性
    • 式の意味
      • 各モジュールのHalstead複雑度と循環的複雑度を計算
      • 各モジュールの各複雑度を、全モジュールの各複雑度の最大値で割って正規化
      • データフローと制御フローの加重平均を計算
      • 全てのモジュールについて複雑度を合計
      • 開発者のスキルと開発ツールの効率性で割る
    • 問題点
      • 開発者のスキル\(\beta\)と開発ツールの効率性\(\lambda\)を客観的に評価する基準がない
      • 単一モジュールを分析する場合、式は\(DC=1 / (\beta\lambda)\)になり、複雑度がソフトウェア自体の特性を反映しなくなる
      • 正規化により、例えば、10個のモジュールのうち1つだけが非常に複雑で他は単純であった場合、\(DC\)は小さくなり、ソフトウェアの実際の複雑さをほとんど反映しない

C. 循環的複雑度(CCM)

  • CCMはモジュールの決定構造グラフ(制御フローグラフ)の複雑さを測る指標
  • プログラムの各コード行はノードとして表される
  • あるコード行から次に実行される可能性のあるコード行への流れはエッジとして表される
  • 制御フローグラフには必ず1つの入口ノードと1つの出口ノードがある
  • 計算方法
    • \(V(G)= e - n + 2\)
      • \(V\): 複雑度
      • \(G\): グラフ
      • \(e\): エッジの数
      • \(n\): ノードの数
    • \(V(G)\)は線形独立なパス(他のパスの組み合わせでは得られない一意なパス)の数

CCMの利点と欠点

  • バグ密度とCCMの間には強い相関関係があることが分かっている
  • しかし、CCMはソフトウェアのデータフローの複雑さを無視
    • 例えば、プログラムのパスが1通りしかない10,000行のコードと、1行のコードのCCMは同じになる
    • そのため、HCMとCCMは併用されることが多い
    • HCMはデータフローの複雑さを測定するために使用され、CCMは制御フローの複雑さを測定するために使用される
  • また、CCMはネストされたコードの複雑さを無視
    • 実際にはネストされたコードはより複雑
  • そして、CCMは制御フローの種類ごとの複雑さを区別しない
    • 例えば、CCMではif文とswitch文の複雑さは同じになる

CCMの種類とその分析

疑似パス測定モデル(Pseudo-Path Metric Model, PPMM)
  • \(V(G)=\sum_{i=1}^{n}{V(P_i)}\)
    • \(V(G)\): コード全体の複雑性
    • \(V(P_i)\): 特定の制御構造の複雑性
  • 分岐構造(if-else, switch-case)の場合
    • \(V(P_i)=W_i\times max(V(s_1), V(S_2))\)
      • \(W_i\): 分岐構造の重み
      • \(V(s_1)\): 分岐内のコードブロック\(s_1\)の複雑さ
      • \(V(s_2)\): 分岐内のコードブロック\(s_2\)の複雑さ
  • ループ構造(for, while)の場合
    • \(V(P_i)=W_i\times V(s_1)\)
      • \(W_i\): ループ構造の重み
      • \(V(s_1)\): ループ内のコードブロック\(s_1\)の複雑さ
  • ネスト構造が考慮されている
    • 再帰的に複雑さを計算するため、ネストが深くなるほど複雑さが指数関数的に増加
  • そして、異なる制御構造に異なる重みを与えることでCCMの一部の短所を解決している
  • しかし、重みの妥当性には疑問がある

指標の相関関係

  • CやC++で記述されたプログラムの分析研究では、LOCとHCM、LOCとCCMの間に非常に強い相関関係があることが示された
  • しかし、特定のメトリクスを別のメトリクスに置き換えることはできない
  • NASAのメトリクスデータプロジェクトの3つのデータセットを使用した研究では、複数のメトリクスを統合したソフトウェア欠陥予測モデルの方が、1つのメトリクスのみを用いたモデルよりもはるかに優れていることが示された

引用情報

  • 著者: Sheng Yu, Shijie Zhou
  • タイトル: A survey on metric of software complexity
  • 雑誌 / 会議名: 2010 2nd IEEE International Conference on Information Management and Engineering
  • ページ: pp. 352-356
  • 出版日: April 2010
  • DOI: https://doi.org/10.1109/ICIME.2010.5477581