我刚读了一篇解释零拷贝机制的文章.
它讨论了有和没有Scatter/Gather支持的零拷贝之间的区别.
网卡没有SG支持,数据副本如下

NIC支持SG,数据副本如下

总之,使用SG支持的零拷贝可以消除一个CPU拷贝.
我的问题是为什么内核缓冲区中的数据可能会分散?
在Java中使用零拷贝将大文件从文件传输到套接字时,我发现了一些奇怪的行为.我的环境:
程序的作用:客户端将输入文件复制到套接字中,服务器使用零复制方法将套接字复制到输出文件:transferFrom和transferTo.如果文件大小相对较大,并非所有字节都到达服务器,在Windows情况下为100Mb +,在Centos情况下为2GB +.客户端和服务器驻留在同一台计算机上,localhost地址用于传输数据.
行为因操作系统而异.在Windows上,客户端成功完成transferTo方法.传输的字节数等于输入文件大小.
long bytesTransferred = fileChannel.transferTo(0, inputFile.length(), socketChannel);
另一方面,服务器报告较少的接收字节数.
long transferFromByteCount = fileChannel.transferFrom(socketChannel, 0, inputFile.length());
在Linux上,即使输入文件大小为4Gb,客户端上的传输也是2Gb.两种配置都有足够的空间.
在Windows上,我能够使用以下解决方法之一传输130Mb文件:1)增加服务器上的接收缓冲区大小和2)在客户端中添加线程睡眠方法.这使我认为当所有字节都发送到套接字发送缓冲区而不是服务器时,客户端上的transferTo方法完成.这些字节是否使它成为服务器是不能保证的,这会给我的用例带来问题.
在Linux上,我能够通过单个transferTo调用传输的最大文件大小是2Gb,但至少客户端报告发送到服务器的正确字节数.
我的问题:客户确保将文件保证交付到服务器,跨平台的最佳方式是什么?在Windows上使用什么机制来模拟sendfile()?
这是代码:
客户端 - ZeroCopyClient.java:
import org.apache.commons.io.FileUtils;
import java.io.*;
import java.net.*;
import java.nio.channels.*;
public class ZeroCopyClient {
public static void main(String[] args) throws IOException, InterruptedException {
final File inputFile = new File(args[0]);
FileInputStream fileInputStream = new FileInputStream(inputFile);
FileChannel fileChannel = fileInputStream.getChannel();
SocketAddress socketAddress = new InetSocketAddress("localhost", 8083);
SocketChannel socketChannel = SocketChannel.open(); …Run Code Online (Sandbox Code Playgroud) 我试图了解vmsplice(2)syscall 的功能(此处的手册页)。关于SPLICE_F_GIFT标志的作用,我有两个问题:
手册页指出,一旦将页面赠予内核,就不能再修改内存。这是否意味着该内存将永远固定,还是可能指的是赠礼过程无法映射的虚拟内存,而不是物理内存?换句话说,这种用法的典型用法是什么样的?
如果我不设置SPLICE_F_GIFT,vmsplice(2)与矢量化写syscall 有什么不同writev(2)吗?
我正在使用 OpenCL 进行一些图像处理,并希望使用它将 RGBA 图像直接写入帧缓冲区。工作流程如下图所示:
1) 将帧缓冲区映射到用户空间。
2) 使用 clCreateBuffer 创建 OpenCL 缓冲区,标志为“CL_MEM_ALLOC_HOST_PTR”
3)使用clEnqueueMapBuffer将结果映射到framebuffer。
然而,这不起作用。屏幕上什么也没有。然后我发现帧缓冲区映射的虚拟地址与映射OpenCL的虚拟地址不同。有没有人做过从 GPU 到帧缓冲区的数据零复制移动?我应该使用什么方法来实现此目的有任何帮助吗?
一些关键代码:
if ((fd_fb = open("/dev/fb0", O_RDWR, 0)) < 0) {
printf("Unable to open /dev/fb0\n");
return -1;
}
fb0 = (unsigned char *)mmap(0, fb0_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
...
cmDevSrc4 = clCreateBuffer(cxGPUContext, CL_MEM_READ_WRITE | CL_MEM_ALLOC_HOST_PTR, sizeof(cl_uchar) * imagesize * 4, NULL, &status);
...
fb0 = (unsigned char*)clEnqueueMapBuffer(cqCommandQueue, cmDevSrc4, CL_TRUE, CL_MAP_READ, 0, sizeof(cl_uchar) * imagesize * 4, 0, NULL, NULL, &ciErr);
Run Code Online (Sandbox Code Playgroud) 我正在尝试使用splice(man 2 splice)将数据从UDP套接字直接复制到文件.不幸的是,第一次调用splice()会返回EINVAL.
手册页指出:
EINVAL Target file system doesn't support splicing; target file is opened in
append mode; neither of the descriptors refers to a pipe; or offset
given for nonseekable device.
Run Code Online (Sandbox Code Playgroud)
但是,我相信这些条件都不适用.我正在使用Fedora 15(内核2.6.40-4),所以我相信所有文件系统都支持splice().目标文件在第一次调用splice时应该是无关紧要的,但为了完整性我将通过它打开它open(path, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR).两个调用都使用管道,除了NULL之外,两个调用都不使用偏移量.
这是我的示例代码:
int sz = splice(sock_fd, 0, mPipeFds[1], 0, 8192, SPLICE_F_MORE);
if (-1 == sz)
{
int err = errno;
LOG4CXX_ERROR(spLogger, "splice from: " << strerror(err));
return 0;
}
sz = splice(mPipeFds[0], 0, file_fd, 0, …Run Code Online (Sandbox Code Playgroud) 网络是我操作系统中最糟糕的区域,所以请原谅我提出一个不完整的问题.我已经读了几个小时了,但是我的脑子里有点游泳.(对我而言,与制定网络协议相比,我觉得芯片设计很容易.)
我有一些通过套接字相互通信的网络服务.具体来说,创建套接字fd = socket(PF_INET, SOCK_STREAM, 0);,自动获取TCP/IP.我需要这个作为基本案例,因为这些服务可能在不同的机器上运行.
但对于一个项目,我们试图将它们全部压缩到基于Atom Z530P的功能不足的嵌入式"设备"中,因此在我看来,内存复制开销是我们可以优化的.我在这里一直在阅读:data-link-access-and-zero-copy和Linux_packet_mmap以及packet_mmap.
对于这种情况,可以创建如下的套接字:fd = socket(PF_PACKET, PF_RAW, 0);.还有很多其他事情要做,比如分配环形缓冲区,对它们进行映射,将它们与套接字相关联等等.看起来你只能使用sendto和recvfrom传输数据.据我所知,由于套接字是本地的,你不需要一个可靠的"流"类型套接字,所以原始套接字是适当的接口,我猜测环形缓冲区是以页面粒度使用的,每个数据包(或数据报)从页面边界开始.
在我花费大量时间尝试进一步调查之前,我希望一些有用的人可以帮助我解决一些问题:
在此先感谢您的帮助!
对我的应用程序进行概要分析表明它在字符串分配中花费了近5%的CPU时间.在许多地方,我从64MB字符缓冲区制作C++ std :: string对象.问题是,缓冲区在程序运行期间永远不会改变.我对std::string(const char *buf,size_t buflen)调用的分析是正在复制字符串,因为缓冲区可能在字符串生成后发生变化.这不是问题所在.有没有解决这个问题的方法?
编辑:我正在使用二进制数据,所以我不能只是传递char *s.此外,由于总是扫描NULL,我会有很大的开销,这std::string避免了.
我想在两个进程本地/网络之间共享/传输数据.通用IPC机制共享内存和消息队列可用于传输数据.但这些机制涉及多个副本.
我遇到了零复制机制,这减少了CPU上的复制开销.Linux支持使用sendfile和splice.这些API不在POSIX中.如何仅使用POSIX API实现零拷贝?
我正在使用期货,tokio,hyper和serde_json来请求和反序列化我需要保留的一些数据,直到我的下一个请求.我最初的想法是创建一个包含hyper::Chunk从中借用的反序列化数据的结构Chunk,但却无法使生命周期正确.我尝试使用租赁箱,但我也无法使用它.也许我'buffer在宣布缓冲区之前使用了生命周期Vec,但也许我搞砸了别的东西:
#[rental]
pub struct ChunkJson<T: serde::de::Deserialize<'buffer>> {
buffer: Vec<u8>,
json: T
}
Run Code Online (Sandbox Code Playgroud)
有没有办法让生命时间正确,或者我应该使用DeserializeOwned并放弃零拷贝?
有关更多上下文,以下代码可以正常工作(定期从两个URL反序列化JSON,保留结果,以便我们可以对它们执行某些操作).我想改变我的X和Y类型Cow<'a, str>用于他们的领域,从改变DeserializeOwned为Deserialize<'a>.为此,我需要存储已经反序列化的切片,但我不知道如何做到这一点.我正在寻找使用Serde的零拷贝反序列化并保留结果的示例,或者用于重构我的代码的一些想法.
#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
extern crate futures;
extern crate tokio_core;
extern crate tokio_periodic;
extern crate hyper;
use std::collections::HashMap;
use std::error::Error;
use futures::future;
use futures::Future;
use futures::stream::Stream;
use hyper::Client;
fn stream_json<'a, T: serde::de::DeserializeOwned + Send + 'a>
(handle: …Run Code Online (Sandbox Code Playgroud) 我想通过 Spring-OpenFeign 从服务器下载一个文件并将其保存在本地目录中,并使用零拷贝。
简单的下载方法如下:
import org.apache.commons.io.FileUtils
@GetMapping("/api/v1/files")
ResponseEntity<byte[]> getFile(@RequestParam(value = "key") String key) {
ResponseEntity<byte[]> resp = getFile("filename.txt")
File fs = new File("/opt/test")
FileUtils.write(file, resp.getBody())
}
Run Code Online (Sandbox Code Playgroud)
在这段代码中,数据流将是这样的 feign Internal Stream -> Buffer -> ByteArray -> Buffer -> File
如何高效快速地下载和保存文件内存?
zero-copy ×10
linux ×4
java ×2
linux-kernel ×2
sockets ×2
splice ×2
c ×1
c++ ×1
feign ×1
framebuffer ×1
nio ×1
opencl ×1
performance ×1
pipe ×1
posix ×1
rust ×1
serde ×1
spring-boot ×1
stdstring ×1
udp ×1