- 提升I/O设备速度,HDD换成SSD,仍觉不够快
- PCI Express接口的SSD硬盘替代SATA接口的SSD硬盘,还是不够快
但无论I/O速度如何提升,比CPU还是太慢。SSD IOPS可到2万、4万,但CPU主频2GHz以上,每秒20亿次操作。如对I/O操作都由CPU发出对应指令,然后等待I/O设备完成操作后返回,那CPU有大量时间浪费在等待I/O设备完成操作。
这种等待无实际意义,对I/O设备大量操作,都只是把内存数据,传输到I/O设备,CPU只是傻等。传输数据量大时,如大文件复制,若所有数据都要经过CPU,就太浪费时间。
就有了DMA(Direct Memory Access,直接内存访问),减少CPU等待时间。
1 协处理器
DMA就是在主板放一块独立芯片。在内存和I/O设备数据传输时,不再通过CPU控制数据传输,而通过DMA控制器(DMA Controller,DMAC)。这块芯片,可认为是个协处理器(Co-Processor)。
DMAC最有价值在:
- 当传输数据大、速度快
如千兆网卡或硬盘,传输大量数据时,若都用CPU搬运,就忙不过来,可选择DMAC - 或传输数据特别小、速度慢
数据传输很慢,DMAC可等数据到齐,再发送信号,给到CPU去处理,而不让CPU忙等待
DMAC是“协助”CPU,完成对应的数据传输工作。在DMAC控制数据传输的过程中,还是需要CPU。
DMAC也是一个特殊I/O设备,CPU以及其他I/O设备一样,通过连接到总线来进行实际的数据传输。总线上的设备呢,其实有两种类型:
- 主设备(Master)
- 从设备(Slave)
想主动发起数据传输,须为主设备,如CPU。 从设备(如硬盘)只能接受数据传输。所以,若通过CPU传输数据:
- 要么是CPU从I/O设备读数据
- 要么是CPU向I/O设备写数据
那I/O设备不能向主设备发请求吗?可以是可以,不过发送的不是数据内容,而是控制信号。I/O设备可告诉CPU,我这里有数据要传输给你,但实际数据是CPU拉走的,而不是I/O设备推给CPU的。
DMAC既是主设备,又是从设备:
- 对CPU,它是从设备
- 对硬盘这样的IO设备,是主设备
2 DMAC数据传输的过程
① CPU作为主设备,向DMAC设备发请求
就是在DMAC里修改配置寄存器。
CPU修改DMAC配置时,会告诉DMAC:源地址的初始值及传输时的地址增减方式。
源地址
数据从哪传过来:
- 若从内存写数据到硬盘,即数据在内存里的地址
- 若从硬盘读数据到内存,即硬盘的I/O接口的地址
I/O地址是个内存地址,也能是个端口地址。
地址增减方式
数据是从大地址向小地址传输,还是从小地址往大地址传输。
还要传输目标地址初始值和传输时的地址增减方式。目标地址自然就是和源地址对应的设备,即数据传输的目的地。
要传输的数据长度
即共要传输多少数据。
② 设置完这些信息后,DMAC就变成空闲状态(Idle)。
若要从硬盘往内存加载数据,这时
③ 硬盘会向DMAC发起数据传输请求
这请求不是通过总线,而是通过一个额外连线。
④ DMAC再通过一个额外连线响应该申请
⑤ DMAC芯片就向硬盘的接口发起总线读的传输请求
数据就从硬盘读到DMAC控制器。
⑥ DMAC再向内存发起总线写的数据传输请求
把数据写入内存。
⑦ DMAC反复执行上面的⑤、⑥ 步
直到DMAC的寄存器里设置的数据长度传输完成。
- 数据传输完成之后,DMAC重新回到第3步的空闲状态
所以,整个数据传输的过程中,不是通过CPU来搬运数据,而由DMAC芯片来搬运数据。但是CPU在这个过程中也是必不可少的。因为传输什么数据,从哪里传输到哪里,其实还是由CPU来设置的。这也是为什么,DMAC被叫作“协处理器”。
外设很多都内置DMAC。最早计算机里没有DMAC,所有数据由CPU搬运。随数据传输需求多,先出现主板独立的DMAC控制器。到今天,各种I/O设备,数据传输需求越来越复杂,使用场景各不相同。显示器、网卡、硬盘对数据传输需求都不一样,所以各设备都有自己DMAC芯片。
2 你咋那么快?来看Kafka实现原理
有啥API,我们应用层调用一下,就能加速数据传输,减少CPU占用?开源项目很好利用了DMA数据传输方式,通过DMA实现非常大的性能提升,如Kafka。Kafka是处理实时数据的管道,常用做消息队列或收集和落地海量日志,其瓶颈也就在I/O。
Kafka有两种常见的海量数据传输情况:
- 从网络中接收上游的数据,然后落地到本地磁盘,确保数据不丢
- 从本地磁盘读出来,通过网络发送出去
写个简单程序,最直观的用个文件读操作,从磁盘上把数据读到内存,然后再用个Socket,把这些数据发送到网络:
1File.read(fileDesc, buf, len);2Socket.send(socket, buf, len);
这过程中,数据发生四次传输过程:
- 两次DMA传输
- 两次通过CPU控制的传输
① 第一次传输,从硬盘读到 os 内核缓冲区。通过DMA搬运
② 第二次传输,从内核缓冲区里数据,复制到应用分配的内存。通过CPU搬运
③ 第三次传输,从应用内存,再写到操作系统的Socket缓冲区。由CPU搬运
④ 最后一次传输,再从Socket缓冲区,写到网卡缓冲区。通过DMA搬运
只要“搬运”一份数据,却运了四次。从内核读缓冲区传输到应用内存,再从应用内存传输到Socket缓冲区,是把同一份数据在内存里搬来搬去,特没效率。
像Kafka的应用场景大部分最终利用到的硬件资源,其实又都是在干这搬运数据,要尽可能减少数据搬运。
Kafka做的就是把这数据搬运次数,从上面四次,变成两次,且只有DMA进行数据搬运,不需要CPU。
1@Override2public long transferFrom(FileChannel fileChannel, long position, long count) throws IOException {3 return fileChannel.transferTo(position, count, socketChannel);4}
最终调用Java NIO库transferTo方法。数据并没读到中间的应用内存,而是直接通过Channel,写入对应网络设备。且对Socket操作,也不是写入SocketBuffer,而是直接根据描述符(Descriptor)写入网卡缓冲区。于是,这过程只进行两次数据传输:
- 第一次,通过DMA,从硬盘读到os内核读缓冲区
- 第二次,则是根据Socket描述符信息,直接从读缓冲区里,写入网卡的缓冲区
同一份数据传输的次数从4次变成2次,并且没有通过CPU进行数据搬运,所有数据通过DMA传输。
这方法没有在内存层面去“复制(Copy)”数据,所以称零拷贝(Zero-Copy)。无论传输数据量的大小,传输同样数据,使用零拷贝能缩短65%时间,大幅提升机器传输数据的吞吐量。
3 总结
如果始终让CPU来进行各种数据传输工作,会特别浪费:
- 我们的数据传输工作用不到多少CPU核心的“计算”功能
- CPU的运转速度也比I/O操作要快很多。所以,我们希望能够给CPU“减负”
于是,工程师们就在主板上放上了DMAC这样一个协处理器芯片。通过这个芯片,CPU只需要告诉DMAC,我们要传输什么数据,从哪里来,到哪里去,就可以放心离开了。后续的实际数据传输工作,都会由DMAC来完成。随着现代计算机各种外设硬件越来越多,光一个通用的DMAC芯片不够了,我们在各个外设上都加上了DMAC芯片,使得CPU很少再需要关心数据传输的工作了。
在我们实际的系统开发过程中,利用好DMA的数据传输机制,也可以大幅提升I/O的吞吐率。最典型的例子就是Kafka。
传统地从硬盘读取数据,然后再通过网卡向外发送,我们需要进行四次数据传输,其中有两次是发生在内存里的缓冲区和对应的硬件设备之间,我们没法节省掉。但是还有两次,完全是通过CPU在内存里面进行数据复制。
在Kafka里,通过Java的NIO里面FileChannel的transferTo方法调用,我们可以不用把数据复制到我们应用程序的内存里面。通过DMA的方式,我们可以把数据从内存缓冲区直接写到网卡的缓冲区里面。在使用了这样的零拷贝的方法之后呢,我们传输同样数据的时间,可以缩减为原来的1/3,相当于提升了3倍的吞吐率。
这也是为什么,Kafka是目前实时数据传输管道的标准解决方案。
标签:DMA,为什么,Kafka,传输,内存,DMAC,数据传输,数据,CPU From: https://blog.51cto.com/JavaEdge/7128043