요즘 Varnish Cache를 본격적으로 도입하기 위해서 노력중입니다. 설계 문서를 보면 많이 사용하는 Squid는 70/80년대 하드웨어에 적합하게 설계되었기 때문에 요즘 하드웨어에서는 퍼포먼스가 아주 많이 떨어진다는 흥미로운 얘기가 있습니다.
Squid에서는 소프트웨어적으로 캐시를 따로 구현해서, 커널에서 메모리 관리하는 것과 같이 잘 동작하지 않는다고 합니다. 소프트웨어적으로 캐시를 따로 구현하면 필요하지 않은 메모리 영역을 swap out할때, 똑똑한 커널에서 이미 swap out됐던 데이터를 메모리 영역으로 불러왔다가 다시 swap out 하는 과정이 일어난다고 합니다. 옛날 OS에서는 메모리 관리가 최적화가 안되어있었지만 요즘은 메모리 관리를 커널에서 알아서 하게 하면 퍼포먼스가 많이 빨라진다고 하네요.
Varnish Cache 최적화는 크게 3가지 정도로 요약할수 있을거 같습니다.
1. 메모리 관리를 커널에 위임
2. 메모리 할당/해제 부분을 최소화 (workspace 단위 할당, 한번에 해제)
3. 설정 파일 컴파일러로 컴파일
메모리 관리는 mmap을 통해서 관리합니다. 파일 시스템 영역과 메모리 영역을 연결하여 사용하고, storage_file.c에서 직접 메모리 쪼개서 관리합니다. 메모리 영역을 쪼개서 free list(VTAILQ)에 넣고, 할당할때 free list에서 꺼내서 쓰는 방식입니다. 프로그램에서는 무엇이 메모리에 있고, 파일로 swap 할지 결정하지 않고 커널이 하도록 내버려둡니다.
메모리 할당은 workspace 단위로 하고 그 안에서 쪼개서 쓰는 거 같습니다. 이 부분은 좀더 분석이 필요할거 같습니다.
설정 파일은 c로 변경한 다음 저장하고, 직접 cc를 호출해서 오브젝트 파일을 생성하며, dlopen으로 로드하게 되어있습니다. mgt_vcc.c의 mgt_CallCc 함수를 참고하세요. dlopen을 사용하기 위해서 컴파일러에 -fpic -shared 등의 옵션을 넘기게 되어있습니다.
Varnish의 개발자가 FreeBSD 커널 개발자 출신입니다. 지금도 개발하는지는 모르겠네요..Varnish는 정말 BSD커널 소스처럼 짜놨네요. BSD 커널 소스보면 데이타 구조가 대부분 queue.h로 되어있는데, 여기서도 VTAILQ 등으로 이름만 조금 바꿔서 사용하고 있습니다. queue.h는 여러가지 데이타 구조를 C 매크로만으로 구현한 헤더파일 라이브러리입니다. 구현할때 struct 안에 포인터가 내장되서 여러가지 방법으로 메모리 사용을 최적화할수 있습니다. 커널에서는 대부분 블럭단위로 메모리 할당하여, 그걸 쪼개서 free 리스트에 넣어놓고 그걸 꺼내써서 사용하는 방식으로 메모리를 관리합니다. 메모리 할당과 해제가 간단한 포인터 연산으로 끝나기 때문에 매우 효율적이고, 메모리 fragmentation도 많이 줄여줍니다. 메모리 할당과 해제가 자주 일어나는 경우 아주 큰 도움이 되죠.
2000년에 FreeBSD에서 방화벽 커널 모듈을 개발한 적이 있었는데, 메모리 관리를 queue.h와 “Redesigning the BSD Callout and Timer Facilities (1995)”를 참고해서 구현했었습니다. 퍼포먼스 최적화에 아주 큰 도움이 됐었습니다. Redesigning the BSD Callout… 논문을 어떻게 알게 되었는지는 기억은 안나지만, 정말 많은 도움을 받았던 논문이네요. 지금 찾아보니 첫번째 저자는 구글에 있고, 두번째 저자는 벤처 만들었다가 시스코에 인수됐다고 하네요. 지금은 UCSD 교수로 있네요. 이 논문에 대해서는 블로그에 글을 한번 정리해서 올리도록 노력하겠습니다.