본문 바로가기

Linux Kernel

[LWN.net] More IOPS with BIO Caching

반응형
 

More IOPS with BIO caching [LWN.net]

Benefits for LWN subscribersThe primary benefit from subscribing to LWN is helping to keep us publishing, but, beyond that, subscribers get immediate access to all site content and access to a number of extra site features. Please sign up today! By Jonatha

lwn.net

참고로 IOPS (I/O Operations Per Second)는 스토리지 디바이스가 초당 I/O 연산을 처리하는 횟수이다.

글 내용

옛날에는 스토리지 디바이스가 매우 느려서 시스템 전체적인 속도에 영향을 줄 정였다. 그래서 IO 스케줄링으로 성능 향상을 하곤 했었다. (특히 HDD같은 경우에는 IO 스케줄링이 성능에 매우 중요했다.) 하지만 시간이 지나고 스토리지 디바이스가 빨라지면서 IO 스케줄링 매커니즘보다는 CPU가 스토리지 디바이스의 속도를 커버할 수 있도록 코드를 최적화하는 데에 노력을 하고 있다. 이번 5.15 머지 윈도우에서 이런 최적화가 있었다.

 

블록 레이어에 IO 요청은 struct bio로 나타낸다. BIO에는 이 요청이 어떤 블록 디바이스의 요청인지, 어느 버퍼에 데이터를 담아서 보낼 것인지, 요청이 끝난 후에 실행되는 콜백함수 등 많은 정보가 들어있다. 커널은 IO 요청이 들어올 때마다 BIO를 하나씩 할당해주어야 한다. 부하가 많은 시스템에서는 수백만 IOPS까지도 처리할 수 있으므로, 그만큼 많은 BIO를 할당하고 해제해야 한다.

 

커널에서 정해진 크기의, 자주 할당/해제되는 메모리 할당은 슬랩 할당자가 담당한다. bio도 그 크기가 정해져있고 자주 할당/해제를 반복하므로 슬랩 할당자를 사용했었다. 하지만 현재 블록 레이어에서 슬랩 할당자가 병목 현상이 되고 있다. 생각한 만큼의 성능이 나오지 않았던 것이다. 그래서 Jens Axboe는 이 패치 시리즈슬랩 할당자 위에서 작동하는 per-cpu, lockless한 캐시를 만들었다. 매커니즘 자체는 복잡하지 않다. CPU별로 free object를 관리하는 linked list를, percpu API로 구현한 것이다. 따라서 각 CPU는 자신의 free list에서 할당을 시도하고, 해당 list가 비어있으면 슬랩 할당자로부터 추가로 할당하는 것이다. 현재 구현은 free list의 객체 수가 너무 많아질 경우 (현재로선 512 + 64 BIO를 넘으면) 64개는 슬랩 할당자에게 다시 돌려준다. 그리고 free list를 스택처럼 사용함으로써, 마지막에 접근한 객체를 다시 접근하도록 만들어서 cache affinity도 높여 성능을 향상했다.

 

그리고 여기엔 한 가지 특수성이 있는데, 이 캐시는 인터럽트가 활성화되어있을 때는 사용할 수 없다. (IRQ safe하지 않다) 따라서 인터럽트를 비활성화하는 IO Polling과 같은 경우에 사용할 수 있다. 대강 정리하면 1. Queueing을 도입해서 성능의 이득을 봤다. 2. IO Polling의 특성을 이용해서 lockless하게 캐시를 만들어서 성능의 이득을 봤다.

 

그리고 기존에는 bio를 memset으로 초기화했었는데, memset보다 각각의 필드를 하나씩 다 0으로 초기화하는 것이 더 빨라서 (memset은 생각보다 느리다) 각각의 필드를 직접 초기화하도록 바뀌었다. 이 모든 걸 적용했을 때 결과적으로 약 10%의 성능 향상(3.1M+ IOPS -> 3.5M+ IOPS)이 있었다.

나의 생각

글쓴이는 글을 마무리하면서, 이 사례로 슬랩 할당자를 더 최적화해야 하는 게 아닌가 이야기했지만, 나는 그렇게 생각하지 않는다. Jens Axboe에게 테스트할 때 어떤 SLAB/SLOB/SLUB중 슬랩 할당자를 사용하냐고 물어봤는데, SLUB을 사용했다고 한다.

 

SLUB은 queuing을 최소화하고 각 CPU에 페이지를 지정해주고, 그 페이지에서 할당과 해제를 함으로써 spatial locality를 활용하는데 bio 처럼 수명 주기가 매우 짧고 대량으로 할당과 해제가 되는 경우에는 SLUB의 이득을 보기가 어렵다. 그리고 IO Polling이라는 특수성 때문에 lockless한 캐시를 만들 수 있다는 점도 성능 향상에 도움이 되었다고 본다.

 

따라서 이건 슬랩 할당자를 최적화 하고 말고의 문제가 아니라고 본다. (물론 슬랩 위에 별도의 레이어를 만드는 건 그렇게 바람직하지는 않아보인다. 메모리가 부족해질 때 반납을 할 수 없기도 하고...)