NIO与零拷贝


零拷贝是什么?

  • 1、零拷贝并不是没有拷贝,而是没有CPU拷贝。DMA拷贝是不可避免,且DMA拷贝速度远远快于CPU拷贝

  • 2、零拷贝是网络编程的关键,很多性能优化(文件传输场景)都离不开

  • 3、在Java程序中,常用的零拷贝有mmap(内存映射)和sendFile。那么他们在OS中,到底是怎么样设计的?

提示

  • 1、零拷贝是从操作系统角度上看,是没有CPU拷贝。因为内核缓冲区之间数据是没有重复的(只有kernelBuffer有一份数)

  • 2、零拷贝不仅仅带来更少的数据负责,还能带来其他的性能优势,例如:更少的上下文切换、更少的CPU缓存、伪共享以及无CPU校验和计算。

传统IO数据读写

以下是关于Java传统io和网络编程的一段代码

File file = new File("text.txt")
RandomAccessFile raf = new RandomAccessFile(file, "rw")

byte[] arr = new byte[(int)file.length];
raf.read(arr);

Socket socket = new ServerSocket(8080).accept();
socket.getOutputSteam().write(arr);

传统I/O

拷贝过程:

  • 首先将硬盘(Hard Drive)的数据先通过DMA拷贝到kernel Buffer(内核缓冲区)

  • 再使用CPU拷贝将kernel Buffer拷贝到User Buffer(用户缓冲区)

  • 然后数据在User Buffer(用户缓冲区)进行修改

  • 修改完成之后,通过CPU拷贝到socket buffer

  • 然后再通过 拷贝到协议引擎(协议栈)

传统I/O缺点:

  • 1、拷贝次数过多,一共经过了4次拷贝3次切换(如下图)

传统I/O

DMA(操作系统中的概念)

DMA(Direct Memory Access直接内存拷贝)不经过CPU

MMAP(内存映射)优化

针对传统IO拷贝的弊端,我们可以通过MMAP优化。

  • 1、mmap通过内存映射,将文件映射到kernel Buffer(内核缓冲区),同时,用户空间可以共享内核的数据,这样,在进行网络传输时就可以减少kernel(内核)空间到用户空间的拷贝次数 MMAP优化

虽然MMAP优化减少了一次拷贝,但是优化比较有限!

sendFile优化

1、Linux2.1版本提供了sendFile函数,其基本原理如下:

数据根本不经过用户态,直接从缓冲区进入到SocketBuffer,同时,由于和用户态完全无关,就减少了一次上下文切换。如图: sendFile优化

小结:Linux2.1版本进行了sendFile优化依旧有1次CPU拷贝。

2、在Linux2.4版本中,做了一些修改

为避免从内核缓冲区拷贝到SocketBuffer,直接拷贝到协议栈,从而再一次减少了数据拷贝,从而实现了零拷贝。但是这里有小部分数据还是有一次CPU拷贝,如下图所示,在kernel Buffer到Socket Buffer 有少量数据(拷贝如length、offset等少量信息、消耗低,可以忽略)。

如图: Linux2.4再次优化

sendFile和MMAP的区别:

  • mmap适合小数据量读写,sendFile适合大文件传输

  • mmap需要4次上下文切换,3次数据拷贝;sendFile需要3次上下文切换,最少2次数据拷贝。

  • sendFile可以利用DMA方式,减少CPU拷贝,mmap则不能(必须从内核拷贝到Socket缓冲区)

在NIO如何实现零拷贝

案例:

  • 1、使用传统的IO方式传递一个大文件

  • 2、使用NIO零拷贝方式传递(使用transferTo方法)一个大文件

  • 3、对比2种传递方式耗时时间内分别是多少


分类:Netty
标签: NIO
文章目录