系统设计 - Zero-Copy总结和性能测试

zero-copy应用场景

应用场景:将本地一个文件通过网络传输给另一个程序
关键字:数据复制过程中,内容不进行修改

zero-copy技术的使用场景有很多,比如Kafka, 又或者是Netty等,可以大大提升程序的性能
下面我们通过对比传统方式和zero-copy的方式传输数据,来理解zero-copy。

传统方式传输数据

代码如下:

// 将文件读取到buf中 
File.read(fileDesc, buf, len);
// 将buf中的数据写入到socket中
Socket.send(socket, buf, len);

结合下图理解理解操作如下:

系统设计 - Zero-Copy总结和性能测试_第1张图片

系统设计 - Zero-Copy总结和性能测试_第2张图片
详细步骤如下
1. 调用File.read()方法会发生上下文切换(context switch),从user mode切换到kernel mode。在read()内部会调用sys_read()来从文件中读取数据。第一次copy由DMA (direct memory access)完成,将文件内容从磁盘读出,并存储在kernel空间的buffer中,为方便说明,这个buffer称为reader buffer。
2. 然后请求的数据被从kernel空间的buffer copy到user buffer中,这是第二次copy。调用的返回又触发了第二次上下文切换,从kernel mode返回到user mode。至此,数据存储在user buffer中。
3. Socket.send()会触发了第三次上下文切换,从user mode到kernel mode,并执行第三次copy,将数据从user buffer重新复制到kernel的buffer中。当然,这次的kernel buffer和第一步的kernel buffer不是同一个buffer,这次buffer和目标socket关联,命名为socket buffer。
4. 完成copy后,Socket.send()返回时,同时也造成了第四次上下文切换。同时第四次copy发生,DMA egine将数据从kernel buffer复制到网卡设备(protocol engine)中。第四次copy是独立而且异步的。

结论
以上操作要经历4次user mode和kernel mode之间的上下文切换,数据都被拷贝了4次。通过上面的分析,我们发现第2步和第3步数据copy是多余,系统完全可以将文件读取到kernel buffer中后,直接将kernel buffer中的数据写入socket。为了实现这个优化,linux引入了zero copy。

zero-copy

要支持zero-copy要满足如下条件
a. 操作系统的要求:linux 2.4及以上版本的内核中,并且网卡支持 gather operation。

java中使用 FileChannel的transferTo()和transferFrom()方法使用zero-copy,如果底层系统支持zero-copy

代码:

public void transferTo(long position, long count, WritableByteChannel target); 
// transferTo()方法底层会调用系统方法sendfile()
ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

系统设计 - Zero-Copy总结和性能测试_第3张图片
系统设计 - Zero-Copy总结和性能测试_第4张图片

结合下图理解理解操作如下
a. 执行transferTo()方法后,会发生上下文切换,从user mode到kernel mode,然后将文件内容copy到kernel buffer(称为Read buffer),这一操作由DMA engine完成。。
b. 和传统方式不同,zero-copy没有将数据copy到socket buffer,只有数据的描述信息(如数据的位置和长度)存储到socket buffer中。然后DMA engine直接把数据从kernel buffer传输到网卡设备(protocol engine)。 完成复制后,又会发生上下文切换,从kernel mode到user mode

结论
1. Zero-copy上下文切换的次数从4次降低到2次,数据复制次数从4次降低到2次
2. Zero-copy 中数据的copy都由DMA执行,CPU不参与复制,从而节省CPU的消耗
3. Zero-copy中的zero不是指不需要copy,而是指user mode到kernel mode copy数据的次数为零

支持zero-copy的操作系统
除了Linux系统,其他系统也支持zero-copy
• Microsoft Windows通过TransmitFile API支持zero-copy
• macOS supports zero-copy through the FreeBSD portion of the kernel

性能测试

下面我们使用代码来比较传统方式和zero-copy的传输效率
测试代码:客服端从本地读取文件,通过网络传输到服务端

  • TraditionalClient.java和 TraditionalClient.java分别实现测试的客服端和服务
  • TransferToServer.java and TransferToClient.java实现的功能和上面的相同,不同点时,TransferToClient使用transferTo()实现zero-copy功能。

测试代码来源:https://developer.ibm.com/articles/j-zerocopy/#download
不过在测试过程中,此源码在测试有问题,我们对测试代码进行修改:
TransferToClient.java

// 原来的版本,这里会有问题,因为transferTo()一次最多发送8388608个字节,需要循环发送。
long position = 0;
while(curnset != 0) {
        curnset = fc.transferTo(position, fsize, sc);
        position += curnset;
        System.out.println("total bytes transferred--" + position + " and time taken in MS--" + (System.currentTimeMillis() - start));
}

TraditionalServer.java

// nread判断条件修改为 nread <=0,而不是nread==0
byte[] byteArray = new byte[4096];
    while(true) {
    	int nread = input.read(byteArray , 0, 4096);
    	if (nread <= 0)
    		break;
    }
}

文件传输花费时间对比
系统设计 - Zero-Copy总结和性能测试_第5张图片
结论:zero-copy的传输效率比传统的方式快一倍

你可能感兴趣的:(系统设计,系统设计)