Zero Copy 에 대하여

바인하·2024년 1월 21일
0
post-thumbnail

카프카에 대해 공부하다가 제로카피(Zero-copy) 라는 키워드를 보게 되었다.
카프카 서버의 성능 개선을 위해 사용한 기법이라고 하여 관련 내용을 자세히 작성해보려고 한다.

zero-copy

zero-copy 는 디스크의 데이터를 네트워크로 전송할 때 일어나는 데이터 복사 작업을 최소화 한 데이터 전송 방식이다.
카프카에서는 파일 시스템에 저장된 메시지를 네트워크를 통해 전송할 때 zero-copy 기법을 사용하여 데이터 전송 성능을 향상시켰다.

일반적으로 파일 시스템에 저장된 데이터를 네트워크로 전송할 때는 아래와 같은 데이터 전달 절차로 이루어진다.

  1. 디스크 → Read Buffer (DMA ; Direct Memory Access)
    • 유저가 read() 시스템콜을 호출하면, DMA 엔진에 의해 디스크로부터 데이터를 읽어 커널 영역의 Read Buffer 에 저장
  2. Read Buffer → Application Buffer (CPU)
    • 커널 주소 공간에는 유저가 접근할 수 없으므로, Read Buffer 의 내용을 Application Buffer 로 복사
  3. Application Buffer → Socket Buffer (CPU)
    • 유저는 Application Buffer 에서 읽어들인 데이터를 Socket Buffer 로 전송하기 위해 send() 함수 호출
    • send() 함수 호출 시 커널 영역에 있는 Socket Buffer로 데이터 복사
  4. Socket Buffer → NIC Buffer (DMA)
    • Socket buffer에 있는 데이터를 NIC buffer로 복사하고 네트워크를 통해 전송

전통적인 데이터 복사 방식은 4번의 컨텍스트 스위칭과 4개의 메모리 복사본이 생기면서 불필요한 복사와 system call이 발생하게 된다.
이처럼 비효율적인 동작을 개선하기 위해 소개된 기법이 zero copy 이다.

리눅스 2.2 버전에서 처음 소개된 sendfile() 시스템 콜이 제로카피 동작을 구현하고 있다.

#include<sys/sendfile.h>
ssize_t sendfile(int out_fd, int in_fd, off_t * offset ", size_t" " count" );

자바에서는 nio 패키지의 transferTo(), transferFrom() 메소드로 구현되어 있으며, 2가지 메서드 역시 sendfile() 시스템 콜을 이용해 구현되어있다.

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

zero copy 를 사용하면, 커널 영역의 Read Buffer 에서 Socket Buffer로 직접 복사가 가능하여 효율적으로 데이터를 전송할 수 있다.
read(), send() 두번의 시스템콜이 transferTo() 한번의 호출로 가능해진다.

  1. 유저가 transferTo() 메서드를 통해 파일 전송 요청
  2. read() 시스템콜과 동일하게, DMA 엔진이 디스크에서 파일을 읽어 커널 주소 공간에 위치한 Read buffer에 데이터를 복사
  3. 커널 모드에서 유저 모드로 컨텍스트 스위칭 없이, 바로 Socket buffer로 데이터를 복사
  4. Socket buffer에 복사된 데이터를 DMA 엔진을 통해 NIC buffer로 복사

따라서 transferTo() 메서드 호출시 커널모드로 1번, 종료시 유저모드로 1번 총 2번으로 컨텍스트 스위칭으로 줄었고, 데이터의 복사본이 4군데에서 3군데로 줄어들었다.
컨텍스트 스위칭 횟수와 복사본의 개수가 줄어든만큼 CPU 자원의 낭비가 줄어들게 되어 성능이 향상되었다.

리눅스 커널 2.4 이후부터는 NIC 장비가 “Gather Operation”을 지원할 경우 복사본을 더 줄일 수 있게 되었다.

  1. 사용자가 transferTo() 메서드를 호출한다. DMA엔진이 디스크에서 파일을 읽어 커널에 위치한 Read Buffer로 데이터를 복사한다.
  2. 데이터를 Socket Buffer 로 복사하지 않는 대신 데이터가 저장된 위치와 데이터 사이즈에 대한 정보와 함께 디스크립터가 Socket Buffer 에 추가된다.
  3. DMA 엔진은 이 정보를 이용해 Read Buffer에 있는 데이터를 NIC Buffer 에 바로 복사하고, 네트워크로 데이터를 전송한다.

이러한 최적화를 위해서는 Gather operation과 프로토콜의 checksum 기능이 추가적으로 필요하다.

아래 표는 실제 리눅스 2.6 커널 버전에서 성능을 비교한 것인데 전통적인 방식의 파일 전송과 transfer() 메소드를 이용한 파일 전송 속도를 비교했을 때, 수행시간이 약 65% 줄어들었다.

디스크에서 파일을 읽은 후 추가 작업 없이 바로 네트워크로 전송하는 파일 서버나 정적 파일을 전송하는 웹 서버의 경우 zero copy 기법을 사용하면 성능 개선 효과를 얻을 수 있다.
특히 네트워크 속도가 매우 빨라서 성능상 병목점이 CPU로 몰릴 수록 불필요한 데이터 복사를 제거하여 성능 개선을 도모할 수 있다.

요약하자면, 디스크에서 파일을 읽고 네트워크로 전달하는 과정에서 read(), send() 두번의 시스템 콜로 인한 컨텍스트 스위칭과 데이터 복사에 따른 성능 저하가 따랐다.
zero copy의 transferTo() 메서드를 통한 하나의 시스템콜을 활용함으로써, 컨텍스트 스위칭과 데이터 복사에 필요한 비용을 줄여 성능 개선을 이룰 수 있다.

profile
되면 한다

0개의 댓글