대학교 2학년 운영체제 시간에, 현재 수행중인 프로세스를 모니터링 하는 프로그램으로 팀프로젝트를 한적이 있었다. C++과 Qt를 사용한 조잡한(?) 프로그램이었지만 밤새가면서 나름 열심히 만든 기억이 난다.
CPU 사용률은 모니터링시에 꼭 필요한 데이터중 하나이다. 해당 컴퓨터(서버)가 현재 얼마만큼의 여유를 갖고 있는지에 대한 핵심적인 척도이기 때문이다. CPU 사용률이 낮다는 것은 현재 어느정도 서비스 하는데 여유가 있다는 것이고 높다는 것은 그렇지 않다는 이야기가 된다.
CPU 사용률 값을 구하기 전에 생각해봐야 할 것들을 간단히 정리하고 가자.
CPU load와 사용률
일반적으로 CPU load와 CPU 사용률은 거의 동의어로 쓰일때가 많다. 그런데 좀 더 따져보면 load는 부하량이라는 의미가 강하다.
예를 들어보자. 두개의 시스템이 있는데 두 시스템 모두 CPU 사용률은 100%라고 가정한다. 하나는 3개의 프로세스를 enqueue시킨 상태이고, 다른 하나는 6개의 프로세스가 enqueue된 상태이다. 여기서 6개중 3개는 전자의 시스템과 같은 프로세스라 가정하자. 그렇다면 누구의 load가 더 클 것인가? 조금만 생각해봐도 후자가 더 load가 크다는것을 알 수 있다.[1]
CPU 사용률의 특성
대기중인 프로세스가 많고, 프로세스가 CPU를 잡는 시간 즉, CPU-time이 길 수록 CPU의 사용률은 커진다. CPU-time은 instruction의 clock cycle과 #에 비례한다. 예를 들어 사칙 연산을 하는 프로그램이 있다고 할 때, CPU의 uarch가 개선되어 사칙 연산 instruction의 clock cycle이 줄어들었다면, 프로그램은 이 CPU에서 CPU-time이 줄어들 것이고 사용률 역시 줄어들 것이다.
또한 같은 일을 하더라도, 다른 명령으로 대체될 수 있다면 CPU-time이 줄어들 요지가 있다. vector 데이터의 경우 SIMD instruction을 사용한다면 같은 일을 하더라도 CPU-time을 많이 줄일 수 있다.
CPU 사용률 계산에 생각해봐야 할 것들
CPU 사용률을 측정하려면 측정 시작부터 끝까지의 기준이 되는 단위 시간을 정해야 한다. 시간차가 작을수록 값이 정밀해지겠지만 어느 선을 넘게된다면 측정에 투입되는 시스템 자원을 소비하는 비율이 커지게된다. 일반적으로 1초 단위로 CPU 사용률 값을 살펴본다면 무난하다.
또한 고려해야 하는것은 싱글코어 시스템은 1CPU/1코어이므로 CPU 사용률 계산은 한 CPU에만 집중하면 되는 문제였다. 하지만 멀티코어/멀티프로세서 시스템은 CPU 사용률에서 전체 대수를 나눠줄 필요가 있다. 예를 들어, 8코어 CPU를 사용한다면 1개 코어 사용률만 100%가 된다고 해도 전체 사용률은 12.5%밖에 되지 않는다.
Linux에서 CPU 사용률 계산
리눅스에서는 top을 치면 CPU 사용률을 볼 수 있다. top을 통해 조회하는 CPU 사용률값은 /proc/stat 파일에 저장되어 있다. 값을 살펴보자. cat /proc/stat을 쳐보면 된다.
전체 CPU 사용률을 볼 경우, cpu라고 적힌 데이터를 fscanf같은 파일 조작함수로 라인단위로 읽으면 된다. 현재 이 시스템은 듀얼코어 시스템으로, 각 코어별로 CPU 사용률을 보려 한다면 cpu#이라 적힌 라인까지 읽으면 된다. 근데 저것이 CPU 사용률 관련 데이터이긴 해도 도저히 의미는 알 수가 없다.
리눅스에서는 내부적으로 jiffy라는 단위 시간을 사용한다. 결국 이 수치 값은 부팅 후 지금까지 소모된 jiffies의 #이다. cpu/cpu# 뒤에 오는 첫번째 숫자는 user 모드의 jiffies, 두번째는 nice 상태의 user 모드 jiffies, 세번째 필드는 system 모드의 jiffies, 마지막은 idle task의 jiffies이다.
이제 이 jiffies 데이터를 통해 우리는 CPU 사용률을 계산할 수 있다. 우리가 얻은 데이터는 idle 상태의 jiffies와 전체 소모된 jiffies이다. 그럼 사용률은? 1 – (idle_jiffies) / total_jiffies)다. 1초단위로 CPU 사용률을 계산하는 프로그램을 발로(?)짜보면 다음과 같다.
#include <stdio.h> #include <string.h> #define ONE_LINE 80 #define PAST 0 #define PRESENT 1 #define JIFFIES_NUM 4 enum jiffy{USER, USER_NICE, SYSTEM, IDLE} jiffy_enum; int main(void){ char loadDataBuf[ONE_LINE] = {0}; char cpuId[4] = {0}; int jiffies[2][JIFFIES_NUM] = {0}, totalJiffies; int diffJiffies[JIFFIES_NUM]; int idx; FILE* statFile; while(1){ statFile = fopen("/proc/stat", "r"); fscanf(statFile, "%s %d %d %d %d", cpuId, &jiffies[PRESENT][USER], &jiffies[PRESENT][USER_NICE], &jiffies[PRESENT][SYSTEM], &jiffies[PRESENT][IDLE]); for(idx = 0, totalJiffies = 0; idx < JIFFIES_NUM; ++idx){ diffJiffies[idx] = jiffies[PRESENT][idx] - jiffies[PAST][idx]; totalJiffies = totalJiffies + diffJiffies[idx]; } printf("Cpu usage : %f%%\n", 100.0*(1.0-(diffJiffies[IDLE] / (double)totalJiffies))); memcpy(jiffies[PAST], jiffies[PRESENT], sizeof(int)*JIFFIES_NUM); fclose(statFile); sleep(1); } return 0; }
CPU 사용률의 변화를 보기 위해 파이어폭스를 실행하여, sunspider[2] 벤치마크를 돌려보았다. 중간중간에 CPU 사용률 변화가 눈에 띈다.
Windows에서 CPU 사용률 계산
세상엔 Linux만 존재하지 않기에 나머지 절반인 Windows의 사례도 살펴보자. Windows는 Linux와는 전혀 다른 방법을 사용한다. Windows에서 CPU 점유율을 구하는 방법은 몇가지가 있으나 여기서는 비교적 간단한 PDH를 사용하는 방법으로 구해본다. PDH란 Performance Data Helper의 약자로 Windows의 Performance 측정에 도움이 되는 라이브러리이다. 소스[3]를 보자.
#include <stdio.h> #include <Pdh.h> #pragma comment(lib,"Pdh.lib") int main(void) { PDH_HQUERY cpuQuery; PDH_HCOUNTER cpuTotal; PdhOpenQuery(NULL, NULL, &cpuQuery); PdhAddCounter(cpuQuery, L"\\Processor(_Total)\\% Processor Time", NULL, &cpuTotal); PdhCollectQueryData(cpuQuery); PDH_FMT_COUNTERVALUE counterVal; while(true){ PdhCollectQueryData(cpuQuery); PdhGetFormattedCounterValue(cpuTotal, PDH_FMT_DOUBLE, NULL, &counterVal); wprintf(L"CPU Usage : %f%%\n", counterVal.doubleValue); Sleep(1000); } return 0; }
Windows에서도 Linux와 마찬가지로 sunspider테스트를 통해 CPU 사용률 변화를 살펴보았다. 다만 필자가 쓰는 PC의 논리 프로세서 수가 8개라 수치가 그리 높지는 않다.
Java에서의 CPU 사용률 계산
Java는 플랫폼에 독립적이기 때문에 자체적인 측정 class가 따로 있다. Java 7이상 환경에서 OperatingSystemMXBeans 클래스를 사용하면 매우 쉽게 CPU 사용률을 측정할 수 있다.[4] 여기서 변경점이 있다면 OperatingSystemMXBeans의 갱신주기는 1초보다는 커야 한다는 것이다.
package cpuload; import java.lang.management.ManagementFactory; import com.sun.management.OperatingSystemMXBean; public class CPULoadMonitor { public static void main(String args[]){ final OperatingSystemMXBean osBean = (com.sun.management.OperatingSystemMXBean)ManagementFactory.getOperatingSystemMXBean(); double load; while(true){ load = osBean.getSystemCpuLoad(); if(load < 0.0) continue; System.out.println("CPU Usage : "+load*100.0+"%"); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } } }
필자의 경우, JRE 인식이 제대로 되지 않아 build path에서 JRE를 한 번 제거 한 뒤에 다시 불러와서 해결했다. Java 6까지만 해도 OperatingSystemMXBean은 오직 java.lang.management 패키지에만 존재했는데 문제가 좀 있어서 Java 7에서 업데이트 되었기 때문이다. Java 7로 버전업 되면서 패키지명이 바뀌고 모니터링 메서드 몇 가지 더 추가 되었다. 역시 똑같이 sunspider를 구동해보면서 CPU 사용률 변화를 지켜보았다.
정리
지금까지 CPU 사용률를 계산하는 방법에 대해 알아보았다. CPU load와 CPU 사용률의 차이와 CPU 사용률의 특징, 계산 방법을 이야기 해 보았다. Linux에서는 jiffy의 차를 이용하여 CPU 사용률을 구할 수 있었다. Windows에서는 PDH 라이브러리를 활용하여 CPU 사용률을 구할 수 있었다. Java는 OperatingSystemMXBean class를 통해 CPU 사용률을 구할 수 있었다.
참고자료
[1] Wikipedia – Load_(computing) : CPU load vs CPU utilization
[2] Sunspider javascript benchmark
[3] Stackoverflow – how to get system cpu ram usage in c on Windows
[4] Oracle – OperatingSystemMXBeans
답글 남기기