'bogomips'에 해당하는 글 1건

BogoMIPS

Linux 2009. 12. 27. 08:10

안녕하세요

우선 cpu 성능의 척도가 되는 Bogomips 를 설명하기 전에 간략하게 PIT(Programmable Interval Timer, 프로그래밍 가능한 구간 타이머) 를 알아보겠습니다.

PIT 는 0x40 - 0x43 포트로 프로그래밍이 가능합니다. 즉 100Hz 로 프로그래밍 해놓으면 10ms 마다 한번씩 IRQ에 타이머 인터럽트가 발생하게 됩니다.

8254 칩의 내부 발진기 주파수는 1.193180  인가 뭐 그럴겁니다 ㅡ.ㅡ; amd 의 것은 다르다고 하지만 어쨌든.. 리눅스에선

#define CLOCK_TICK_RATE 1193180 입니다

즉 1초에 1193180번 왔다갔다 할겁니다.

그래서 0x40 포트에 1193180 을 넣어버리면 1초마다 IRQ0이 발생하게 됩니다

근데 이렇게 1초마다 인터럽트가 발생하게 두면 너무 느리겠죠? 아마 이렇게 리눅스를 프로그래밍하면 context-switching 하는게 눈에 보일거고 사실 그전에 돌아가지도 않겠죠...

그래서 여기에 100을 나눈 숫자를 포트에 집어넣으면

1/100 초마다 IRQ0 이 발생됩니다.

리눅스에선 IRQ0 이 발생하면 do_timer() 가 실행되는데 간단하게 소스를 보면

volatile long jiffies;

do_timer()
{
   jiffies++;
}

여기서 알수있듯이 인터럽트가 발생할때마다
jiffies 값이 올라갑니다. 이 jiffies 값을 가지고 알람이나 스케쥴링 등등 많은것을 합니다. 매우 중요한 변수지요

이제 bogomips 를 재는 소스를 분석하기 전에
보고밉스가 무엇인가 부터 알고 넘어가도록 합시다

보고밉스는 linus 가 생각해낸 cpu 성능의 척도 입니다. 즉
1 tick(즉 jiffies 가 1 늘어난 동안) 동안 수행 가능한 명령어수 정도로 알고 계시면 됩니다.

아마 리눅스 부팅할때 Calibrating delay loop... 450.210 BogoMIPS! 이렇게 나온걸 보신적 있으실 겁니다. 이 수치가 높을수록 (절대적인 수치는 아니지만) 좋습니다.

커널이 시작해서 여러 초기화를 한후에
sti 명령어를 주어서 인터럽트가 발생하게끔 합니다. 그러면 타이머 인터럽트가 마구 발생하겠죠? 그리고 나서 calibrate_delay 라는 함수를 호출합니다.

이제 소스를 보도록 하죠

#define LPS_PREC 8

void __init calibrate_delay(void)
{
       unsigned long ticks, loopbit;
       int lps_precision = LPS_PREC;

       loops_per_jiffy = (1<<12);

       printk("Calibrating delay loop... ");
       while (loops_per_jiffy <<= 1) {

               /* wait for "start of" clock tick */
               ticks = jiffies; 현재 틱 수를 저장합니다.
               __delay(loops_per_jiffy); 처음에는 4096(1<<12) 번 무한루프를 돕니다.
               ticks = jiffies - ticks; 방금 저장해둔 틱에서 뺍니다.
               근데 이 코드가 의외로 쉬울수도 있고 어려울수도 있습니다.
               그냥 일반 어플리케이션 개발하던 사람이 보면 당연히 0이 나와야 한다고 생각할지도 모르지만
               10ms 마다 jiffies 가 알게모르게 1씩 올라갑니다.

               여기서 하는짓은 처음 4096번 무한루프를 돌고 한틱이 지나갔나 알아보는 것입니다. 만약 4096번 돌고도
               아직 jiffies - ticks 가 0이면 ( 루프가 도는동안 IRQ0 가 발생하지 않았으면) 2배로 곱해서 다음엔 8192 번 돌고
               또 아니라면 반복합니다.                
               if (ticks)
                       break;
       }


아래 코드는 더욱 정확히 재기 위한것입니다. 리눅스 커널 1.0 에선 이런 코드가 없었는데
       2.4 를 보니 있군요. 이에는 숫자를 2배씩 곱하기 때문에 정확한 숫자를 구할수가 없습니다
       예를들어 6000번이 한계이면 8192 라는 값으로 잡히게 되어 있습니다. 이것을 방지하기 위해서
       
       loops_per_jiffy >>= 1 세밀하게 다시 2로 나누고 (4096 이라고 저장하고..)
       loopbit = loops_per_jiffy;
       while ( lps_precision-- && (loopbit >>= 1) ) {
       4096 + 2048 해서 한틱이 지나갔으면 값을 프린트 하고 아니면
       다시 거기다가 1024 를 더하고 또 아니라면 512 를 더하고 이런식으로 해서 거의 정확하게 구하는 것입니다.
               loops_per_jiffy |= loopbit;
               ticks = jiffies;
               __delay(loops_per_jiffy);
               if (jiffies != ticks)        /* longer than 1 tick */
                       loops_per_jiffy &= ~loopbit;
       }

/* Round the value and print it */        
       printk("%lu.%02lu BogoMIPS\n",
               loops_per_jiffy/(500000/HZ),
               (loops_per_jiffy/(5000/HZ)) % 100);

       여긴 프린트 ㅡ.ㅡ;
}




PC에 내장되어 있는 CPU는 8259에서 IRQ를 받습니다.. 그리고 8259는 8253이라는 클럭 디바이스에 연결되어 있지요..
CLOCK_TICK_RATE가 1193180으로 정의되어 있는 이유는 기본적으로 8253의 칩 클럭이 1.19318MHz이기 때문입니다.
그것을 i386 아키텍쳐의 리눅스는 100Hz Timer로 쓰고 있기 때문에 LATCH를 시키려고 100Hz로 나누어 downcounting을 하는 것을 볼 수 있죠.. 그럼 Latch값이 11931.8가 되고 8253의 frequency는 1193180 / 11931.8 = 100 Hz가 되어 주기가 10ms 가 되는 것을 볼 수 있죠..
따라서 리눅스 커널을 클럭 디바이스로 8253을 쓰지 않는 플랫폼에 포팅한다고 하면 저 수치는 칩 데이타에 근거하여 바뀌어져야 합니다.


출처 : http://www.asmlove.co.kr/Board/Lecture02/44094/page/3

'Linux' 카테고리의 다른 글

리눅스 원격접속 VNC 서버 사용하기  (0) 2010.07.15
리눅스에서 시디이미지 만들기  (0) 2010.07.08
CSCOPE settings for vim  (0) 2010.04.02
스크립트(awk,sed,vi,gcc..)  (0) 2009.10.24
고급 타이머  (0) 2009.07.30

WRITTEN BY
RootFriend
개인적으로... 나쁜 기억력에 도움되라고 만들게되었습니다.

,