'구현'에 해당되는 글 1건

  1. 2006.11.04 구현은 변경된다
dev.log2006. 11. 4. 23:37

그렇다, 구현은 늘상 변경되기마련이다. "foo.bar"파일을 fopen으로 파일을 열었다가도 어느 순간인가는 CreateFile로 바뀔수도 있고, 한참 지나다보면 또 std::fstream 객체를 만들어야 할 때도 오는 법이다.

얼마전 회사에서 있었던 일 하나.

거대한 테이블을 파일에서 참조해야 하는 상황이다. 대략 평균 60메가 정도의 파일이 80개 가량. 결국 4.8기가의 테이블을 참조해야 하는 것인데, 이만한 크기의 테이블을 메모리에 올릴 수는 없는 노릇이다. 아무리 요새 메모리가 썪어 난다지만, 단일 테이블을 4.8기가나 메모리에 올리는 것은 미친짓이라고 할수밖에;

그래서 내 손에 들어온 애초의 구현은 윈도우의 경우는 MapViewOfFile 함수로 메모리에 파일포인터를 매핑하여 접근하는 것이었고, 리눅스에서는 mmap 함수로 동일한 일을 하고 있었다. 대략적으로, 다음과 같은 식이다.

 

void Table::init( ... )
{
    short* table = MapViewOfFile( ... );
    // short* table = mmap( ... ); // in case of linux
    // ...
}

void certain_function()
{
    short value = table[index];
    // ...
}

void another_function( ... )
{
    short another_value = table[another_index];
    // ...
}

void yet_another_function( ... )
{
    short yet_another_value = table[yet_another_index];
    // ....
}

// goes on and on .....

코드를 보면 뭔가 불안하다. 일단, table 포인터를 얻어오는 과정이 플랫폼마다 다른데, 이걸 이렇게 흩어진 코드에서 마구잡이로 접근한다면 문제가 생길 것이 뻔하지 않은가. 그래서 코드를 받자마자 테이블을 참조하는 작업을 별도의 인터페이스로 빼서 구현을 따로 하였다. 조금 안심이 된다. 구현과 인터페이스를 분리한 것이다.

void Table::init(  ... )
{
   buffer = MapViewOfFile( ... );
}

short Table::lookup(short index)
{
    return buffer[index];
}

void certain_function()
{
    short value = table.lookup(index);
    // ...
}

void another_function( ... )
{
    short another_value = table.lookup(another_index);
    // ...
}

void yet_another_function( ... )
{
    short yet_another_value = table.lookup(yet_another_index);
    // ....
}

 

문제는 윈도우에서 터졌는데, 이놈의 MapViewOfFile 함수가, 파일을 주소공간에 매핑하고 포인터를 얻어와야 하는데, 메모리가 부족하여 매핑을 못하는 사태가 벌어진 것이다. (사용메모리양 2기가) 유력한 용의자는 당연히 MapViewOfFile 함수. 이 함수가 파일을 통째로 메모리에 올릴 것이라는 혐의가 잡혔다. 리눅스에서는 문제가 안 터졌는데, 아마도 64bit 커널을 쓰고 있어서 주소공간이 넉넉하여 별 문제가 발생하지 않은듯 싶다.

 

그래서 메모리맵을 쓰는 대신, 테이블의 개별 레코드를 일정개수씩 캐쉬로 유지하면서 참조하는 전략을 택하기로 했다. 그 전의 구현이었으면 수십개의 함수에 흩어져있는, 테이블 참조 코드를 일일이 고쳐야 했겠지만, 다행히도 그 전에 구현과 인터페이스를 분리해 두었으므로, 참조 인터페이스는 그대로 두고 구현만 고치면 되는 문제였다. Table::init과 Table::lookup 함수, 단 두개.

 

결과는?

"메모리 뻑 고치는데 한 3일이면 될까요?"

"네.. 해보죠 뭐."

구현만 뚝딱뚝딱 고친다. 30분 뒤....

"다됐음. 받으셈"

"브라보! 3일걸린다면서요."

"훗훗훗"

 

구현은 항상 변경된다. 그러므로 구현과 인터페이스는 분리되어야 하는 것이다.

그럼 당신도 인정받는 프로그래머.

 

PS : 혹은, 3일동안 할일을 30분만에 끝내놓고 이틀동안 빈둥빈둥 노는 것도 추천;

Posted by uhm