概述
mmap
是用于内存映射的函数,它是一种在用户空间的程序和一个或多个问价或其他对象之间创建直接的内存访问接口的方法。通过内存映射,操作系统将一个文件或者其他对象映射到进程的地址空间中,使得文件的内容可以直接作为进程内存的一部分来读写。
好处
- 内存映射允许你以字节为单位来访问文件,这使得对模型权重等二进制数据的随机访问变得更加简单和直观。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26// 大模型文件的路径
char *filename = "large_model.bin";
// 打开文件,只读方式
int fd = open(filename, O_RDONLY);
if (fd == -1) {
perror("open");
exit(EXIT_FAILURE);
}
// 映射文件到内存
void *mapped_address;
mapped_address = mmap(NULL, filesize, PROT_READ, MAP_PRIVATE, fd, 0);
if (mapped_address == MAP_FAILED) {
perror("mmap");
exit(EXIT_FAILURE);
}
// 访问权重文件的第32个字节开始的数据,是不是很方便,像访问内存一样访问文件
*(mapped_address + 32)
// 使用映射的内存
// 假设我们想读取文件中的前10个字节
char first_ten_bytes[11]; // +1 for null terminator
strncpy(first_ten_bytes, mapped_address, 10);
first_ten_bytes[10] = '\0'; // Ensure null termination
printf("First ten bytes: %s\n", first_ten_bytes); - 对于非常大的模型文件,可能无法一次性全部加载到内存中。内存映射允许按需逐页加载部分数据,这样可以在优先的内存中处理更大的模型,大模型动辄几十个G。也就是说,内存映射并不是将打开文件中所有字节一次性读到内存中,而是根据访问的位置进行分块读取。 换句话说,使用 mmap() 后,程序可以直接在内存地址上进行操作,而不需要关心文件读写的位置。这意味着可以用简单的指针操作来替代复杂的文件偏移量管理。
- 减少数据拷贝次数:传统的文件读取操作需要将数据从内核缓冲区复制到用户空间的缓冲区,而内存映射则避免了这种复制,通过将打开的文件直接映射到进程地址空间的办法提高了数据访问速度。
映射权重文件到内存空间中
- 用系统调用接口
open
打开文件地址,获取打开的文件描述符fd
,open
系统调用会打开pathname
参数指定的文件,如果文件路径存在且被成功执行后就会返回文件描述符,它是一个非负的整数值。
1 | int open(const char *pathname, int flags); |
从功能上来说,
open
系统调用创建了一个新的文件描述项,也就是在一个系统全局的已打开文件表中添加一条记录。获取文件的大小,用于将整个权重文件范围都映射到内存空间中;
1 | FILE* file = fopen(model_path_.data(), "rb"); |
首先是使用C库的fopen
函数打开权重文件,获取文件结构体指针同时我们将文件的读写位置移动到末尾SEEK_END
,随后再读取文件当前的读写位置就可以获得整个文件的大小。
简单来说,就是打开一个文件,并将文件的读写地址移动到结尾,随后再获取结尾地址的位置就可以获取整个模型文件的大小。
- 读取模型文件起始位置的配置信息,也就是前文模型导出时候存入的配置信息;