1. 소프트웨어 복잡도
- 소프트웨어 개발의 핵심은 복잡도를 통제하여 필요 이상으로 복잡도가 높지 않도록 하는 일이다.
- 높은 복잡도를 가지는 프로그램은 신뢰성, 테스트 비용, 유지보수성 측면에서 좋지 않은 결과를 가져오기 때문이다.
- 프로그램 복잡도 통제를 위해 신뢰할 수 있는 척도를 사용하여 프로그램이 복잡도를 측정하는 것은 매우 중요한 작업이다.
- 1976년에 McCabe가 발표한 순환 복잡도가 가장 널리 사용되고 있으며 많은 정적 도구가 이 지표를 지원하고 있다.
- 프로그램을 제어 흐름 그래프로 변환 후에 구함
SW 변환 -> 제어 흐름 그래프 -> 순환 복잡도 측정 -> 복잡도 통제
2. 제어 흐름 그래프: Control Flow Graph
- 시작 노드: 제어 흐름 그래프의 시작을 나타내며 원으로 나타낸다.
- 종료 노드: 제어 흐름 그래프의 종료를 나타내며 원으로 나타낸다.
- 기본 블록: 기본 블록은 일련의 실행 가능한 문장들로 구성되며 각 블록 안의 문장들은 전체가 다 실행되거나 모두 실행되지 않도록 구성한다. 기본 블록은 박스로 나타낸다.
- 블록의 첫 문장을 통하지 않고서 블록 내의 문장들로 외부의 문장으로부터 분기가 되지 않음
- 기본 블록 마지막 문장을 통하지 않고 블록 외부에 있는 문장으로 분기가 되지 않는다.
- 간선: 기본 블록과 기본 블록 간의 실행 관계를 나타내며 화살표를 사용하여 표시한다. 만약 기본 블록 A로부터 기본 블록 B로 간선이 존재한다면 A가 실행된 후에 B가 실행될 수 있다.
int oddSum(int n) {
int total = 0;
for (int i = 1; i<=n; i++) {
if (i % 2 == 1)
total += i;
}
return total;
}
위의 예제 문제를 블록별로 나타내보았다.
블록 | 문장 |
B1 | int total = 0; i = 1 |
B2 | i<=n |
B3 | i % 2 == 1 |
B4 | total += i; |
B5 | i++ |
B6 | return total |
3. 순환 복잡도 구하기
총 3가지 방법
- E-N+2
여기에서 E는 제어 흐름 그래프 상에 있는 간선들의 개수이고, N은 노드들의 개수
- 영역들의 개수
제어 흐름 그래프 종료 노드로부터 시작 노드로 간선을 연결한다면 제어 흐름 그래프가 간선과 노드들에 의해 둘러싸인 여러 개의 영역들로 분할된다.
- 분기 노드 개수 + 1
앞서 살펴 본 코드의 순환 복잡도를 구해보자
- 9-8+2=3
- 영역1: 종료 노드부터 시작 노드까지의 영역, 영역2: B3에서 B5, 영역3: B5에서 B2 -> 3
- 분기 노드의 개수(2) + 1 = 3
4. 순환 복잡도 사용 사례
- Walsh는 1979년 임베디드 시스템 모듈이 순환 복잡도가 10 이상일 때 결함률이 확연하게 증가한다는 연구 결과를 발표하였다.
- Audi, BMW, Porshe, 폭스바겐, 클라이슬러의 연합체, HIS에서도 10을 순환 복잡도의 제한값으로 유지하도록 권고하였다.
- JSF(Joint Strike Fighter) AV C++ 코딩 표준의 AV rule 3에서는 20 이하로 유지할 것을 권고하였다.
- MISRA Peport 5에서는 15를 넘어서는 안된다고 기술하고 있다.
- RIAC(Reliability Information Analysis Center)
복잡도 | 신뢰성 |
10 이하 | acceptable |
30 이상 | questionable |
50 이상 | cannot be tested |
75 이상 | bad fix(결함을 수정하기 위해 수행한 작업이 새로운 결함을 도입한다) |