对比fwrite、mmap、DirectIO 的内存、性能开销,剖析 Page Cache

背景

如上图所示:应用程序写文件有三种形式。

  1. fwrite : 应用程序 -> fwrite(Buffered IO) -> File System -> Page Cache -> Block IO Layer -> Device & Disk etc.
  2. mmap : 应用程序 -> mmap -> Page Cache -> Block IO Layer -> Device & Disk etc.
  3. Direct IO : 应用程序 -> Block IO Layer -> Device & Disk etc.

Direct IO优点:使用Direct I/O时,数据直接在应用程序和存储设备之间传输,绕过了操作系统的缓存机制。这样可以提高数据传输的效率

Direct IO缺点:其中一个限制是,Direct I/O要求数据缓冲区必须对齐到存储设备的块大小,否则会导致性能下降或者出现错误。

为了解决这个问题,通常需要使用一块内存作为中转缓冲区,将应用程序的数据先复制到中转缓冲区中,然后再使用Direct I/O将数据从中转缓冲区传输到存储设备。这样可以保证数据缓冲区对齐,并且可以避免在应用程序和存储设备之间频繁地复制数据。

这块中转缓冲区通常被称为“heap”,因为它是从堆内存中分配的。在使用Direct I/O时,heap的大小通常需要根据存储设备的块大小和应用程序的需求来确定。

测试用例

#include <iostream>
#include <fstream>
#include <chrono>
#include <cstring>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>using namespace std;const int FILE_SIZE = 1024 * 1024 * 1024; // 1GB
const int BLOCK_SIZE = 4096; // 4KBvoid test_fwrite() {ofstream ofs("test_fwrite.bin", ios::binary);char* buffer = new char[BLOCK_SIZE];auto start = chrono::high_resolution_clock::now();for (int i = 0; i < FILE_SIZE / BLOCK_SIZE; i++) {ofs.write(buffer, BLOCK_SIZE);}auto end = chrono::high_resolution_clock::now();cout << "fwrite time: " << chrono::duration_cast<chrono::milliseconds>(end - start).count() << "ms" << endl;delete[] buffer;
}void test_mmap() {int fd = open("test_mmap.bin", O_RDWR | O_CREAT, 0666);ftruncate(fd, FILE_SIZE);char* buffer = (char*) mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);auto start = chrono::high_resolution_clock::now();memset(buffer, 0, FILE_SIZE);auto end = chrono::high_resolution_clock::now();cout << "mmap time: " << chrono::duration_cast<chrono::milliseconds>(end - start).count() << "ms" << endl;munmap(buffer, FILE_SIZE);close(fd);
}void test_directio() {int fd = open("test_directio.bin", O_RDWR | O_CREAT | O_DIRECT, 0666);char* buffer = new char[BLOCK_SIZE];char* heap_buffer = new char[FILE_SIZE];auto start = chrono::high_resolution_clock::now();for (int i = 0; i < FILE_SIZE / BLOCK_SIZE; i++) {write(fd, buffer, BLOCK_SIZE);}auto end = chrono::high_resolution_clock::now();cout << "directIO time: " << chrono::duration_cast<chrono::milliseconds>(end - start).count() << "ms" << endl;delete[] buffer;delete[] heap_buffer;close(fd);
}int main() {test_fwrite();test_mmap();test_directio();return 0;
}
  1. 这个 demo 分别测试了 fwrite、mmap 和 DirectIO 的性能,其中 fwrite 和 mmap 都是直接写入文件,而 DirectIO 则需要使用 heap_buffer 作为内存中转。
  2. 在测试 DirectIO 时,我们使用了 O_DIRECT 标志来打开文件,这会禁用文件系统缓存,从而确保数据直接从内存写入磁盘。但是,由于 DirectIO 要求内存对齐,因此我们需要使用 heap_buffer 来确保内存对齐。这个 heap_buffer 的大小与文件大小相同。

运行这个 demo,我们可以得到以下输出:

wj@wj:~/WORK/Learning/Learning/DirectIO$ ./test.out 
fwrite time: 649ms
mmap time: 267ms
directIO time: 1191ms
wj@wj:~/WORK/Learning/Learning/DirectIO$

实验结论

从输出可以看出,mmap 的性能远远优于 fwrite,而 DirectIO 的性能比 fwrite 差一些。

这表明,mmap 可以映射文件到内存中,从而避免了数据的复制,因此性能更高。

而 DirectIO 需要使用 heap_buffer 作为内存中转,因此存在一定的内存开销,目前来看这个开销还是非常大的,不能忽略不计。

DirectIO的内存开销

DirectIO 的内存开销主要取决于两个因素:每个请求的大小和并发请求数量。由于 DirectIO 使用 heap_buffer 作为内存中转,因此每个请求都需要分配一定大小的内存。并且,由于 DirectIO 是异步的,因此在高并发情况下,可能会有大量的请求同时进行,从而导致内存开销增加。

具体来说,假设每个请求的大小为 `request_size`,并发请求数量为 `concurrency`,则 DirectIO 的内存开销可以估算为: ``` memory_overhead = request_size * concurrency ```

例如,如果每个请求的大小为 4KB,同时有 1000 个请求在进行,则 DirectIO 的内存开销大约为 4MB。

需要注意的是,这只是一个粗略的估算,实际的内存开销可能会受到其他因素的影响,例如操作系统的内存管理策略、硬件配置等。

实验demo

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>#define BUF_SIZE 4096int main(int argc, char *argv[]) {int fd;char *buf;struct stat st;off_t offset = 0;ssize_t nread;if (argc != 2) {fprintf(stderr, "Usage: %s <file>\n", argv[0]);exit(EXIT_FAILURE);}//使用 `open` 函数打开一个文件,并指定 `O_DIRECT` 标志以启用 DirectIOfd = open(argv[1], O_RDONLY | O_DIRECT);if (fd == -1) {perror("open");exit(EXIT_FAILURE);}//使用 `fstat` 函数获取文件的信息,包括块大小if (fstat(fd, &st) == -1) {perror("fstat");exit(EXIT_FAILURE);}//使用 `aligned_alloc` 函数分配一个对齐到块大小的缓冲区buf = (char*)aligned_alloc(st.st_blksize, BUF_SIZE);if (buf == NULL) {perror("aligned_alloc");exit(EXIT_FAILURE);}//使用 `pread` 函数读取文件,并在读取过程中计算内存开销while ((nread = pread(fd, buf, BUF_SIZE, offset)) > 0) {offset += nread;}if (nread == -1) {perror("pread");exit(EXIT_FAILURE);}printf("offset : %ld\n",offset);free(buf);close(fd);return 0;
}

wj@wj:~/WORK/Learning/Learning/DirectIO$ g++ directIO.cpp -o directIO.out
wj@wj:~/WORK/Learning/Learning/DirectIO$ ./directIO.out test_mmap.bin
offset : 1073741824
wj@wj:~/WORK/Learning/Learning/DirectIO$ 

注意,该程序只是一个简单的示例,实际的内存开销可能会更复杂,需要根据具体情况进行测试和分析。

fwrite 和 mmap 都需要经过 Page Cache,再到 Block IO Layer,那么Linux系统为什么要这样设计呢?

什么是Page Cache?

思考一个问题:Page Cache 到底是属于内核空间还是属于用户空间呢?

参考:如何用数据观测Page Cache?_如何查看page cache-CSDN博客

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

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

相关文章

【LLM】vLLM部署与int8量化

Acceleration & Quantization vLLM vLLM是一个开源的大型语言模型&#xff08;LLM&#xff09;推理和服务库&#xff0c;它通过一个名为PagedAttention的新型注意力算法来解决传统LLM在生产环境中部署时所遇到的高内存消耗和计算成本的挑战。PagedAttention算法能有效管理…

虾皮商品标题:如何创建有效的虾皮商品标题

虾皮&#xff08;Shopee&#xff09;平台是一个非常受欢迎的电商平台&#xff0c;为卖家提供了一个广阔的销售渠道。在虾皮上&#xff0c;一个有效的商品标题是吸引潜在买家注意力的关键元素之一。一个好的商品标题能够吸引更多的点击和浏览量&#xff0c;从而提高销售机会。下…

什么是API网关代理?

带有API网关的代理服务显着增强了用户体验和性能。特别是对于那些使用需要频繁创建和轮换代理的工具的人来说&#xff0c;使用 API 可以节省大量时间并提高效率。 了解API API&#xff08;即应用程序编程接口&#xff09;充当服务提供商和用户之间的连接网关。通过 API 连接&a…

【PostgreSQL】在DBeaver中实现序列、函数、视图、触发器设计

【PostgreSQL】在DBeaver中实现序列、函数、触发器、视图设计 基本配置一、序列1.1、序列使用1.1.1、设置字段为主键&#xff0c;数据类型默认整型1.1.2、自定义序列&#xff0c;数据类型自定义 1.2、序列延申1.2.1、理论1.2.2、测试1.2.3、小结 二、函数2.1、SQL直接创建2.1.1…

Python教程37:使用turtle画一个戴帽子的皮卡丘

---------------turtle源码集合--------------- Python教程36&#xff1a;海龟画图turtle写春联 Python源码35&#xff1a;海龟画图turtle画中国结 Python源码31&#xff1a;海龟画图turtle画七道彩虹 Python源码30&#xff1a;海龟画图turtle画紫色的小熊 Python源码29&a…

基于宝塔搭建Discuz!论坛

一、安装宝塔 我是在我的虚拟机上安装图的宝塔 虚拟机版本&#xff1a;Ubuntu 18.04 wget -O install.sh https://download.bt.cn/install/install-ubuntu_6.0.sh && sudo bash install.sh 6dca892c安装完成之后在浏览器输入你的地址 https://你的域名&#xff08;或…

基于JavaWeb+BS架构+SpringBoot+Vue校园一卡通系统的设计和实现

基于JavaWebBS架构SpringBootVue校园一卡通系统的设计和实现 文末获取源码Lun文目录前言主要技术系统设计功能截图订阅经典源码专栏Java项目精品实战案例《500套》 源码获取 文末获取源码 Lun文目录 第一章 概述 4 1.1 研究背景 4 1.2研究目的及意义 4 1.3国内外发展现状 4 1…

网络安全之你的浏览器记录真的安全吗?

密码是每个人最私密的东西&#xff0c;轻易是不会展示给他人的&#xff0c;那么我如何能知道你电脑上浏览器里保存的密码呢&#xff1f;浏览器是大家在网上冲浪最常用的软件&#xff0c;在登录一些网站填写账号密码后&#xff0c;浏览器为了方便大家使用&#xff0c;会提示是否…

unity小程序websocket:nginx配置https (wss)转http (ws)及其他问题解决

目录 前言 实际运用场景 处理流程如下 nginx配置ssl和wss 配置过程中遇到的问题 1、无法连接服务器 2、通过IP可以访问&#xff0c;域名却不行 问题描述 解决 3、如何判断该域名是否备案了 前言 为了服务器网络的通用性&#xff0c;我们在实现移动端的游戏转微信小程序…

Python教程38:使用turtle画动态粒子爱心+文字爱心

Turtle库是Python语言中的一个标准库&#xff0c;它提供了一种有趣的方式来介绍编程和图形绘制的基本概念。Turtle库使用一个虚拟的“海龟”来绘制图形。你可以控制海龟的方向、速度和位置&#xff0c;通过向前移动、向左转或向右转等命令来绘制线条、圆弧多边形等图形。 -----…

Java 反射(一)

反射 1.反射的介绍 1.反射机制允话程序在执行期间借助于Refelction API取得任何类的信息&#xff08;比如成员变量&#xff0c;构造器&#xff0c;成员方法等&#xff09;并能操作对象的属性及方法&#xff0c;反射在设计模式和框架底层都会用到 2.加载完类之后&#xff0c;在…

API调试怎么做?Apipost快速上手

前言 Apipost是一款支持 RESTful API、SOAP API、GraphQL API等多种API类型&#xff0c;支持 HTTPS、WebSocket、gRPC多种通信协议的API调试工具。除此之外&#xff0c;Apipost 还提供了自动化测试、团队协作、等多种功能。这些丰富的功能简化了工作流程&#xff0c;提高了研发…

CodeWave智能开发平台--03--目标:应用创建--08联系人管理

摘要 本文是网易数帆CodeWave智能开发平台系列的第11篇&#xff0c;主要介绍了基于CodeWave平台文档的新手入门进行学习&#xff0c;实现一个完整的应用&#xff0c;本文主要完成08联系人管理 CodeWave智能开发平台的11次接触 CodeWave参考资源 网易数帆CodeWave开发者社区…

【VRTK】【Unity】【VR开发】Linear Drives

课程配套学习项目源码资源下载 https://download.csdn.net/download/weixin_41697242/88485426?spm=1001.2014.3001.5503 【概述】 前面一篇讨论了角度运动机制,本篇讨论线性运动机制。和角度运动机制类似,线性运动机制提供了更为仿真的互动机制。也分为基于物理的和不基于…

doris部署

doris-2.0.1.1部署安装 一、下载doris安装包二、解压到/data下&#xff0c;修改名称三、修改fe配置文件四、启动doris-fe五、验证doris-fe六、修改be配置文件七、启动doris-be八、mysql中连接be&#xff0c;在Doris中添加后端节点九、设置密码 一、下载doris安装包 wget https…

ArrayBlockingQueue原理探究

类图结构 同样&#xff0c;为了能从全局一览ArrayBlockingQueue的内部构造&#xff0c;先来看它的类图。 ArrayBlockingQueue的内部有一个数组items,用来存放队列元素&#xff0c;putindex变量表示入队元素下标&#xff0c;takelndex是出队下标&#xff0c;count统计队列元素个…

雅意2.0:打造专为中文优化的300亿参数多语言模型

前言 雅意2.0&#xff0c;作为一款专注于中文语境的开源大型语言模型&#xff0c;其在多语言处理方面的能力尤为突出。该模型不仅具有300亿参数规模的庞大体量&#xff0c;还在多个关键领域取得了显著的技术突破。 Huggingface模型下载&#xff1a;https://huggingface.co/wen…

大前端nestjs入门教程系列(四):如何nestjs整合mysql数据库

经过前面的几篇文章&#xff0c;想必大家已经对nestjs有了基础的了解&#xff0c;那么这篇文章就带大家玩玩数据库&#xff0c;学会了这篇&#xff0c;就离大前端又进了一步 Nest与数据库无关&#xff0c;使你可以轻松地与任何 SQL 或 NoSQL 数据库集成。 根据你的喜好&#xf…

【面试高频算法解析】算法练习3 双指针

前言 本专栏旨在通过分类学习算法&#xff0c;使您能够牢固掌握不同算法的理论要点。通过策略性地练习精选的经典题目&#xff0c;帮助您深度理解每种算法&#xff0c;避免出现刷了很多算法题&#xff0c;还是一知半解的状态 专栏导航 二分查找回溯&#xff08;Backtracking&…

权威mcc mnc 列表网址

MCC-MNC.net 链接: MCC-MNC.net 这个网站提供的mcc mnc 比下面itu文档中更全。 itu.int 2023年发布的mcc mnc分配信息&#xff1a; 链接: PDF T-SP-E.212B-2023 若想获取最新的可以参考如下截图查询&#xff1a;