Data Engineering/Kafka

Kafka의 Zero copy

신수동탈곡기 2022. 2. 12. 18:21

Zero copy


Zero-copy란 컴퓨터에서 CPU의 개입을 받지 않고 한 메모리의 영역에서 다른 메모리의 영역으로 데이터를 카피하는 작업을 말한다. 불필요한 데이터 복사을 줄이고 CPU 자원을 아껴 성능을 개선할 수 있다. Kafka는 이 방식을 사용하여 클라이언트에서 요청하는 메시지를 빠르게 송신할 수 있다.

카프카는 파일(또는 리눅스의 경우 파일시스템 캐시)의 메시지를 중간 버퍼 메모리에 쓰지 않고 곧바로 네트워크 채널로 전송한다. 데이터를 클라이언트에게 전송하기 위해 로컬 캐시 메모리에 저장하는 대부분의 데이터베이스와 달리 Kafka가 갖는 하나의 특징이며 데이터 전송을 빠르게 하여 성능을 향상시킨다.

아래에서 데이터를 전송하는 일반적인 방식과 Kafka가 채택한 Zero copy 방식을 비교해본다.

일반적인 Copy 방식


File.read(fileDesc, buf, len);
Socket.send(socket, buf, len);

파일에서 바이트를 복사하고 소켓으로 전송하는 간단한 작업이다. 겉으로는 간결해 보이지만 내부적으로 copy 연산은 유저모드와 커널모드를 4번 왔다갔다 하며(4번의 context switch가 이루어지며) 데이터의 복사 역시도 4번이 이루어져야 copy 연산이 완료된다. 아래의 그림은 파일로부터 소켓에 옮기기 위해 데이터가 어떻게 움직이는지, 어떻게 context swtich가 이루어지는지 보여준다.

일반적인 복사에서의 데이터 복사 단계와 Context switch

  1. 어플리케이션에서 read() 시스템 콜을 호출해 파일을 요청하면 DMA*엔진(direct memory access engine)이 디스크에 있는 데이터를 읽어 커널의 주소공간에 있는 Read buffer에 복사하며 첫 번째 복사가 진행된다. 이 때 유저모드에서 커널모드로 변경하는 context swtich(유저모드 → 커널모드로 모드가 변경되는 현상, 커널모드 → 유저모드도 포함)를 일으킨다.
  2. 요청된 만큼의 데이터가 커널의 Read buff에서 Application buffer로 두 번째 복사가 진행되며 read() 시스템 콜이 종료된다. read() call이 종료되며 커널모드에서 유저모드로 두 번째 conext swtich가 일어난다. 현재는 유저의 주소공간에 있는 Application buffer에 데이터가 저장된 상황이다.
  3. 어플리케이션은 Application buffer에 있는 데이터를 소켓에 전달하기 위해 send() 시스템 콜을 호출한다. 이 때 Application buffdf에 있는 데이터는 커널 주소영역에 존재하는 Socket buffer로 세 번째 복사를 진행한다. 이 때도 유저모드에서 커널모드로 전환하는 context switch가 발생한다.
  4. 커널 주소영역의 DMA엔진이 Socket buffer에 있는 데이터를 NIC(network interfact controller, 흔히 랜카드라 부르는 네트워크 장치)로 데이터로 복사하며 네 번째 복사가 진행된다. send() 시스템 콜이 종료되며 다시 커널모드에서 유저모드로 context switch가 발생한다.

처음 단계에서 데이터를 유저 버퍼로 바로 보내지 않고 커널 버퍼를 거치는 단계는 디스크-메모리, 메모리-네트워크 장치 간의 속도 차이를 극복하기 위해 도입했다. 어플리케이션에서 커널의 버퍼보다 적은 양의 데이터를 요청할 경우, 커널은 요청한 데이터(1)와 그 다음 데이터(2)도 미리 읽어 요청한 데이터(1)을 반환하고, 이후에 그 다음 데이터(2)를 요청할 경우 디스크에서 물리적 I/O를 수행하지 않고 캐시에서 읽어 반환하는 “readahead cache”로 작동할 수 있기 때문이다.

하지만 커널의 버퍼사이즈보다 큰 데이터를 요청하면 이런 방식이 무용지물이 된다. 요청한 데이터 이후의 데이터를 미리 쌓아두어 요청하면 캐시에서 꺼내어주는 readahead cache 역할을 할 수 없고, 그냥 중간에 복사 단계가 추가되는 비효율이 발생한다. context switch가 발생하는 것도 덤.

*DMA(Direct Memory Access) 및 입출력 제어방식에 대해 알아보기

 

PIO(Programmed I/O)와 DMA(Direct Memory Access)

입출력 제어방식 컴퓨터와 입출력장치 사이에서 데이터가 오가는 방식을 입출력 제어방식이라고 하는데, DMA와 PIO는 이 입출력 제어방식들 중 하나이며 총 네 가지 방식이 있다. 대형 컴퓨터에

h-devnote.tistory.com

제로 카피(Zero copy)


제로 카피는 위의 상황과 어떤 차이가 있는지, 어떻게 성능을 개선하는지 알아본다.

제로 카피는 리눅스 2.2의 sendfile() 시스템 콜으로 구현했으며, Java의 transferTo() 메소드 활용할 수 있다.(Java의 class library - java.nio.chaneels.Filechannel)

public void transferTo(long position, long count, WritableByteChannel target);

위의 상황을 찬찬히 생각해보면 알 수 있지만 두, 세 번째 복사는 실질적으로 필요하지 않다. 어플리케이션(클라이언트, 유저측)에서는 데이터를 캐싱하고 소켓버퍼로 되돌려주는 일 밖에 하지 않는다. 게다가 데이터는 읽기 버퍼(첫 번째 복사에서 저장하는 공간)에서 소켓 버퍼로 바로 보내질 수 있다.

Zero copy에서의 데이터 복사 단계와 Context switch

  1. transferTo() 메소드로 파일 전송을 요청하면 커널모드로 전환하는 context switch가 발생하며 read() 시스템 콜 처럼 파일 시스템으로부터 DMA엔진이 데이터를 읽어 커널 주소공간에 위치하는 Read buffer에 복사한다.
  2. 유저모드로 변환하지 않고(context swtich를 일으키기 않고) Read Buffer의 데이터를 Socket Buffer로 복사한다.
  3. Socket buffer에 복사된 데이터를 DMA 엔진을 통해 NIC Buffer로 복사된다. 데이터가 전송되고 transferTo() 메소드를 리턴한다. (유저모드로 context swtich)

비교


Zero copy는 두 가지 면에서 성능을 개선할 수 있었다. 첫 번째는 복사 횟수다. 데이터를 유저 주소공간에 있는 Application buffer로 복사하지 않기 때문에 4번의 복사를 3번으로 줄여 I/O 횟수를 개선했다. 더불어 컨텍스트 스위칭도 4회에서 2회로 줄였다. 유저모드와 커널모드를 오가는 컨텍스트 스위칭 횟수가 줄은만큼 CPU 자원을 낭비가 줄어 성능을 개선했다.

참고자료


https://developer.ibm.com/articles/j-zerocopy/

https://en.wikipedia.org/wiki/Zero-copy

 

Zero-copy - Wikipedia

"Zero-copy" describes computer operations in which the CPU does not perform the task of copying data from one memory area to another or in which unnecessary data copies are avoided. This is frequently used to save CPU cycles and memory bandwidth in many ti

en.wikipedia.org