카프카에 대해 공부하다가 제로카피(Zero-copy) 라는 키워드를 보게 되었다.
카프카 서버의 성능 개선을 위해 사용한 기법이라고 하여 관련 내용을 자세히 작성해보려고 한다.
zero-copy 는 디스크의 데이터를 네트워크로 전송할 때 일어나는 데이터 복사 작업을 최소화 한 데이터 전송 방식이다.
카프카에서는 파일 시스템에 저장된 메시지를 네트워크를 통해 전송할 때 zero-copy 기법을 사용하여 데이터 전송 성능을 향상시켰다.
일반적으로 파일 시스템에 저장된 데이터를 네트워크로 전송할 때는 아래와 같은 데이터 전달 절차로 이루어진다.
전통적인 데이터 복사 방식은 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() 한번의 호출로 가능해진다.
따라서 transferTo() 메서드 호출시 커널모드로 1번, 종료시 유저모드로 1번 총 2번으로 컨텍스트 스위칭으로 줄었고, 데이터의 복사본이 4군데에서 3군데로 줄어들었다.
컨텍스트 스위칭 횟수와 복사본의 개수가 줄어든만큼 CPU 자원의 낭비가 줄어들게 되어 성능이 향상되었다.
리눅스 커널 2.4 이후부터는 NIC 장비가 “Gather Operation”을 지원할 경우 복사본을 더 줄일 수 있게 되었다.
이러한 최적화를 위해서는 Gather operation과 프로토콜의 checksum 기능이 추가적으로 필요하다.
아래 표는 실제 리눅스 2.6 커널 버전에서 성능을 비교한 것인데 전통적인 방식의 파일 전송과 transfer() 메소드를 이용한 파일 전송 속도를 비교했을 때, 수행시간이 약 65% 줄어들었다.
디스크에서 파일을 읽은 후 추가 작업 없이 바로 네트워크로 전송하는 파일 서버나 정적 파일을 전송하는 웹 서버의 경우 zero copy 기법을 사용하면 성능 개선 효과를 얻을 수 있다.
특히 네트워크 속도가 매우 빨라서 성능상 병목점이 CPU로 몰릴 수록 불필요한 데이터 복사를 제거하여 성능 개선을 도모할 수 있다.
요약하자면, 디스크에서 파일을 읽고 네트워크로 전달하는 과정에서 read(), send() 두번의 시스템 콜로 인한 컨텍스트 스위칭과 데이터 복사에 따른 성능 저하가 따랐다.
zero copy의 transferTo() 메서드를 통한 하나의 시스템콜을 활용함으로써, 컨텍스트 스위칭과 데이터 복사에 필요한 비용을 줄여 성능 개선을 이룰 수 있다.