이 내용은  IBM의 "무복사 기법을 통한 효율적인 데이터 전송" 글을 바탕으로 만들어졌습니다.
http://www.ibm.com/developerworks/kr/library/j-zerocopy/index.html

기존의 read하고 socket 으로 send하는 예제는 다음과 같이 복사됩니다.
기존 데이터 복사 방식 


Context Swithing은 다음과 같이 나타납니다.
기존 방식에서의 맥락 전환 




기존 방식은 Context Swithing은 총 4번이 일어나고, cpu copy는 2번 일어납니다.





만약 FileChannel의 transferTo 라는 메소드를 호출하면, 운영체제의 sendfile을 호출하게 됩니다.

아래 그림처럼 cpu copy는 한번만 일어납니다.
transferTo()를 사용할 때 일어나는 데이터 복사



context swithing은 두번만 일어나면서 속도향상이 일어납니다. 
 
transferTo()를 사용할 때의 맥락 전환


만약 Scatter gather IO를 지원하는 네트웍 카드이면  아예 cpu copy를 하지 않습니다.

transferTo()와 데이터 모으기를 사용한 데이터 복사



NIC는 Network interface card를 의미하는 것이고, 우리가 일반적으로 얘기는 하는 이더넷 카드, 네트웍 카드를 말합니다.

 
잘 들여다 보면, zero copy는 call 단계를 줄이면서 checksum 코드나 복사 비용을 적게하려는 시도라고 할 수 있습니다.


리눅스에서 zero copy 기법은 sendfile() 이라는 함수로 매핑되고 있습니다. 리눅스에서 돌아가는 어플리케이션들(apache http server, nginx, tomcat 6 )에서 sendfile 옵션을 제공해서 속도 향상을 할 수 있지요. 실제 Sendfile을 옵션을 주었을 때 상황에서 따라서 2배이상의 효과를 누리기도 하고 어쩔 때는 효과가 없기도 합니다.

 
중요한 점은 네트웍 카드에서 지원을 해줘야 합니다. Scatter gather DMA를 지원해야 줘야 하는데요. Scatter/gather DMA를 쓰기 위한 api는 메모리 청크단위로 read하고 write 하는 구조로 되어 있습니다. cpu가 따로 작업을 하지 않아도 되기 때문에 속도는 늘어날 수 있지요. (http://www.artima.com/cppsource/scattered_io.html)

struct iovec
{
  void*   iov_base;
  size_t  iov_len;
};
ssize_t readv(int fd, const struct iovec* vector, int count);
ssize_t writev(int fd, const struct iovec* vector, int count);



 


내가 사용하고 있는 서버의 네트웍 카드를 보니 broadcom 사의 NetXtrem II 이며, zero copy를 지원하는 것으로 되어 있습니다.

java를 이해하려면, 리눅스의 sendfile 을 제대로 이해할 필요가 있다. 이에 대한 원전격인  http://www.linuxjournal.com/article/6345?page=0,0의 내용을 봐야 합니다.

 

read 함수와 write 함수를 호출했을 때 어떻게 동작되는지 보여주는 그래프입니다. 

 

 

zero copy(sendfile 콜 호출)를 했을 때,  어떻게 동작하는 지 보여주는 그래프입니다.  0번의 cpu copy와 2번의 dma copy와 2번의 conext switching을 보여주고 있지요.

 

 

 

바로  mmap 이라는 함수를 사용하는 것인데요. (mmap은 펌웨어 개발자라면 다들 알고 있는 함수로 유명합니다.)

tmp_buf = mmap(file, len);
write(socket, tmp_buf, len);

 

dma copy는 2번, cpu copy는 한번, context switching은 2번 일어나는 구조입니다.  대신 사용자 영역에서 사용할 수 있는 user buffer에 데이터가 실린다는 점에서 훌륭한 구조라 할 수 있습니다.

 

zero copy의 큰 단점은 표준의 부재입니다. 운영체제마다 각각 구현했고, 리눅스나 유닉스, HP, Solaris끼리 서로 호환이 되지 않고 있습니다. 또한,  zero copy를 구현한 sendfile의 manual에 따르면, in은 무조건 소켓이 되지 못하고, out은 반드시 소켓이어야 한다는 점입니다. 따라서 쓰임새가 자유롭지 못해서 mmap을 사용하는 경우가 많이 있습니다.

  

java의 경우도 반드시 이 경우를 따를 수 밖에 없지요.  그림에서 보여주듯이 NIC 만 보여주고 있습니다.

즉, zero copy인 경우에 java의 FileChannel의 transferTo 메소드를 호출할 때는 socket to file 식은 쓸 수 없다점입니다. file-to-file, file-to-socket으로 쓰는 구조가 되는 것입니다. 그리고, 동적인 데이터보다는 static 데이터 위주로 사용되기 때문에 dynamic한 작업을 하는 경우에는 맞지 않습니다. 속도를 저하시킬 수 있기 때문입니다.

 그래서 ibm 참조자료에서 예제를 파일을 전송하는 것으로 든 이유가 바로 그런 부분입니다.



참조자료

http://www.ibm.com/developerworks/kr/library/j-zerocopy/index.html

Posted by '김용환'
,