살군의 보조기억 장치

Another memory device…

Archive for June 2010

const 멤버 함수

leave a comment »

정의 방법 : const 멤버 함수를 선언하려면, 단순히 함수의 param을 정의하는 괄호 다음에 const 라는 키워드를 붙여주면 된다.

class Csurface {

protected:

BOOL visible;

BOOL isVisible() const

{return visible;}

void makeVisible(BOOL onoff) {visible = onoff;);

};

isVisible() 은 멤버변수를 변경시키지 않고 단지 그 값만을 리턴하므로 const 로 선언될 수 있다. 또한 이렇게 하는 것이 바람직하다.

const 멤버 변수가 가지는 다른 특징은 const형 클래스 오브젝트에 사용될 경우를 볼 수 있다. 만일 클래스 오브젝트 자체를 const로 선언하면 자신의 멤버변수가 일단 초기화 되면 추후에 그 내용을 다시 바꿀 수 없다고 약속하는 것이므로 멤버 함수를 써서 멤버 변수 값 자체를 변경시키는 작업이 불가능하다. 따라서 const 클래스 오브젝트에는 오직 const 멤버 함수만 호출이 가능해진다.

main() {

const Csurface triangle;

BOOL bShown;

triangle.makeVisible(TRUE); // error. const가 아닌 멤버함수는 호출할 수 없다.

bShown = triangle.isVisible(); // OK. const 멤버함수 호출가능

}

물론 위의 경우에는 Csurface의 생성자가 visible 멤버 변수를 초기화했으리라고 가정하고 있다.

const 멤버 함수의 경우 멤버 변수의 값 자체를 변경시키는 것이 불가능하지만, 멤버변수가 포인터인 경우 그 포인터가 가리키는 내용을 포인터를 사용하여 변경시키는 것은 가능하다. 이것은 const 인 것이 포인터 자체이지 포인터가 가리키는 값이 아닌경우 이기 때문이다.

출처 : Windows로 배우는 C++, 한수찬 지음, 교학사

Advertisements

Written by gomiski

2010/06/23 at 7:35 am

Posted in Embedded, Lecture

m-fold cross validation

with one comment

m-fold cross-validation 을 하는 방법은,

  1. original sample을 m개의 subset sample로 나눈다
  2. 이 가운데 (m-1) 개의 subset을 사용해 training 한 후 나머지 1개의 subset을 validation한다.
  3. 위의 2번 을 모든 subset에 대해서 각각 1번씩 validation 수행
  4. m개의 validation결과를 average 하거나 (혹은 다른 방법으로 combination 해서) single estimation 결과를 산출

m-fold cross-validation의 장점은 모든 observation이 training과 validation에 모두 사용된다는 것이다. 또한, observation은 모두 단 1번만의 validation이 수행된다. 10-fold cross-validation이 일반적으로 사용된다.

m개의 subset을 나눌때는 mean이 동일하도록 나눠야 한다. 즉, 2 class 문제의 경우, 각각 class가 subset에 가능한 동일한 비율로 분포되도록 나눠줘야 한다.

Written by gomiski

2010/06/15 at 12:52 am

Parametric / Non-Parametric 차이

leave a comment »

패턴인식의 방법가운데, unsupervised 방법에는 크게 두가지가 있다. Parametric 과 Non-Parametric이다. 이 둘의 가장 큰 차이점은 기본적인 문제의 접근방법에 있다.

Parametric technique의 경우: Basic assumption은 우리가 모든 확률정보를 알고 있다고 가정한다. 즉, unknown의 pdf를 알고 있어야 한다. 그렇지 않을 경우에는 이 방법을 사용할 수가 없다.

ex) p(x|S1) , p(x|S2) 를 알고 있다. 고로 바로 이 정보를 이용해서 p(S1|x) <– posteriori 를 구한다.

Non-Parametric technique : 아무것도 모르기 때문에, pdf를 estimation한다. 먼저 prototype(& unknown)들로 부터 pdf를 estimation 한 다음 이정보를 이용해서 posteriori를 구한다.

Written by gomiski

2010/06/14 at 1:12 am

register 변수와 volatile 변수

with one comment

별로 시간이 없는 관계로 정리는 하지 못하고 몇몇분의 글을 그대로 붙여넣었습니다. 시간이 나면 나름대로 정리하도록 하겠습니다. 언제 그 시간이 날지… 주인장 허락없이 그냥 퍼왔습니다만, 트랙백 남겼으니 말씀 주시면 바로 삭제하겠습니다.

== register 변수

변수는 보통 컴퓨터의 메모리에 저장된다. 그러나 운이 좋으면 레지스터 변수는 CPU 레지스터 또는 더 일반적으로 가장 빠른 메모리에 저장되는데, 보통 변수보다 더 빠르게 접근하고 계산될 수 있다. 이외의 부분들은 자동변수와 똑같다. 이때 운이 좋으면 이라고 표현한 것은 레지스터 변수의 수가 제한되기 때문이다. 컴파일러는 레지스터 수와 선언된 변수의 수를 가중치에 따라 조정하기 때문에 레지스터 변수로 선언했지만 자동 변수로 되는 경우도 있을 수 있다. 그리고 레지스터 변수로 선언될 수 있는 형도 제한이 있다. 또한 & 연산자도 레지스터 변수에는 적용할 수 없다.

== volatile 변수

변수를 register가 아닌 메모리에 저장한다. 아무리 많은 register 변수를 선언하더라도 CPU의 register 개수에는 한계가 있기 때문에 register 선언이 효과를 지니지 않고 메모리 변수로 되는 경우도 있다.

/* 레지스터 변수를 사용하는 예제 */


int jegob(int a, int b);        /* 사용자 정의 함수를 선언 */

void main(void)
{
    int a = 0, b = 0;

    printf("원하는 수는 :");
    scanf("%d", &amp;a);
    printf("몇 제곱을 원하십니까?");
    scanf("%d", &amp;b);

    if (b <= 0)
        return;

    jegob(a, b);        /* 사용자정의 함수를 호출 */
}

int jegob(int a, int b)
{
    register int n;   /* 레지스터 변수로 n을 선언 */
    long c = 1;

    for(n = 1; n <= b; n++)
    {
        c *= a;
        printf("%d의%d제곱은 %d입니다.\n", a, n, c);
    }
    return;        /* 빈 값으로 돌려줌 */
}

프로그램을 코딩하는 중에 자주 쓰는 변수들을 register 변수로 선언하여 사용한다. 하지만 똑똑한 Compiler는 직접 선언해 주지 않아도 register 변수로 잡기도 한다(Compiler에 의한 최적화 수행). Memory Mapped I/O(메모리의 특정 영역을 특정 장치와 연결해서 사용)의 경우에는 문제가 발생한다. 왜냐하면, 어떤 장치의 한 영역을 나타내는 변수가 있고 이 변수를 통하여 그 장치의 상황을 받아들여 특정한 일을 처리하는 경우 당연히 이 변수는 자주 쓰이는 변수가 되므로 Compiler에 의해서 Register 변수로 처리가 될 수 있는데 이를 미연에 방지하고자 volatile이라는 Keyword를 주게 되면 Compiler는 이 변수에 대한 최적화를 수행하지 않게 된다. 따라서, 원하는 결과를 얻을수 있게 된다.

메모리 변수(volatile variable)는 지역 변수를 선언할 때 레지스터가 아닌 반드시 메모리에 변수를 만들어 처리하도록 한다. 그러면 “할 수 있다면 빠른 레지스터 변수를 사용하는 것이 더 낫지 않느냐?” 라는 반론이 제기될 수 있는데 일반적으로 메모리 변수는 인터럽트 루틴 등의 변수에 사용된다. 레지스터 변수인 경우 인터럽트 루틴에서 그 변수의 내용을 바꾸면 레지스터에 있는 값이 바뀌므로 언제 그 레지스터의 값이 없어질지 알 수 없다. 이런 경우 메모리 변수로 선언하기 위해 volatile을 사용한다.

volatile은 컴파일러 키워드이다. 특별히 따로 최적화를 하지 않고 쓴대로 수행시키라는 의미이다. 함수에 이것이 있으면 함수 내부를 컴파일할때 최적화 옵션이 있어도 최적화를 수행 하지 않는다. 변수에 이것이 붙으면 속도를 위해서 변수를 레지스터에 올려놓고 쓰는일 같은 것을 안하게 된다. 보통 IO Port를 특별한 변수 이름으로 지정해놓고 사용하고자 할때 volatile키워드를 붙여서 쓴다.

일반적으로 컴파일러는 최적화를 하는 동안 불필요 하다고 생각되는 코드를 생략을 하게된다. 그러나 volatile을 사용하는 경우 이 변수와 관련된 내용은 생략하지 않는다. 키워드 volatile이 정의되어 있지 않으면 컴파일러는 마지막 명령만 남기고 이전 명령들을 생략할 수 있다.

예)

char * Addr;

Addr = (char *)0x10000;
*Addr = 0x55; /*생략한다..*/
*Addr = 0xAA; /* 이것만 인식한다*/

위의 경우 단지 0x10000에 0xAA만 쓴 것으로 컴파일러는 생각하게 된다. 일반적으로 같은 곳에 값을 써주거나 값을 읽을 일이 많은 메모리나 레지스터인 경우에는 volatile를 써주는 것이 좋다. 만약 컴파일러가 최적화 옵션을 사용하지 않는다면 써줄 필요는 없다.

volatile 선언은 디버그 모드에서는 작동하지 않고 보통 릴리즈 모드에서 최적화시에 발생한다. 쉬운 예를 들자면 for (;;) { int a = 1; .. .. } 이렇게 되어 있을때. 이 루프가 한 100만번 돈다고 하면. int a = 1;은 루프 밖으로 빼는게 실행 속도가 빨라진다. 컴파일러는 자기가 알아서 이런 것을 루프 밖으로 빼버린다. 그런데 이 문장을 빼는 것이 큰 문제가 되는 경우도 있다. 이럴때 volatile int a = 1 해주면 최적화 대상에서 제외 된다.

참고

Nesting: Sub-routine에서 Sub-routine을 부르는것(Call)을 의미한다.

먼저 다음 물음에 답해보자.

혹시 당신이 C로짠 임베디드 코드에서 다음과 같은 경우를 경험한 적이 있는가?

* 옴티마이즈 옵션을 켜기 전까지는 코드가 잘 동작한다.
* 어떤 인터럽트를 disable 시킨 동안에는 코드가 잘 동작한다.
* RTOS가 탑재된 멀티태스킹 시스템에서 어떤 태스크(TASK)가 enable 되기 전까지는 태스크가 잘 동작한다.

만약 위의 물음에 “네(yes)”라고 대답한다면 그것은 바로 당신이 volatile라는 C keyword를 사용하지 않았기 때문이다. 이건 비단 당신혼자만의 문제는 아니다. 많은 프로그래머들이 volatile 키워드에 대해서 어설프게 잘못 알고 있거나 제대로 사용하지 않고 있다. 이건 그리 놀랄만한 일이 아닌데 그건 바로 많은 C 책이 이점에 관해서 너무 하리만큼 무심하기 때문이다.

volatile은 변수를 선언할 때 같이 사용하는 키워드이다. volatile을 사용함으로 인해 컴파일러에게 volatile과 함께 선언된 변수는 언제 어느 때든지 값이 바뀔수 있다는 것을 말한다. 구체적인 사용 예를 들기 전에 먼저 volatile에 대한 문법적인 사항을 알아보자. volatile 변수를 사용하기 위해 volatile 키워드를 정의된 변수의 데이터 타입 앞 또는 뒤에 명시하면 된다. 다음과 같이 말이다.

volatile int foo;
int volatile foo;

자, 그럼 포인터에서는 어떻게 될까? 포인터에서 뭔가 특별한 점이 있다고 생각하는가? 포인터라 해서 별반 다를게 없다. 포인터 역시 다음과 같이 하면된다.

volatile int *foo;
int volatile *foo;

마지막으로 volatile을 struct나 union에 적용시켜버리면 struct나 union의 모든 내용들은 volatile이다. 만약 위와 같은 경우를 원하지 않는다면 어떻게 할것인가? struct나 union 멤버에게 개별적으로 사용하면 된다. 자, 그럼 본격적인 사용법을 알아보자.

어떤 변수들이 예고 없이 값이 바뀔 수 있을 가능성이 있는 경우에는 volatile로 선언해야 한다. 사실상 다음의 3가지 타입의 변수들이 바뀔 수 있다.

  • memory-mapped periherral registers
  • 인터럽트 서비스 루틴에 의해 수정되는 전역변수
  • 멀티 태스킹 또는 멀티 쓰레드에서 사용되는 전역변수

그럼 먼저 첫번째 항목에 대해서 좀더 자세히 알아보자.

임베디드 시스템에서는 진보되고 복잡한 실질적인 주변 디바이스(peripheral)를 포함하게 된다. 이런 peripheral들은 프로그램 흐름과 비동기적으로 값들이 변하는 레지스터들을 가지고 있는 경우가 대부분이다. 매우 간단한 예로 0x1234 address에 위치한 8비트 status 레지스터가 있다고 가정하고 생각해보자. 만약 이 레지스터가 0이 아닌 값을 가질때까지 이 레지스터를 폴링(polling)한다고 가정해보자. 그럼 당신은 분명히 다음과 같이 코드를 작성할것이다.

INT8U *ptr = (INT8U *)0x1234; //wait for register to become non-zero
while (*ptr == 0); //Do something else

만약 당신이 옴티마이즈 옵션을 켰다면 위의 코드는 제대로 동작하지 않을 확률이 굉장히 높다. 왜냐하면 컴파일러는 당신이 작성한 코드에 대해서 다음과 같은 어셈블러를 생성할 것이다. 반드시 유심히 보길 바란다. 중요하다.

move ptr, #0x1234
move a, @ptr
loop bz loop

자, 한번 분석해보자. 컴파일러는 굉장히 똑똑하게 어셈블리 코드를 생성한 것을 볼 수 있다. 첨에 한번반 0x1234를 액세스해서 값을 로딩한 이후로 두번 다시는 0x1234를 억세스 하지 않는다. 두 번째 코드에서 볼 수 있듯이 값은 accumulator에 이미 로딩이 되있기 때문에 값을 재 로딩할 필요가 없다고 컴파일러는 판단하기 때문이다. 왜냐하면 값은 항상 같다고 보기 때문이다. 그러므로 3 번째 라인에 의해 당신이 작성한 코드는 무한 루프에 빠지게 된다. 정작 우리가 원하는 동작을 하기 위해서는 위의 코드를 다음과 같이 수정해야 한다.

INT8U volatile * ptr = (INT8U volatile *)0x1234;

그럼 컴파일러는 이제 다음과 같이 어셈블러를 생성할 것이다.

mov ptr, #0x1234
loop mov a, @ptr
bz loop

자, 어떤가? 드디어 당신이 원하는 결과를 얻게 될 것이다.

자, 그럼 인터럽트 서비스 루틴의 경우에 대해서 생각해보자. 종종 인터럽트 서비스 루틴은 메인함수에서 테스트하는 변수를 셋팅하게된다. 예를 들어 시리얼 포트 인터럽트는 각각에 수신한 캐릭터에 대해 ETX 캐릭터가 수신됐는지를 테스트한다고 가정해보자. 만약 ETX가 수신되면 인터럽트 서비스 루틴은 전역 플래그를 셋팅할 것이다. 불완전한 코드를 다음에 보이겠다.

int ETXRcvd = FALSE;

void main (void)
{
    //...
    while (!ETXRcvd)
    {
        // what
    }
    //...
}

interrupt void RxISR (void)
{
    //...
    if (rx_char == ETX)
    {
        ETXRcvd = TRUE;
    }
    //...
}

옵티마이즈 옵션을 꺼논 동안에는 코드가 올바르게 작동할 것이다. 그러나, 그렇지 않을 경우에는? 문제는 컴파일러는 ETXRcvd가 인터럽트 서비스 루틴에 의해서 값이 바꼈을 경우 이를 알 수 없는 경우가 생긴다. 위에 peripheral 예에서 들었듯이 !EXTRcvd는 항상 참이기 때문에 while 루프를 절대 벗어날 수 없는 경우가 생길 수도 있다. 게다가 심지어는 이런 이유로 인해 루프 이후에 코드들은 옵티마이즈에 의해 제거 되어버릴 수도 있다. 만약 당신이 운 좋은 놈이라면 당신의 컴파일러는 이런 문제에 대해서 경고 메세지를 보내게 될것이다. 그렇지 않고 당신이 운좋은 놈이 아니거나 컴파일러가 제공하는 경고 메세지가 때로는 얼마나 무서운 것인지를 경험해보지 못했다면 어떻게 될까? 말안해도 알리라 본다. 마지막으로 멀티 쓰레드또는 멀티 태스킹 어플리케이션 경우를 생각해 봐야 한다.

*((volatile byte *)(port))

port라는 포인터 변수가 가리키는 번지의 byte 형을 읽는다.

(port라는 포인터 변수를 byte 형을 가리키는 포인터로 전환한 후(cast 연산자) 이곳의 내용을 참조하라는 의미(* 연산자))

포인터 변수의 이름이 port인 것과 volatile이라는 키워드가 나온 것으로 보아 memory mapped io 루틴에 사용되었을 가능성이 있다. volatile이라는 키워드는 변수가 언제 어느 때든지 값이 바뀔 수 있기 때문에 값을 항상 메모리(레지스터가 아닌)에만 보관하라는 의미이다. 일반적인 프로그래밍에서는 보통 잘 쓰이진 않지만 하드웨어 관련 프로그래밍에서는 매우 중요한 키워드이다.

똑똑한 컴파일러들은 반복문 등에서 어떤 변수의 사용이 잦을 경우에 자기가 알아서 자동으로 해당 변수를 register 형으로 사용하는 경우가 있다. (변수가 위치한 메모리 주소를 직접 액세스 하는 것보다 레지스터에 넣고 사용하는 것이 적게는 수십 배에서 많게는 수백 배까지 빠르기 때문이다.) 이렇게 해당 변수를 레지스터에 넣고 사용하다가 반복문이 끝나면 메모리로 복귀시키는 경우가 있는데 문제는 프로그램이 수행되는 도중에 인터럽트에 의해서라던가 아니면 다른 쓰레드에 의해 해당 변수의 내용(메모리 상에 있는 내용)이 바뀌는 경우가 있다. 이 상황에서 그 변수를 register에 넣고 돌아가는 코드가 동시에 돌아가고 있다면 그 곳에서는 변수의 내용이 바뀌었는지 알아챌 수가 없다. 따라서 이로 인해 프로그램이 오작동하거나, 큰 문제를 일으킬 수 있다. 반대의 경우로서, 변수를 레지스터에 넣고 돌리는 동안에는 다른 루틴에서 이 변수의 내용이 바뀌었는지의 여부를 알 수가 없다.

이러한 상황을 방지하기 위해, 변수를 선언할 때, volatile라는 키워드를 사용하면 그 변수를 참조할 때 무조건 메모리 상의 내용을 바탕으로 하기 때문에 문제를 해결할 수가 있다.

또한 위에서 말했던 memory mapped io 등과 같은 곳에서도 해당 메모리 번지를 ‘직접’ 액세스하여야만 하는 것이기 때문에 만약의 사태에 대비하여 volatile로 선언하곤 한다.

From 달빛온도’s Blog
http://www.sinwoong.co.kr/entry/register-%EB%B3%80%EC%88%98%EC%99%80-volatile-%EB%B3%80%EC%88%98


register와 volatile 키워드는 해당 변수의 메모리 적재와 관련되어 있으며, 어떤 의미에서는 상반된 역할을 수행하도록 컴파일러에 지시합니다.

register 키워드
일반적인 변수선언의 형태처럼 아래와 같이 선언했다면,

int i;

해당 변수가 전역 변수로 선언된 경우에는 프로그램의 데이터 영역에, 지역 변수로 선언된 경우에는 스택 영역에 변수의 위치가 할당됩니다. 지역 변수가 스택 영역에 할당되는건, 해당 언어의 스펙과 구현에 따라 다를 수 있습니다. 대개의 경우 재귀호출을 허용하는 언어인 경우에는 스택 영역에, 그렇지 않은 언어의 경우에는 데이터 영역에 지역 변수를 할당하게 됩니다. 조금 말이 길었지만 결국 메모리 어딘가에 전역 변수든 지역변수든 할당된다는 것입니다. 예를 들어 아래와 같은 코드가 있다고 가정해 봅시다.

int i, sum, limit;
//...
for (i=0; i < limit; i++) 
{
    sum = sum + i;
}
...

앞에서 설명한 바와 같이 위의 코드에서는 i, sum, limit 변수 모두 메모리 어딘가에 할당됩니다. 여기서 메모리(변수) 관련 연산들을 살펴보면 i를 0으로 초기화, i와 limit을 비교, i에 1을 증가, sum을 i만큼 증가시키는 연산이 필요하게 됩니다. 해당 변수값이 메모리에 할당되어 유지되므로 필요한 메모리 연산을 계산해보면, 루프를 한 번 수행하는 동안 sum 변수는 읽기 1번, 쓰기 1번, i 변수는 읽기 3번, 쓰기 1번, limit 변수는 읽기 1번이 필요합니다. 모두 합해서 7 * limit 횟수 만큼의 메모리 읽고 쓰기가 필요하게 되겠습니다.

메모리 연산은 CPU의 여러 연산 중 상대적으로 느리게 동작하는 연산 중에 하나입니다. 그나마, 80년대 초까지는 CPU의 클럭 속도가 낮은 상태에서는 큰 부담이 없었지만 지금은 그 차이가 무척 크지요. 이러하 CPU와 메모리 사이의 속도의 현격한 차이가 CPU 내의 1차 캐쉬 그리고 외부에 2차 캐쉬 또는 3차 캐쉬까지 제공되는 이유가 된 것입니다. 결론적으로 위에서 설명한 바와 같이 모든 경우에 메모리에 있는 변수를 읽고 쓴다면 CPU가 메모리 읽기 및 쓰기 연산이 수행되는 동안 상당한 수준 대기해야 함으로 CPU 본래의 성능에 비해 프로그램이 느리게 동작하게 됩니다.

CPU 내에는 그 갯수가 한정적이지만, 메모리처럼 사용할 수 있는 메모리 공간이 있습니다. 바로 레지스터입니다. 어셈블리어 책을 보면, 해당 같은 연산이 레지스터를 이용한 경우와 메모리를 이용한 경우의 두가지로 나오는 경우가 있습니다. CPU의 종류에 따라서 메모리 참조방식에 따라 여러 개 나오기도 하지요. 이러한 동일연산의 수행시간을 살펴보면, 레지스터를 참조하는 경우가 메모리를 참조하는 경우보다 빠른 것을 알 수 있습니다.

이 정도면 어느 정도 짐작하겠지만, register 키워드는 프로그램의 최적화를 위해서 해당 변수를 가능한한 (레지스터 갯수가 한정되어 있으니까요) 메모리 대신 레지스터에 넣어서 사용하라는 얘기입니다. 위의 예에서 아래와 같이 코딩을 하였다면,

register int i, limit, sum;
//...
for (i=0; i<limit; i++) 
{
    sum = sum + i;
}

for 루프를 수행하기 전에 Ra, Rb, Rc라는 레지스터에 각각 i, limit, sum의 값을 먼저 옮기고, 루프를 수행합니다. 물론 루프 수행중에는 메모리 대신 해당 레지스터를 참조하겠지요. 루프 수행이 마치고 나면, 다시 메모리 변수 i, limit, sum의 값을 레지스터 Ra, Rb, Rc에서 읽어 저장하게 됩니다. 그럼 모두합해서 읽기 3번, 쓰기 3번 총 6번의 메모리 접근만 일어나게 되겠지요. 당연히 빨라집니다.

물론 이런 수고를 사람이 직접 해야 하느냐? 그건 아닙니다. 앤만한 경우에는 컴파일러가 알아서 해주게 됩니다. 괜히 있는 레지스터를 놀릴만컴 컴파일러가 둔하지는 않습니다. 컴파일러가 판단하기에 자주 그리고 많이 사용되겠다는 변수를 우선순위를 매긴 다음, 현재 남아 있는 레지스터 가운데 적절히 할당합니다. 레지스터 갯수가 별루 많지도 않고 특정 연산에는 특정 레지스터를 사용해야 하는 x86 CPU는 이러한 레지스터 할당이 힘들겠지만 그래도 최소 SI, DI 레지스터 일반 용도로 사용할 수 있습니다. 최신 x86 CPU에서는 레지스터 수가 좀 늘었는지 모르겠군요. 대부분의 RISC CPU는 수개에서 수십개까지의 일반 목적으로 활용할 수 있는 레지스터를 보유하고 있으니, 적절히 레지스터에 변수를 할당하게 되면 무척 빨라지겠죠. 이 때문에 RISC CPU에서는 컴파일러의 성능이 특히나 중요하게 여겨지게 됩니다. 물론 사용자가 register 키워드를 통해서 직접 제어한다면, 컴파일러에 의한 변수의 레지스터 할당에 비해 우선적으로 처리되겠습니다.

정말 똑똑한 컴파일러라면 위의 코드를 아래와 같이 바꾸겠지요.

int i, limit, sum;
//...
sum = (1 + limit) * limit / 2;
i = limit;

volatile 키워드
volatile의 경우 어떤 의미에서는 앞에서 설명한 컴파일러의 최적화와 관계있습니다. 그 외에도 CPU 내, 외부의 캐쉬와 갈은 하드웨어적인 최적화와도 관계가 있습니다.

volatile 키워드가 가장 많이 사용되는 경우의 하나가 memory-mapped I/O인 경우입니다. 메모리의 특정 영역을 특정 장치와 연결하여 사용하는 방법입니다. 가장 흔한 예가 비디오 메모리가 되겠고, 그 이외에도 많은 장치들을 이러한 식으로 사용될 수 있습니다.

즉, 자신 엄밀하게 말한다면 컴파일러가 컴파일하고 있는 코드의 상황과는 관계 없이 바뀔 수 있는 메모리 변수가 있다면, 해당 변수에 대해 특별한 최적화를 하지 못하도록 컴파일러를 제약하는 키워드가 volatile입니다.

예를 들어, 0x0C000000 번지에 특별한 장치가 있다고 하겠습니다. 이 장치가 일종의 센서라고 하고 입력되는 센서값의 범위에 따라 다른 동작을 수행하게 프로그래밍을 아래와 같이 하였다고 하면,

int *p = 0x0C000000;
while (1) {
    if (*p == 0) {
        break;
    } else if  (*p<lower_limit) {
        //action 1;
    } else if (*p<upper_limit) {
        //action 2;
    }
    wait 10 mili-seconds
}

똑똑한 컴파일러는 위의 코드를 아래와 아래와 같이 바꿉니다.

int *p = 0x0C000000;
register int pvar = *p;

if (pvar == 0) 
{
} else if (pvar < lower_limit) 
{
    while (1) 
    {
        action 1;
        wait 10 mili-seconds;
    }
} else if (pvar < upper_limit) 
{
    while (1) 
    {
        action 2;
        wait 10 mili-seconds;
    }
}

위와 같이 코드가 변형된다면, 프로그래머가 의도한 바와는 다른 결과가 나타나게 됩니다. 이러한 최적화를 억제하는 목적으로 volatile을 사용합니다. 컴파일러는 volatile 키워드가 선언된 변수에 대해서는 무조건 메모리에 접근하여 됩니다. 이러한 memory-mapped I/O 이외에도, 쓰레드 등으로 프로그램을 만들어서 공유변수를 한쪽에서 읽고, 한쪽에서 쓰는 경우도 해당될 수 있으며, 시스템 시간과 같이 특정 위치의 변수값이 자신과는 독립적으로 계속 변하는 경우에도 사용할 수 있습니다.

또는 CPU 내 외부의 캐쉬에 의해서도 이러한 최적화 효과가 나타날 수 있습니다. 캐쉬내에 해당 메모리 번지 값이 저장되어 읽을 때마다 같은 값이 읽히고, 또한 적을 때도 실제 메모리에 저장되지 않고 캐쉬에 임시 보관될 수 있습니다. 다른 경우는 잘 모르겠지만 MIPS의 R 시리즈 CPU에서는 (아마 2000 인가 3000 시리즈로 기억됩니다) 메모리를 두가지 방법으로 접근할 수 있습니다. 메모리 주소의 최상위 비트가 0이면 캐쉬를 거친 일반적인 접근을, 최상위 비트가 1이면 똑같은 주소의 메모리를 캐쉬를 거치지 않고 직접 접근할 수 있습니다. 당연히 이런 컴퓨터의 컴파일러에서 volatile로 선언하면 0C000000이 아니라 8C000000의 메모리를 접근하게 됩니다.

From Sunny’s Homepage

http://www.sunnyn.com/research/942

그외에 자료들

KLDP 관련 토론

http://kldp.org/node/21829

http://kldp.org/node/4016

Written by gomiski

2010/06/13 at 8:14 pm

Posted in Embedded, Lecture

ROC(Receiver Operating Characteristic) 커브

leave a comment »

ROC curve (from wiki)

ROC (from wiki)

이런 그림이다.

TPR / FPR 비교가 가능하다. 쓸모? 어떤 threshold 혹은 parameter 값에 따라 True와 False가 어떻게 변화하는지 한눈에 보여준다. 어떤 시스템의 성능을 비교하는데 객관적인 자료가 될 수 있다.

(wikipedia에서 퍼옴)

더 자세한 내용이 보고싶으면…

http://en.wikipedia.org/wiki/Receiver_operating_characteristic

링크 참조.

Written by gomiski

2010/06/13 at 7:55 am

Posted in General, Lecture

Φ(Phi)-machine

leave a comment »

피-머신.

Polynomial을 linear function으로 바꾸는 아이디어.

즉,

x^3 + x^2 + x ===> A + B + C 로 바꿔서 생각한다.

이 결과 feature dim. 이 높아지게 된다.

Note.

  1. 강력한 툴이 될 수 있다.
  2. polynomial의 order가 올라가면, overfitting을 초래할 가능성이 아주아주 높다.  왜냐하면 polynomial의 차수가 높아지면 각각의 항들은 무진장 많아진다.
    2차 => 3개(x^2 + x + 1)      3차 xy라면 => (x^3 + x^2y + xy^2 + y^3 + 1)
    이런식으로 무진장 늘어남
  3. polynomial의 degree를 먼저 정의해야 처리가 가능

Written by gomiski

2010/06/13 at 7:37 am

Perceptron

leave a comment »

이거 어떻게 쓰는거지?

망할? 한글지원 안되는거 아니라? =_=

한글지원은 되는데… 역시나 수식 지원이 안된다.

이거 어떻게 써야 잘 썼다고 할수 있으려나…

수식이 지원안되면 완전 케망하는건데… ㅋㅋ

Written by gomiski

2010/06/13 at 6:46 am