Java零拷贝技术实战

news2024/11/26 14:25:38

文章目录

  • 引入
  • 传统IO
  • 内存映射mmap
  • 文件描述符sendFile
  • 测试
  • 总结

引入

为什么要使用零拷贝技术?
传统写入数据需要4次拷贝,如下图:
在这里插入图片描述

传统IO

import java.io.*;
import java.net.Socket;

public class TranditionIOClient {

	private static final int PORT = 8888;
	private final static String FILE_NAME = "D:\\test.mp4";
	// 接收缓冲区大小
	private static final int BUFFER_SIZE = 1024;

	public static void main(String[] args) throws Exception {
		try (Socket socket = new Socket("localhost", PORT);
			 InputStream inputStream = new FileInputStream(FILE_NAME);
			 DataOutputStream dos = new DataOutputStream(socket.getOutputStream());) {
			byte[] buffer = new byte[BUFFER_SIZE];
			long readCount = 0;
			long total = 0;
			long startTime = System.currentTimeMillis();
			// 读取文件:从硬盘读取到内存,发生2次copy(DMA拷贝和CPU拷贝)
			while ((readCount = inputStream.read(buffer)) >= 0) {
				total += readCount;
				// 网络发送:从内存到网卡,发生2次copy(DMA拷贝和CPU拷贝)
				dos.write(buffer, 0, (int) readCount);
			}
			System.out.println("TranditionIOClient发送总字节数:" + total + ",耗时:" + (System.currentTimeMillis() - startTime) + " ms");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

内存映射mmap

在这里插入图片描述

import java.io.FileInputStream;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;

public class MmapClient {

	private static final int PORT = 8888;
	private final static String FILE_NAME = "D:\\test.mp4";

	public static void main(String[] args) throws Exception {
		try (SocketChannel socketChannel = SocketChannel.open();
			 FileChannel fileChannel = new FileInputStream(FILE_NAME).getChannel()) {
			socketChannel.connect(new InetSocketAddress("localhost", PORT));
			socketChannel.configureBlocking(true);
			long startTime = System.currentTimeMillis();
			// 获取文件大小
			long size = fileChannel.size();
			// 内存映射整个文件,发生3次copy(DMA拷贝和CPU拷贝)
			ByteBuffer buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, 0, size);
			// 发送文件
			while (buffer.hasRemaining()) {
				socketChannel.write(buffer);
			}
			System.out.println("MmapClient发送总字节数:" + size + ",耗时:" + (System.currentTimeMillis() - startTime) + " ms");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

文件描述符sendFile

在这里插入图片描述

import java.io.FileInputStream;
import java.net.InetSocketAddress;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;

public class SendFileClient {

	private static final int PORT = 8888;
	private final static String FILE_NAME = "D:\\test.mp4";

	public static void main(String[] args) throws Exception {
		try (SocketChannel socketChannel = SocketChannel.open();
			 FileChannel fileChannel = new FileInputStream(FILE_NAME).getChannel()) {
			socketChannel.connect(new InetSocketAddress("localhost", PORT));
			socketChannel.configureBlocking(true);
			long startTime = System.currentTimeMillis();
			long position = 0;
			// 8MB,与系统缓冲区大小匹配或略小以避免问题
			long chunkSize = 8 * 1024 * 1024;
			long size = fileChannel.size();
			while (position < size) {
				long bytesRemaining = size - position;
				// 确保最后一次传输不会超过文件大小
				long count = Math.min(bytesRemaining, chunkSize);
				// transferTo⽅法⽤到了零拷⻉,底层是sendfile,发生2次copy(DMA拷贝)
				long transferCount = fileChannel.transferTo(position, count, socketChannel);
				if (transferCount == 0) {
					// 如果一次传输没有发生,可能需要检查连接是否仍然活跃或处理其他错误情况
					break;
				}
				position += transferCount;
			}
			System.out.println("SendFileClient发送总字节数:" + size + ",耗时:" + (System.currentTimeMillis() - startTime) + " ms");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

如果发送的文件不大于8M,则可以简单写,如下:

import java.io.FileInputStream;
import java.net.InetSocketAddress;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;

public class SendFileClient {

	private static final int PORT = 8888;
	private final static String FILE_NAME = "D:\\test.mp4";

	public static void main(String[] args) throws Exception {
		try (SocketChannel socketChannel = SocketChannel.open();
			 FileChannel fileChannel = new FileInputStream(FILE_NAME).getChannel()) {
			socketChannel.connect(new InetSocketAddress("localhost", PORT));
			socketChannel.configureBlocking(true);
			long startTime = System.currentTimeMillis();
			// transferTo⽅法⽤到了零拷⻉,底层是sendfile,发生2次copy(DMA拷贝)
			long transferCount = fileChannel.transferTo(0, fileChannel.size(), socketChannel);
			System.out.println("SendFileClient发送总字节数:" + transferCount + ",耗时:" + (System.currentTimeMillis() - startTime) + " ms");
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

测试

服务端代码如下:

import java.io.DataInputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {

	private static final int PORT = 8888;
	// 接收缓冲区大小
	private static final int BUFFER_SIZE = 1024;

	public static void main(String[] args) throws Exception {
		try (ServerSocket ss = new ServerSocket(PORT);) {
			while (true) {
				try (Socket s = ss.accept();
					 DataInputStream dis = new DataInputStream(s.getInputStream());) {
					int byteCount = 0;
					byte[] bytes = new byte[BUFFER_SIZE];
					while (true) {
						int readCount = dis.read(bytes, 0, BUFFER_SIZE);
						if (readCount == -1) {
							break;
						}
						byteCount = byteCount + readCount;
					}
					System.out.println("服务端接受字节数:" + byteCount + "字节");
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

我们都使用了test.mp4进行测试,文件大小500M,测试结果如下:

TranditionIOClient发送总字节数:524288000,耗时:9590 ms
MmapClient发送总字节数:524288000,耗时:1182 ms
SendFileClient发送总字节数:524288000,耗时:983 ms

总结

零拷贝并不是不需要拷贝,而是指计算机执行操作时,不需要将数据从内存复制到应用程序

效率高到低:sendFile>mmap>传统IO

明明传了500M的文件,但实际读出来8M?代码如下:

try (SocketChannel socketChannel = SocketChannel.open();
	 FileChannel fileChannel = new FileInputStream(FILE_NAME).getChannel()) {
	socketChannel.connect(new InetSocketAddress("localhost", PORT));
	socketChannel.configureBlocking(true);
	long startTime = System.currentTimeMillis();
	// transferTo⽅法⽤到了零拷⻉,底层是sendfile,发生2次copy(DMA拷贝)
	long transferCount = fileChannel.transferTo(0, fileChannel.size(), socketChannel);
	System.out.println("SendFileClient发送总字节数:" + transferCount + ",耗时:" + (System.currentTimeMillis() - startTime) + " ms");
} catch (Exception e) {
	e.printStackTrace();
}

// 输出结果:SendFileClient发送总字节数:8388608,耗时:15 ms

原因:由于操作系统的默认socket缓冲区大小限制所导致的。当使用transferTo进行大文件传输时,如果文件大小超过了操作系统为socket分配的缓冲区大小,那么transferTo可能在达到这个限制后停止,因为它试图一次性将数据从文件通道转移到socket通道,但缓冲区不足以容纳整个文件内容。

解决:分批次进行文件传输

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1652164.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

​「Python绘图」绘制太极图

python 绘制太极 一、预期结果 二、核心代码 import turtlepen turtle.Turtle()print("开始绘制太极")radius 100 pen.color("black", "black") pen.begin_fill() pen.circle(radius/2, 180) pen.circle(radius, 180) pen.left(180) pen.circ…

小项目“谈笑风生”测试报告

文章目录 一、项目介绍1.1项目背景1.2功能介绍 二、测试环境三、测试执行过程3.1功能测试3.1.1登录页面测试3.1.2注册页面测试3.1.3主页面测试 3.2界面自动化测试3.2.1登录模块测试3.2.2注册模块测试3.2.3展示各种信息模块测试3.2.34聊天消息传送模块测试 四、测试结论与建议 一…

8.Redis

Redis是一个基于内存的key-value结构数据库&#xff0c;而mysql是通过数据文件&#xff0c;以二维表的方式存储在磁盘上。 优点 基于内存存储&#xff0c;读写性能高适合存储热点数据企业应用广泛 Redis目录文件 启动命令 redis-server.exe redis.windows.conf 退出命令 c…

☺☺☺☺☺☺☺栈的应用习题:有效的括号☺☺☺☺☺☺☺

目录 一解题思路&#xff1a; 二对解答代码分析&#xff1a; 三解答代码展示&#xff1a; 即浅学栈的创建后&#xff0c;可以简单利用其性质&#xff08;先进后出&#xff0c;后进先出&#xff09;来完成对一些题目的解答 如&#xff1a; 一解题思路&#xff1a; 这里我们可…

图数据库 之 Neo4j 与 AI 大模型的结合绘制知识图谱

引言 随着信息时代的到来&#xff0c;海量的文本数据成为了我们获取知识的重要来源。然而&#xff0c;如何从这些文本数据中提取出有用的信息&#xff0c;并将其以可视化的方式展示出来&#xff0c;一直是一个具有挑战性的问题。近年来&#xff0c;随着人工智能技术的发展&…

2024年开抖音小店需要多少钱?你真的知道吗?最新入驻条件及费用

大家好&#xff0c;我是电商花花。 现在仍然有很多想开抖店&#xff0c;想做抖音小店&#xff0c;但是很多人都不知道投资一家抖音小店需要多少钱&#xff0c;今天花花就给大家讲一下做一家抖音小店需要投入多少资金&#xff0c;以及具体投入到哪些方面。 我们就说一下个体店…

layui 数据表格 自动定位新增行位置

由于数据表格新增行后没有到新增到当前位置 继续增加的需求&#xff1a; 因为自己是新增行后到最后一行的 所以 就定位到最后一行 并且 高亮 高亮颜色浅 可自行更改 整理了一下 可根据 情况 修改 // 初始化滚动条位置变量 let tableScroll {scrollTob: 0,scrollLeft: 0,…

Shuffle Cards (STL rope平衡树库)

本题链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 题目&#xff1a; 样例1&#xff1a; 输入 5 1 2 3 输出 2 3 4 1 5 样例2&#xff1a; 输入 5 2 2 3 2 3 输出 3 4 1 2 5 样例3&#xff1a; 输入 5 3 2 3 1 4 2 4输出 3 4 1 5 2 思路&#xff1a; 这道题&…

windows端口复用

1. 概述 使用 HTTP.sys 中的 Net.tcp Port Sharing 服务&#xff0c;配合 WinRM 实现端口复用。 优点&#xff1a; HTTP.sys 为 windows 原生机制&#xff0c; WinRM 为 windows 自带功能&#xff0c;动作较小&#xff0c;不易触发主 动防御。 需要管理员权限。 2. 原理 (…

STM32 VS Code 扩展用户指南

系列文章目录 前言 一、视频教程快速入门 通过我们简单易学的视频教程&#xff0c;快速掌握新版本的使用方法&#xff1a; 二、功能描述 2.1 创建/导入项目 STM32 VS Code 扩展提供两种不同的项目创建选项&#xff1a; STM32CubeMX 项目&#xff1a; 这是一个依靠 CMake 作为…

线下预约线下消费万能DIY小程序源码系统 带完整安装代码包

在数字化时代&#xff0c;线上线下融合的消费模式已成为新趋势。为满足广大商家对于个性化预约系统的需求&#xff0c;我们隆重推出了“线下预约线下消费万能DIY小程序源码系统”。该系统不仅具备完整的安装代码包&#xff0c;还提供了丰富的定制功能&#xff0c;助力商家轻松搭…

【大比武01】AIGC赋能档案文创设计的尝试

关注我们 - 数字罗塞塔计划 - 随着科技的飞速发展&#xff0c;AI技术逐渐深入我们的工作与生活&#xff0c;特别是随着2022年底ChatGPT的横空出世&#xff0c;AIGC&#xff08;Artificial Intelligence Generated Content&#xff0c;内容生成式人工智能&#xff09;开始登上历…

Covalent引入五个新网络运营商,提升去中心化特性和数据安全性

为了进一步扩大运营商基础以并践行去中心化网络基础设施的宗旨&#xff0c;Covalent Network&#xff08;CQT&#xff09;在网络中引入了五个新的区块样本生产者&#xff08;BSPs&#xff09;角色。该举措不仅重申了 Covalent Network&#xff08;CQT&#xff09;对社区驱动协议…

20基础解决-buuctf-1.RSA1

2.buuctf-1.RSA1 RSATool2v17工具的使用 打开RSA Tool 1.Number Base 设置为十进制 2.注意&#xff1a;Public Exponent这里要使用16进制的数&#xff0c;如果公钥e17的话&#xff0c;就应该填入十六进制的11 3.给出p,q,e的话直接填入&#xff0c;再点击Calc.D,获得d 4.给出…

【第6节课笔记】LagentAgentLego

Lagent 最中间部分的是LLM&#xff0c;即为大语言模型模块&#xff0c;他可以思考planning和调用什么action&#xff0c;再将其转发给动作执行器action executer执行。 支持的工具如下&#xff1a; Arxiv 搜索 Bing 地图 Google 学术搜索 Google 搜索 交互式 IPython 解释器 IP…

C++ | Leetcode C++题解之第74题搜索二维矩阵

题目&#xff1a; 题解&#xff1a; class Solution { public:bool searchMatrix(vector<vector<int>>& matrix, int target) {int m matrix.size(), n matrix[0].size();int low 0, high m * n - 1;while (low < high) {int mid (high - low) / 2 l…

安卓玩机工具----一键备份手机分区 防止全檫除或者格机导致安全数据分区丢失

工具说明; 目前玩机root后有很多格机脚本。模块等等误刷会导致基带信号等等问题&#xff0c;在前面的博文中我有介绍过备份主要数据分区的重要性 。其实对于不了解root和不安装有些模块 外挂等等需要的友友不建议对手机进行root。root后对于手机安全性会有所降低。对于玩家来说…

信创 | 中国信创建设的历程与未来展望!

中国信创建设的历程与未来展望可以从多个维度进行分析。首先&#xff0c;从发展历程来看&#xff0c;中国信创产业的发展可以分为几个阶段。根据不同的资料来源&#xff0c;这些阶段的划分有所不同&#xff0c;但大体上可以概括为起步、试点、快速发展等关键时期。 起步阶段&am…

医疗器械软件如何生成符合FDA要求的SBOM文件?

在当今这个高度互联的世界&#xff0c;医疗器械的安全性和可靠性对于保障公众健康至关重要。FDA作为美国食品和药物的主要监管机构&#xff0c;长期以来一直致力于确保医疗器械的安全性和有效性。 2023年9月27日&#xff0c;美国食品和药品监督管理局&#xff08;后续简称FDA&…

文本批量操作实例:如何实现文本批量拆分?实用方法与技巧分享

在文本处理和数据分析的过程中&#xff0c;我们经常会遇到需要批量拆分大量文本数据的情况。文本批量拆分能够大大提高工作效率&#xff0c;减少重复劳动。下面&#xff0c;我们将通过一个实例来介绍如何实现文本批量拆分&#xff0c;并分享一些实用方法与技巧。 一、实例背景 …