Java 零拷贝_java高性能编程

Java 零拷贝_java高性能编程1、摘要零拷贝的“零”是指用户态和内核态间copy数据的次数为零。传统的数据copy(文件到文件、client到server等)涉及到四次用户态内核态切换、四次copy。四次copy中,两次在用户态和内核态间copy需要CPU参与、两次在内核态与IO设备间copy为DMA方式不需要CPU参与。零拷贝避免了用户态和内核态间的copy、减少了两次用户态内核态间的切换。零拷贝可以提高数据传输效率,但对于…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

1、摘要

零拷贝的“零”是指用户态和内核态间copy数据的次数为零。

传统的数据copy(文件到文件、client到server等)涉及到四次用户态内核态切换、四次copy。四次copy中,两次在用户态和内核态间copy需要CPU参与、两次在内核态与IO设备间copy为DMA方式不需要CPU参与。零拷贝避免了用户态和内核态间的copy、减少了两次用户态内核态间的切换。

零拷贝可以提高数据传输效率,但对于需要在用户传输过程中对数据进行加工的场景(如加密)就不适合使用零拷贝。

使用Zero Copy前后对比:

前:

7130162242e232e6bf8901d7f82375b9.png   , 后:

ec9a6143ae9d6beaabff6eb68b6db2cc.png

2、介绍

java 的zero copy多在网络应用程序中使用。Java的libaries在linux和unix中支持zero copy,关键的api是java.nio.channel.FileChannel的transferTo(),transferFrom()方法。我们可以用这两个方法来把bytes直接从调用它的channel传输到另一个writable byte channel,中间不会使data经过应用程序,以便提高数据转移的效率。

许多web应用都会向用户提供大量的静态内容,这意味着有很多data从硬盘读出之后,会原封不动的通过socket传输给用户。这种操作看起来可能不会怎么消耗CPU,但是实际上它是低效的:kernal把数据从disk读出来,然后把它传输给user级的application,然后application再次把同样的内容再传回给处于kernal级的socket。这种场景下,application实际上只是作为一种低效的中间介质,用来把disk file的data传给socket。

data每次穿过user-kernel boundary,都会被copy,这会消耗cpu,并且占用RAM的带宽。幸运的是,你可以用一种叫做Zero-Copy的技术来去掉这些无谓的 copy。应用程序用zero copy来请求kernel直接把disk的data传输给socket,而不是通过应用程序传输。Zero copy大大提高了应用程序的性能,并且减少了kernel和user模式的上下文切换

使用kernel buffer做中介(而不是直接把data传到user buffer中)看起来比较低效(多了一次copy)。然而实际上kernel buffer是用来提高性能的。在进行读操作的时候,kernel buffer起到了预读cache的作用。当写请求的data size比kernel buffer的size小的时候,这能够显著的提升性能。在进行写操作时,kernel buffer的存在可以使得写请求完全异步。

悲剧的是,当请求的data size远大于kernel buffer size的时候,这个方法本身变成了性能的瓶颈。因为data需要在disk,kernel buffer,user buffer之间拷贝很多次(每次写满整个buffer)。

而Zero copy正是通过消除这些多余的data copy来提升性能。

3、传统方式及涉及到的上下文切换

通过网络把一个文件传输给另一个程序,在OS的内部,这个copy操作要经历四次user mode和kernel mode之间的上下文切换,甚至连数据都被拷贝了四次,如下图:

具体步骤如下:

read() 调用导致一次从user mode到kernel mode的上下文切换。在内部调用了sys_read() 来从文件中读取data。第一次copy由DMA (direct memory access)完成,将文件内容从disk读出,存储在kernel的buffer中。

然后请求的数据被copy到user buffer中,此时read()成功返回。调用的返回触发了第二次context switch: 从kernel到user。至此,数据存储在user的buffer中。

send() Socket call 带来了第三次context switch,这次是从user mode到kernel mode。同时,也发生了第三次copy:把data放到了kernel adress space中。当然,这次的kernel buffer和第一步的buffer是不同的buffer。

最终 send() system call 返回了,同时也造成了第四次context switch。同时第四次copy发生,DMA egine将data从kernel buffer拷贝到protocol engine中。第四次copy是独立而且异步的。

bc07929312be205ef910da3cafb1afe5.png

963031d24519fd24df5f217bf163fa36.png

4、zero copy方式及涉及的上下文转换

在linux 2.4及以上版本的内核中(如linux 6或centos 6以上的版本)修改了socket buffer descriptor,使网卡支持 gather operation,通过kernel进一步减少数据的拷贝操作。这个方法不仅减少了context switch,还消除了和CPU有关的数据拷贝。user层面的使用方法没有变,但是内部原理却发生了变化:

transferTo()方法使得文件内容被copy到了kernel buffer,这一动作由DMA engine完成。 没有data被copy到socket buffer。取而代之的是socket buffer被追加了一些descriptor的信息,包括data的位置和长度。然后DMA engine直接把data从kernel buffer传输到protocol engine,这样就消除了唯一的一次需要占用CPU的拷贝操作。

5d58d0e8f6ffd77ad75af3538a25f008.png

f144c19c98bb5777bf26e1e25defd7e2.png

5、Java NIO 零拷贝示例

NIO中的FileChannel拥有transferTo和transferFrom两个方法,可直接把FileChannel中的数据拷贝到另外一个Channel,或直接把另外一个Channel中的数据拷贝到FileChannel。该接口常被用于高效的网络/文件的数据传输和大文件拷贝。在操作系统支持的情况下,通过该方法传输数据并不需要将源数据从内核态拷贝到用户态,再从用户态拷贝到目标通道的内核态,同时也减少了两次用户态和内核态间的上下文切换,也即使用了“零拷贝”,所以其性能一般高于Java IO中提供的方法。

5.1、通过网络把一个文件从client传到server:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 /**

2 * disk-nic零拷贝3 */

4 classZerocopyServer {5 ServerSocketChannel listener = null;6

7 protected voidmySetup() {8 InetSocketAddress listenAddr = new InetSocketAddress(9026);9

10 try{11 listener =ServerSocketChannel.open();12 ServerSocket ss =listener.socket();13 ss.setReuseAddress(true);14 ss.bind(listenAddr);15 System.out.println(“监听的端口:” +listenAddr.toString());16 } catch(IOException e) {17 System.out.println(“端口绑定失败 : ” + listenAddr.toString() + ” 端口可能已经被使用,出错原因: ” +e.getMessage());18 e.printStackTrace();19 }20

21 }22

23 public static voidmain(String[] args) {24 ZerocopyServer dns = newZerocopyServer();25 dns.mySetup();26 dns.readData();27 }28

29 private voidreadData() {30 ByteBuffer dst = ByteBuffer.allocate(4096);31 try{32 while (true) {33 SocketChannel conn =listener.accept();34 System.out.println(“创建的连接: ” +conn);35 conn.configureBlocking(true);36 int nread = 0;37 while (nread != -1) {38 try{39 nread =conn.read(dst);40 } catch(IOException e) {41 e.printStackTrace();42 nread = -1;43 }44 dst.rewind();45 }46 }47 } catch(IOException e) {48 e.printStackTrace();49 }50 }51 }52

53 classZerocopyClient {54 public static void main(String[] args) throwsIOException {55 ZerocopyClient sfc = newZerocopyClient();56 sfc.testSendfile();57 }58

59 public void testSendfile() throwsIOException {60 String host = “localhost”;61 int port = 9026;62 SocketAddress sad = newInetSocketAddress(host, port);63 SocketChannel sc =SocketChannel.open();64 sc.connect(sad);65 sc.configureBlocking(true);66

67 String fname = “src/main/java/zerocopy/test.data”;68 FileChannel fc = newFileInputStream(fname).getChannel();69 long start =System.nanoTime();70 long nsent = 0, curnset = 0;71 curnset = fc.transferTo(0, fc.size(), sc);72 System.out.println(“发送的总字节数:” + curnset + ” 耗时(ns):” + (System.nanoTime() -start));73 try{74 sc.close();75 fc.close();76 } catch(IOException e) {77 System.out.println(e);78 }79 }80 }

View Code

5.2、文件到文件的零拷贝:

8f900a89c6347c561fdf2122f13be562.png

961ddebeb323a10fe0623af514929fc1.png

1 /**

2 * disk-disk零拷贝3 */

4 classZerocopyFile {5 @SuppressWarnings(“resource”)6 public static void transferToDemo(String from, String to) throwsIOException {7 FileChannel fromChannel = new RandomAccessFile(from, “rw”).getChannel();8 FileChannel toChannel = new RandomAccessFile(to, “rw”).getChannel();9

10 long position = 0;11 long count =fromChannel.size();12

13 fromChannel.transferTo(position, count, toChannel);14

15 fromChannel.close();16 toChannel.close();17 }18

19 @SuppressWarnings(“resource”)20 public static void transferFromDemo(String from, String to) throwsIOException {21 FileChannel fromChannel = newFileInputStream(from).getChannel();22 FileChannel toChannel = newFileOutputStream(to).getChannel();23

24 long position = 0;25 long count =fromChannel.size();26

27 toChannel.transferFrom(fromChannel, position, count);28

29 fromChannel.close();30 toChannel.close();31 }32

33 public static void main(String[] args) throwsIOException {34 String from = “src/main/java/zerocopy/1.data”;35 String to = “src/main/java/zerocopy/2.data”;36 //transferToDemo(from,to);

37 transferFromDemo(from, to);38 }39 }

View Code

6、参考资料

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/192961.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • git 迁出/克隆远程仓库的指定分支方法(附常用git配置命令)

    普通克隆方式:gitclone<远程仓库地址>这种克隆方式默认是克隆master主分支,而且通过命令gitbranch–list能看到克隆后在本地也只有这一个分支,如果再通过新建分支再拉取指定分支,甚至可能还需要解决冲突,太繁琐。那么,如何快速有效的直接克隆远程指定分支?只需要一条命令:gitclone-b<指定分支名>&…

    2022年4月17日
    50
  • 最新!2018年中国程序员薪资生活调查报告

    最新!2018年中国程序员薪资生活调查报告根据中国互联网络信息中心(CNNIC)发布的第41次《中国互联网络发展状况统计报告》,截至2017年12月,我国网民规模达7.72亿,普及率达到55.8%,超过全球平均水平(51.7%)4.1个百分点,超过亚洲平均水平(46.7%)9.1个百分点。我国网民规模继续保持平稳增长,互联网模式不断创新、线上线下服务融合加速以及公共服务线上化步伐加快。而其中推动我国网民规模持续增长的原因离不开一批中国程序…

    2022年10月11日
    0
  • unity 阴影shader(高温下空气扭曲)

    简介千等万等终于等到了《耻辱2》打折,本以为可以爽一发了,然而各种出问题,先是steam下载速度奇慢无比,下了三天晚上好不容易下完的游戏,第一次打开给弹了个3D11CreateDeviceAndSwapChainFailed,折腾半天装了个补丁算是能打开游戏了,然而过完新手教学显卡驱动就崩了,崩了!崩了,连崩三回,差点想把坑爹的A卡从机箱掏出来顺着窗户扔出去,后来发现贴吧里面也有好几

    2022年4月11日
    53
  • latex大的中括号_文献引用中括号怎么标注

    latex大的中括号_文献引用中括号怎么标注括号是数学中最常用的符号之一。括号不仅能使我们的公式更加美观,还能使我们的表达更为清晰、丰富。latex里面的括号和我们常见的括号是一样的,主要是小括号、中括号(或者叫方括号)和花括号。这里我们看到花括号如果直接打出来的话是不显示任何东西的,这里我们需要加一个转义符,也就是反斜杠。如果只用直接的括号字符,只能打出固定大小的括号,比如这样打出的括号就比较小。那么如何打出更大一点的括号呢?在latex…

    2022年10月10日
    0
  • 锂电池稳压3.3V芯片_电源芯片型号

    锂电池稳压3.3V芯片_电源芯片型号干电池升压3.3V的电源芯片PW5100适用于一节干电池升压到3.3V,两节干电池升压3.3V的升压电路,PW5100干电池升压IC。干电池1.5V和两节干电池3V升压到3.3V的测试数据输入电压输入电流输出电压输出电流0.9V输入测试0.907V0.21A3.26V50MA0.887V0.45A3.21V100MA0.857V0.83A3.12V150MA输入电压输入电流输出电压输出电流1V输入测试1V0.9…

    2022年10月7日
    0
  • 宽字节注入详解_pba商业分析师含金量

    宽字节注入详解_pba商业分析师含金量尽管现在呼吁所有的程序都使用unicode编码,所有的网站都使用utf-8编码,来一个统一的国际规范。但仍然有很多,包括国内及国外(特别是非英语国家)的一些cms,仍然使用着自己国家的一套编码,比如gbk,作为自己默认的编码类型。也有一些cms为了考虑老用户,所以出了gbk和utf-8两个版本。我们就以gbk字符编码为示范,拉开帷幕。gbk是一种多字符编码,具体定义自行百度。但有一个地方尤其要…

    2022年10月15日
    0

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号