当前位置: 首页 > news >正文

颠覆传统IO:零拷贝技术如何重塑Java高性能编程?

01 引言

零拷贝(Zero-copy)是高性能IO绕不开的话题,通过减少拷贝次数(IO次数)来提高IO的性能。

要了解零拷贝就要先了解一下几个基本概念:用户态(User Mode)、内核态(Kernal Mode)、虚拟内存以及DMA

  • 用户态:也叫用户空间,该区域主要用来运行程序代码,为了保证系统内核的安全,它不能直接访问内存等硬件设备,必须通过系统调用进入到内核态来访问那些受限的资源。
  • 内核态:也可以成为内核空间,该区域是运行内核代码的地方,就是计算底层的代码,如80中断等。可以执行任意的指令访问系统资源,既可以访问内核空间也可以访问用户空间。
  • 虚拟内存:操作系统为每个进程分配了独立的虚拟地址空间,也就是虚拟内存,虚拟地址空间又分为用户空间和内核空间。
  • DMA:内存直接读取(Direct Memory Access),又被人成为协处理器。是一种允许外围设备(硬件子系统)直接访问系统主内存的机制。

02 基本概念

我们先了解一下传统的IO,对于用户进程发出读写指令时,计算机是如何运行的。

  • 当用户进程调用read(),用户态无法调用内核态的设备,只能触发系统调用(IO)。这时计算机需要从用户态切换为内核态。
  • 到达内核态之后,计算机通过DMA控制器将数据从磁盘读取出来,放到内核的缓冲区。完成第一次拷贝。
  • CPU需要将缓冲区的数据拷贝到用户态的缓冲区,完成第二次拷贝,也是read()函数的返回。这时计算器需要从内核态切换为用户态。
  • 因为最终的数据需要通过网卡输出,所以用户进程就需要调用write()函数,CPU将用户缓冲区的数据拷贝到Socket缓冲区,完成第三次拷贝。同时需要再次触发系统调用。这时计算机又需要从用户态切换为内核态。
  • DMA控制器把数据从Socket缓冲区,拷贝到网卡设备输出,至此完成第四不拷贝。同时需要将内核态切换为用户态,write()函数返回。

从流程来看,传统的IO读写操作需要四次的拷贝,以及用户态和内核态的来回切换。而这些操作都是比较消耗资源的。所以传统的IO有它的瓶颈。

那这样的问题能不能解决呢?

当然可以,零拷贝的技术正是为此而来。

03 零拷贝

零拷贝(Zero-Copy)一般指的是从磁盘读取文件发送到网络或者从网络接收数据写入到磁盘文件的过程中,最大程度的减少数据的拷贝次数。零拷贝并不是真正上将拷贝的次数变成0,只是减少。

Java中也可以使用零拷贝技术, 主要是java.nio.channels.FileChannel中的方法:

  • transferTo(long position, long count, WritableByteChannel target)
  • transferFrom(ReadableByteChannel src,long position, long count)

用来实现字节数据从一个channel转换到另一个channel中,这里就是Page CacheSocket缓冲区的拷贝。

从示例图中,可以看到将两次拷贝变成了一次拷贝,总共三次拷贝。

  • 用户进程调用了FileChannel.transferTo()后,触发系统调用,系统从用户态切换到内核态。
  • DMA协处理器将文件数据拷贝到内核缓冲区Page Cahe。这是第一次拷贝。
  • CPU将内核缓冲区的数据拷贝到Socket缓冲区,完成第二次拷贝。
  • DMA又将Socket缓冲区的数据拷贝到网卡进行数据传输,完成第三次拷贝。

内核缓冲区到Socket缓冲区都属于内核态,能不能共享或者取消这次拷贝呢?这个属于系统层面的设计。而linux 2.4版本中已经做了优化。

“Then by using the DMA scatter/gather operation, the network interface card can gather all the data from different memory locations and store the assembled packet in the network card buffer.”

避免了从内核缓冲区拷贝到 Socket 缓冲区的操作,直接拷贝到协议栈,从而再一次减少了数据拷贝。

3.1 Java的场景

@Test
void test06() throws Exception {StopWatch watch = new StopWatch();watch.start();try (FileInputStream fileInputStream = new FileInputStream(Path.of("src/main/file/upload/10最大图片.jpg").toFile());FileOutputStream outputStream = new FileOutputStream(Path.of("src/main/file/temp/part.jpg").toFile())) {byte[] buffer = new byte[1024];int len = 0;while ((len = fileInputStream.read(buffer)) != -1) {outputStream.write(buffer, 0, len);}}watch.stop();System.out.println(watch.getTotalTimeMillis());System.out.println("----------------------------------");StopWatch watch2 = new StopWatch();watch2.start();try (RandomAccessFile sraf = new RandomAccessFile("src/main/file/upload/10最大图片.jpg", "r");RandomAccessFile raf = new RandomAccessFile("src/main/file/temp/part1.jpg", "rw")) {long length = sraf.length();System.out.println("length:" + length);// 调用transferTo方法sraf.getChannel().transferTo(0, length, raf.getChannel());}watch2.stop();System.out.println(watch2.getTotalTimeMillis());
}

执行结果:

我们可以看到使用零拷贝,是传统的IO性能的10倍左右。意不意外,惊喜不惊喜!

3.2 Socket场景

Linux 中系统调用 sendfile() 可以实现将数据从一个文件描述符传输到另一个文件描述符,从而实现了零拷贝技术。


try (ServerSocketChannel server = ServerSocketChannel.open();FileChannel fileChannel = FileChannel.open(Paths.get("data.big"), READ)) {server.bind(new InetSocketAddress(9000));SocketChannel client = server.accept();fileChannel.transferTo(0, fileChannel.size(), client);
}

04 零拷贝技术栈扩展

框架零拷贝实现类应用场景
NettyFileRegion大文件网络传输
RocketMQMappedFile消息存储
TomcatSendfileFeature静态资源传输
gRPCByteBufferInputStream流式数据传输

05 小结

零拷贝和传统IO的性能产生了相当大的性能差异,赶快用起来吧。零拷贝的实现方式有很多如mmapsendfiledmadirectI/O等。

你了解几个呢?评论区留言。

http://www.lryc.cn/news/584505.html

相关文章:

  • 【Linux基础命令使用】查找文件、归档管理、计划任务命令详解
  • 决策树与随机森林Python实践
  • 云原生技术与应用-Docker高级管理--Dockerfile镜像制作
  • STM32之风扇模块(开关控制+PWM调速)
  • 相机:以鼠标点为中心缩放(使用OpenGL+QT开发三维CAD)
  • 动态物体滤除算法
  • 【读书笔记】如何画好架构图:架构思维的三大底层逻辑
  • CTFHub————Web{信息泄露[备份文件下载(vim缓存、.DS_Store)]}
  • UI前端与数字孪生结合案例分享:智慧城市的智慧能源管理系统
  • JAVA面试宝典 -《新潮技术:协程与响应式编程实践》
  • Java 中的多线程实现方式
  • 原创数学建模国赛半自动化辅助排版模板及国赛论文排版格式要求
  • 藏文识别技术的关键挑战与解决方案
  • Win11系统安装Anaconda环境极简教程
  • RV1126平台(Buildroot Linux)+ SunplusIT SPCA2688 USB摄像头 RTSP推流全流程复盘与问题解决记录
  • 反向传播notes
  • 用 ngrok + SSH 实现公网远程控制电脑
  • AI驱动的低代码革命:解构与重塑开发范式
  • Java观察者模式实现方式与测试方法
  • LUMP+NFS架构的Discuz论坛部署
  • 04-ES6
  • RuoYi-Cloud ruoyi-gateway 网关模块
  • 大数据时代UI前端的智能化升级:基于机器学习的用户意图预测
  • 在生信分析中,处理vcf 比较好用的python包推荐
  • 【PTA数据结构 | C语言版】顺序栈的3个操作
  • WebSocket实战:实现实时聊天应用 - 双向通信技术详解
  • Apache
  • AI探索 | 豆包智能助手跟扣子空间(AI办公助手)有什么区别
  • Java 与 MySQL 性能优化:MySQL连接池参数优化与性能提升
  • ClickHouse 查看正在执行的SQL查询