파티셔닝이 없는 시뮬레이션
이제 다중 시뮬레이션 프로세스를 시작할 필요가 있다. 각각의 프로세스는 우리가 모델링한 CPU 사용과 실시간 요구 특성을 만족시키는 옵션을 가지고 수행된다. 이러한 과정을 위해서 리스트 2에서와 같이 우리는 simmod를 여러 번 실행하는 간단한 스크립트를 사용할 것이다.
리스트 2의 스크립트는 시스템의 각 프로세스에 대해서 한번씩 simmod를 수행한다. 프로세스의 각 연결고리는 세마포어에 의해 구성되고, 세마포어는 그 연결고리의 첫 번째 프로세스에 -p (공급자) 옵션을 줌으로써 만들어진다. 각각의 이어지는 프로세스는 -c 옵션을 사용함으로써 이전 프로세스에서 만들어진 데이터를 소모하게 되며 다음 프로세스에 데이터를 공급한다.
io-audio, echo-cancel, Bluetooth 등 몇 가지 프로세스에 대해서 “입력”과 “출력” 데이터 흐름에 따라 CPU 사용량을 나누었다. 작업의 일부는 마이크로부터 들어오는 오디오에 대해서 처리되어야 하며, 일부는 스피커로 나가는 오디오에 처리가 되어야 한다. 비록 실제 시스템에서는 그 부하가 명백히 동일하지는 않겠지만 우리는 단순히 그 부하를 반으로 나누어 “in”과 “out” 태스크로 만들었다. 다른 모든 태스크에 대해서는, 우리는 직접 CPU 사용 퍼센트를 이용했다.
이 프로그램을 실행했을 때 시스템 로그는 즉시 리스트 3에서 보이는 것처럼 overrun 리포트를 기록하기 시작했다. (실제 로그는 수백 개의 이벤트를 포함하고 있지만, 리스트 3은 첫 번째 몇 개만을 보여주고 있다)
더욱 심각한 것은 비록 우리가 남아있는 CPU 사이클이 있음에도 불구하고 우리가 정한 실시간 요구사항을 만족하지 못하고 있다는 것이다.
그림 4에서 나타내고 있는 QNX Momentics짋 system profiler 스냅샷 그림을 보면, 약 3초 정도 범위의 스냅샷에서 유휴 CPU 시간이 전부 15.4%에 이른다는 것을 알 수 있을 것이다. 이는 상당히 많은 유휴 시간이며, 확대된 시간선 추적을 나타내는 그림 5에서는 그러나 여러 실시간을 만족하는 요구시간(realtime deadline)이 만족되지 않고 있음을 볼 수 있다.
그림 5에서 slogger (system logger) 시간선에 있는 검은 세로선은 실시간 요구시간을 놓친 것을 의미한다. 노란 화살표에 의해 표기되는 특정 이벤트는 Bluetooth Out 프로세스가 충분한 CPU 사이클을 얻는데 실패한 것을 나타내고 있다.
짙은 초록색으로 된 가는 가로선은 쓰레드가 Ready 상태로 동작을 대기하고 있으나 아직 CPU가 주어지지 않았음을 나타내며, 밝은 초록색 넓은 가로선은 현재 구동 중인 쓰레드를 나타낸다. 여기에서 알 수 있듯이 대부분의 쓰레드는 많은 시간을 동작을 위한 대기 “Ready to run” 상태로 보내게 되지만, 실제 주어진 실시간 요구시간 경계를 넘어서 CPU 부족 현상을 경험하고 있는 것이다.
우선순위를 적용한 문제 접근
이제 대부분의 다중 쓰레드를 잘 아는 엔지니어라면 위의 해결책은 우선순위의 장점을 사용하고 있지 않는 것을 알 것이다. 그러나 이 시스템은 각 프로세스들이 다른 프로세스에서 제공하는 데이터에 의존적이라는 사실 만으로 매우 복잡하다. 우리는 반드시 CPU 부족 현상과 Data 부족 현상을 동시에 해결해야 하며 이러한 사실은 우선순위 기반의 해결책을 얻기가 더욱 더 힘들게 한다. 예를 들어, 출력 오디오 고리에서 하나의 쓰레드 우선순위를 증가시키는 것은 충분하지 않다. 이유는 그 고리에서 어느 한 부분이 약해지면 프로세스는 쉽게 다음으로 전달하는 데이터를 굶주리게 하기 때문이다.
이러한 관점을 보여주기 위해서, 한 연결 고리에 있는 모든 요소의 우선순위를 높여서 데이터 부족 현상을 막을 수 있는지 시도해보자. 그렇게 하기 위해서, 우리는 리스트 4에서처럼 시작 스크립트를 약간 수정할 것이다.
이 스크립트에서 우리는 -p (priority) 옵션과 함께 QNX의 on 명령어를 사용하여 각 프로세스의 우선순위를 조절했다. 이 명령어는 코드의 변경 없이 기본 우선순위와 스케줄링 알고리즘을 변경해서 프로세스를 실행할 수 있다. 우리는 가장 “공평”한 스케줄링으로 Round-robin을 선택한다. (우선순위 값 뒤에 r을 줌)
안타깝게도 이 스크립트를 실행시킬 때 시스템은 멈춰 버린다. (참고: 실제로 Shell 입력을 할 수 없는 상황을 우리는 보통 전체 시스템이 멈춘 것처럼 표현하지만 사실은 그렇지 않다.) 그러면 이러한 멈춤 현상의 이유는 무엇일까? 이유는 Shell은 기본 우선순위인 10으로 동작하고 모든 simmod 프로세스는 Shell보다 높은 우선순위에서 동작하면서 남아있는 CPU를 모두 써버리기 때문이다. 이것은 우연한 결과로 보이긴 하지만, 대부분의 실시간 다중 쓰레드를 다루는 엔지니어에게는 매우 친숙한 상황일 것이다. 동작하려고 하는 쓰레드보다 높은 우선순위의 모든 쓰레드는 자신이 CPU를 넘겨주기 전까지 계속 동작을 할 것이다. 낮은 우선순위의 쓰레드는 모든 높은 우선순위 작업이 끝나기 전에는 결코 CPU를 얻는 기회를 잡지 못하게 된다.
이러한 시스템 멈춤 현상은 종종 완전 부하가 걸린 시스템에서 우선순위를 조정할 때 자주 나타난다. 이 예는 단지 우선순위 모델을 가지고 시스템 통합 단계에서 생길 수 있는 문제를 해결하는데 발생할 수 있는 문제점을 보여준다. Shell과 Debug agent의 우선순위를 조절하려는 여러 시도는 좀전에 본 것과 같이 시스템이 CPU를 얻지 못하는 상황이나 반응이 없는 상황으로 만들 수 있다. 유사하게도, 실제 시스템에서 이렇게 동시에 CPU와 Data 흐름의 요구사항을 만족시키면서 잘 동작하는 시스템을 만들기란 여간 어려운 것이 아니다. 이 해결책은 끝내 얻어 질 수 있겠지만, 이는 단지 경험에 의해서 다양한 튜닝과 실험을 거친 후에야 가능할 수 있다.
파티셔닝을 이용한 문제 해결
위 시뮬레이션은 너무 많은 태스크가 CPU를 공유하려고 하고 있으며, 우리는 이러한 다중 실행 구조에 대해서 제어를 하기 어렵다는 근본적인 문제를 보여주고 있다. 우선순위를 설정하는 것은 이 문제를 해결하는데 효과적인 답을 제공하고 있지 못하는데, 이는 쓰레드들이 다른 쓰레드로부터의 데이터에 의존하고 있기 때문이다. 만약 몇몇 쓰레드의 우선순위를 높이면 그 연결 고리에 있는 모든 우선순위를 높여야 할 것이다. 그렇지 않다면 데이터 흐림이 깨질 것이다. 그리고 모든 쓰레드에 우선순위를 올리는 것은 근본적으로 가지고 있는 문제를 유지시켜서 여전히 실행 방법을 미세 튜닝할 방법이 없다. (여기서 말하는 데이터 흐름의 문제는 높은 우선순위 쓰레다가 낮은 우선순위 쓰레드가 붙잡고 있는 리소스를 기다리면서 생기는 우선순위 역전 현상, Priority Inversion과는 다른 문제임을 주의하라.)
그렇다면, 좀더 빠른 CPU로 교체하거나 소스코드의 수정이 없이 위와 같은 문제를 좀더 잘 해결하는 방법이 있는지 확인해 보자. 우리의 목표는 audio, incoming audio, navigation 등 각각의 서브시스템의 부하를 적절히 재분산시킴으로써 좀더 효율적으로 CPU 시간을 사용하는 것이다. 우리는 또한 전체적인 성능 향상을 하기 위해서 유휴의 CPU 시간을 보다 효율적으로 사용하기를 원한다.
이러한 목적을 달성하기 위해 우리는 QNX Adaptive Partitioning이라는 기술을 사용할 것이다. Adaptive Partitioning은 주어진 프로세서를 가상의 CPU들로 쪼개어 각각의 파티션으로 나눈 다음 각 파티션은 설계자가 정의한 전체 CPU 처리 능력의 퍼센트를 사용하도록 하는 것이다. Adaptive Partitioning은 아래의 매우 중요한 두 가지 예외를 뺀다면, 다른 RTOS에서 제공하는 파티셔닝 방법과 유사하다.
● 여분의 CPU 사이클의 동적 할당 -Adaptive Partitioning에서는 파티션의 퍼센트가 고정되어 있지 않다. 만약 한 파티션이 이미 할당된 CPU 사이클을 모두 써버린다면 QNX Neutrino scheduler는 여유 처리 시간을 가진 파티션 중에 유휴 사이클을 재분배한다. 일반적인 파티셔닝 모델에서는 이렇게 사용되지 않은 사이클은 여전히 유휴 상태로 소모된다.
● 특별한 프로그래밍 기술이나 코드의 변경 불필요 - Adaptive Partitioning에서 파티션은 mailbox나 API 등 특별한 프로그래밍 기술을 필요로 하지 않고 다른 파티션과 상호통신을 할 수 있다. 그 결과로 이미 존재하는 코드를 수정할 필요 없이 이미 존재하는 시스템 구조에서 파티션을 조절하거나 새로 만들 수 있다. Adaptive Partitioning은 단지 RTOS의 스케줄러에만 영향을 미친다. 프로세스나 쓰레드는 사실상 파티션에서 동작하고 있는 사실조차 알 필요가 없는 것이다.
시뮬레이션 스크립트의 수정
코드의 수정 없이 동작할 수 있는 능력은 Adaptive Partitioning을 우리가 지금까지 했던 실험에 완벽하게 적용 가능하게 한다. 우리는 다음과 같은 작업을 할 것이다.
쪾 outgoing audio, incoming audio, navigation, system 등 각각의 데이터 흐름을 위해서 분리된 파티션을 만들기 위해 시뮬레이션 스크립트를 수정할 것이다. 파티션을 생성하기 위해서 이 스크립트는 adaptive partition들을 검색하고 수정하고 만들 수 있는 QNX aps 명령어를 사용한다.
쪾 그 프로세스의 데이터 흐름과 연관된 파티션에 각 프로세스를 구동시킬 것이다. 예를 들어 noise reduction 프로세스는 출력 오디오 파티션, audio mixing 프로세스는 입력 오디오 파티션 등등.
시간 측정 범위의 조정
Adaptive Partitioning을 가지고 시간 측정 범위의 크기를 조정할 수 있다. 즉 이것은 커널에 퍼센트가 계산되는 시간 간격을 알려주는 역할을 한다. 파티션을 공정하게 관리하기 위해서 커널은 주어진 시간 간격에 따라 각 쓰레드가 동작하는 시간에 대해 Microbilling을 수행한다. 커널은 각 파티션에서 사용된 시간 퍼센트를 예측하여 정보를 이용하여 어떻게 시간을 배분할 지를 조절한다.
이러한 시간 측정 범위가 커지면 커질수록 이미 정의된 CPU 퍼센트의 CPU를 각 파티션이 좀더 정확하게 사용할 수 있다. 또한 이 범위가 작아지면 작아질수록 CPU 퍼센트의 정확성은 줄어들지만 각 파티션에 할당되는 처리 능력을 스케줄러가 좀더 빨리 조정할 수 있게 된다. 왜냐하면 우리는 작은 시간 지연에 민감한 프로세스를 사용하고 있으므로, 측정 범위 기본값 100ms를 8ms로 줄여서 사용할 것이다. 이렇게 함으로써 커널은 불공평한 스케줄링에 보다 빨리 반응을 할 수 있다.
<저작권자 © AEM. 무단전재 및 재배포, AI학습 이용 금지>