Linux C读写超大文件效率问题

时间:2021-09-25 20:11:13
各位高手帮忙看看,我这有几十G级别的比如60G文件需要读写,生成一些子文件,需要对原来文件的每一行进行解析;之前用fgets每一行的方法,客户对时间不是很满意;后来改用fork多个子进程,分段读取源文件,再生成不同的子文件,最后再把子文件合在一起,效果也不是很理想,逻辑不复杂,估计主要是IO的瓶颈;内存映射空间不够大吧,而且还要一行行的解析出来。
各位亲们不知有没有更好的办法啊?

27 个解决方案

#1


内存映射不一定非要映射整个文件进来的,映射一段处理完再换一段。

#2


用二进制模式打开,用fread()读取,读取到内存中再处理,而不是一行一行地读取文件。
这样会不会快些?游戏编程都是推荐这样操作的。
---------
不懂海量数据处理,纯路过……

#3


引用 1 楼 qq120848369 的回复:
内存映射不一定非要映射整个文件进来的,映射一段处理完再换一段。

那不还得一行行的截出来读啊?而且临界的地方如果不是正好一行的话,也很麻烦啊

#4


void mmap(void *addr,size_t len,int prot,in flag,int fd,off_t offset)


这是函数原型,offset是文件内的字节偏移量,len是映射多少字节.

问题就是要区分行边界.

第一步,映射文件开始的2G进来,找'\r'啥的,就可以得到一行,具体的你慢慢研究...如果找不到'\r'了,那后边的字节不能白瞎,你得通过计算把这些字节留下来作为下面2G的开始位置.

如此简单的问题,不解释了.

#5


你的问题是I/O瓶颈,这个与海量数据处理没啥关系.

如果给预处理时间的话,那可以读大文件哈希到若干小文件,每个小文件可以直接映射到内存处理,这样的话利用两个线程并发遍历所有的文件,效率会高不少了,因为I/O问题已经不存在了 =,=

#6


引用 5 楼 qq120848369 的回复:
你的问题是I/O瓶颈,这个与海量数据处理没啥关系.

如果给预处理时间的话,那可以读大文件哈希到若干小文件,每个小文件可以直接映射到内存处理,这样的话利用两个线程并发遍历所有的文件,效率会高不少了,因为I/O问题已经不存在了 =,=

哦,我再试试,谢谢啊

#7


引用 5 楼 qq120848369 的回复:
你的问题是I/O瓶颈,这个与海量数据处理没啥关系.

如果给预处理时间的话,那可以读大文件哈希到若干小文件,每个小文件可以直接映射到内存处理,这样的话利用两个线程并发遍历所有的文件,效率会高不少了,因为I/O问题已经不存在了 =,=


我想问下,我碰到过一个问题,对一个文件做MD5校验,当时我是用malloc申请的内存一次性读进来所有数据,但是对超大文件怎么做MD5校验啊,分割了不能做校验了吧。

#8


引用 7 楼 mangsong 的回复:
引用 5 楼 qq120848369 的回复:
你的问题是I/O瓶颈,这个与海量数据处理没啥关系.

如果给预处理时间的话,那可以读大文件哈希到若干小文件,每个小文件可以直接映射到内存处理,这样的话利用两个线程并发遍历所有的文件,效率会高不少了,因为I/O问题已经不存在了 =,=


我想问下,我碰到过一个问题,对一个文件做MD5校验,当时我是用malloc申请的内存一次性读进来所有……

md5支持分割校验,前提是你得有基础算法

md5基础算法一般是三个函数
init
process
done
其中process可以执行多次

#9


以前碰到过类似的问题,IO是很大的瓶颈。写文件的时候,可以设置个较大的缓存,减少写的次数

#10


引用 9 楼 aaajj 的回复:
以前碰到过类似的问题,IO是很大的瓶颈。写文件的时候,可以设置个较大的缓存,减少写的次数

这个我也有加啊,一般8K左右最好吧

#11


别急着优化程序,先算算现在的 I/O 速度是多少,要是已经达到了硬盘的速度上限程序再怎么优化都是白搭。

顺序读取大文件,内存映射其实没有直接读取快,不要碰到大文件就想着内存映射。

#12


我写过一个文件处理的程序,是顺序读取的。我不懂内存映射。只是自己把小段小段的读取,改成大段大段读取。减少文件读取次数,还是挺有效的。边界的问题,想办法处理下就好了。

不过,我也很想知道有什么经典的解决方法。老是做重复发明*的事情,浪费时间。还发明得比别人差,丢脸。

#13


有一点现在都没弄明白,当时想通过setvbuf修改缓冲大小来提高效率,没有看到任何效果。后来改成每次读取大段内容却有效。不知道问题出在哪里。

#14


不懂,帮顶!

#15


引用 11 楼 hpsmouse 的回复:
别急着优化程序,先算算现在的 I/O 速度是多少,要是已经达到了硬盘的速度上限程序再怎么优化都是白搭。

顺序读取大文件,内存映射其实没有直接读取快,不要碰到大文件就想着内存映射。


硬件问题硬件解决

#16


给你个建议,用空间换时间。

你看看你地文本内容长度最大是多少,建议设定一个值:256或者128

然后如果每行不足256个字节,补足256字节。进行文件定位的时候直接seek到指定行。

如果内容不是简单的就知道行数,那么建议再创建一个外部索引,进行读写文件的时候只是先查看索引然后在进行读写文件指定位置。

如果你的需求更复杂一点,需要在中间插入和删除行。建议把大文件拆分成小文件。然后外部任然通过一个索引文件进行索引。文件大小或者分片与否对上层不可见。

#17


你要知道你地程序读取的IO大部分都是废IO,

那就说明程序的优化空间或者架构优化空间还非常非常大。

#18


引用 11 楼 hpsmouse 的回复:
别急着优化程序,先算算现在的 I/O 速度是多少,要是已经达到了硬盘的速度上限程序再怎么优化都是白搭。

顺序读取大文件,内存映射其实没有直接读取快,不要碰到大文件就想着内存映射。

至少对于Windows,内存映射的机制类似于异步IO,省去了同步等待ReadFile和WriteFile的时间,我做加密算法和Hash算法,处理大文件还是文件映射快

#19


引用 16 楼 hgxxx007 的回复:
给你个建议,用空间换时间。

你看看你地文本内容长度最大是多少,建议设定一个值:256或者128

然后如果每行不足256个字节,补足256字节。进行文件定位的时候直接seek到指定行。

如果内容不是简单的就知道行数,那么建议再创建一个外部索引,进行读写文件的时候只是先查看索引然后在进行读写文件指定位置。

如果你的需求更复杂一点,需要在中间插入和删除行。建议把大文件拆分成小文……

现在是有把文件分成几段,分子进程去读写,生成几个临时文件,再合并,直接用系统命令CAT,就是在合并的时候,速度又太慢,40G的文件要10分钟左右

#20


多进程写同一个文件的话,又要去加锁啥的,估计也很慢

#21


引用楼主 xiaowhy 的回复:
各位高手帮忙看看,我这有几十G级别的比如60G文件需要读写,生成一些子文件,需要对原来文件的每一行进行解析;之前用fgets每一行的方法,客户对时间不是很满意;后来改用fork多个子进程,分段读取源文件,再生成不同的子文件,最后再把子文件合在一起,效果也不是很理想,逻辑不复杂,估计主要是IO的瓶颈;内存映射空间不够大吧,而且还要一行行的解析出来。
各位亲们不知有没有更好的办法啊?


文件以缓冲的形式打开,应该能有所改善,但是,60G实在是太大了,一行行fgets才是占用时间的关键,应该在这上面想办法优化

#22


引用 19 楼 xiaowhy 的回复:
引用 16 楼 hgxxx007 的回复:

给你个建议,用空间换时间。

你看看你地文本内容长度最大是多少,建议设定一个值:256或者128

然后如果每行不足256个字节,补足256字节。进行文件定位的时候直接seek到指定行。

如果内容不是简单的就知道行数,那么建议再创建一个外部索引,进行读写文件的时候只是先查看索引然后在进行读写文件指定位置。

如果你的需求更复杂一点……
硬盘的存储速度也就这么慢吧!!!

#23


引用 18 楼 jackyjkchen 的回复:
至少对于Windows,内存映射的机制类似于异步IO,省去了同步等待ReadFile和WriteFile的时间,我做加密算法和Hash算法,处理大文件还是文件映射快

内存映射也是等到缺页的时候才去读文件的吧?也是同步的才对。

刚刚在 Linux 下分别用 MD5 和每行的字符个数统计都试了一遍,内存映射比直接分块读取是要慢那么一点点,差距大概是几个 MB/s……

#24


mmap 就节约一次从内核空间到用户空间之间的拷贝, 其他速度上差别不大.  如果用 mmap 慢了就比较奇怪了.

这东西你还是优化优化处理过程吧, 要快的时候就  fread , fwrite , mmap 信得过. 这种东西瓶颈都在IO上, 多线程/进程没啥用的...

#25


可以利用分页的思想去读吧?先读取100行显示出来,在101到200行

#26


引用 23 楼 hpsmouse 的回复:
引用 18 楼 jackyjkchen 的回复:
至少对于Windows,内存映射的机制类似于异步IO,省去了同步等待ReadFile和WriteFile的时间,我做加密算法和Hash算法,处理大文件还是文件映射快

内存映射也是等到缺页的时候才去读文件的吧?也是同步的才对。

刚刚在 Linux 下分别用 MD5 和每行的字符个数统计都试了一遍,内存映射比直接分块读取是要慢那么一点点……


操作系统区别,Windows下内存映射优势很明显

#27


引用 19 楼 xiaowhy 的回复:
现在是有把文件分成几段,分子进程去读写,生成几个临时文件,再合并,直接用系统命令CAT,就是在合并的时候,速度又太慢,40G的文件要10分钟左右

40GB 读 + 40GB 写 = 80GB I/O,平均下来有 136.5MB/s。

如果用的是单块硬盘这速度都快暴表了……你还想闹那样……

#1


内存映射不一定非要映射整个文件进来的,映射一段处理完再换一段。

#2


用二进制模式打开,用fread()读取,读取到内存中再处理,而不是一行一行地读取文件。
这样会不会快些?游戏编程都是推荐这样操作的。
---------
不懂海量数据处理,纯路过……

#3


引用 1 楼 qq120848369 的回复:
内存映射不一定非要映射整个文件进来的,映射一段处理完再换一段。

那不还得一行行的截出来读啊?而且临界的地方如果不是正好一行的话,也很麻烦啊

#4


void mmap(void *addr,size_t len,int prot,in flag,int fd,off_t offset)


这是函数原型,offset是文件内的字节偏移量,len是映射多少字节.

问题就是要区分行边界.

第一步,映射文件开始的2G进来,找'\r'啥的,就可以得到一行,具体的你慢慢研究...如果找不到'\r'了,那后边的字节不能白瞎,你得通过计算把这些字节留下来作为下面2G的开始位置.

如此简单的问题,不解释了.

#5


你的问题是I/O瓶颈,这个与海量数据处理没啥关系.

如果给预处理时间的话,那可以读大文件哈希到若干小文件,每个小文件可以直接映射到内存处理,这样的话利用两个线程并发遍历所有的文件,效率会高不少了,因为I/O问题已经不存在了 =,=

#6


引用 5 楼 qq120848369 的回复:
你的问题是I/O瓶颈,这个与海量数据处理没啥关系.

如果给预处理时间的话,那可以读大文件哈希到若干小文件,每个小文件可以直接映射到内存处理,这样的话利用两个线程并发遍历所有的文件,效率会高不少了,因为I/O问题已经不存在了 =,=

哦,我再试试,谢谢啊

#7


引用 5 楼 qq120848369 的回复:
你的问题是I/O瓶颈,这个与海量数据处理没啥关系.

如果给预处理时间的话,那可以读大文件哈希到若干小文件,每个小文件可以直接映射到内存处理,这样的话利用两个线程并发遍历所有的文件,效率会高不少了,因为I/O问题已经不存在了 =,=


我想问下,我碰到过一个问题,对一个文件做MD5校验,当时我是用malloc申请的内存一次性读进来所有数据,但是对超大文件怎么做MD5校验啊,分割了不能做校验了吧。

#8


引用 7 楼 mangsong 的回复:
引用 5 楼 qq120848369 的回复:
你的问题是I/O瓶颈,这个与海量数据处理没啥关系.

如果给预处理时间的话,那可以读大文件哈希到若干小文件,每个小文件可以直接映射到内存处理,这样的话利用两个线程并发遍历所有的文件,效率会高不少了,因为I/O问题已经不存在了 =,=


我想问下,我碰到过一个问题,对一个文件做MD5校验,当时我是用malloc申请的内存一次性读进来所有……

md5支持分割校验,前提是你得有基础算法

md5基础算法一般是三个函数
init
process
done
其中process可以执行多次

#9


以前碰到过类似的问题,IO是很大的瓶颈。写文件的时候,可以设置个较大的缓存,减少写的次数

#10


引用 9 楼 aaajj 的回复:
以前碰到过类似的问题,IO是很大的瓶颈。写文件的时候,可以设置个较大的缓存,减少写的次数

这个我也有加啊,一般8K左右最好吧

#11


别急着优化程序,先算算现在的 I/O 速度是多少,要是已经达到了硬盘的速度上限程序再怎么优化都是白搭。

顺序读取大文件,内存映射其实没有直接读取快,不要碰到大文件就想着内存映射。

#12


我写过一个文件处理的程序,是顺序读取的。我不懂内存映射。只是自己把小段小段的读取,改成大段大段读取。减少文件读取次数,还是挺有效的。边界的问题,想办法处理下就好了。

不过,我也很想知道有什么经典的解决方法。老是做重复发明*的事情,浪费时间。还发明得比别人差,丢脸。

#13


有一点现在都没弄明白,当时想通过setvbuf修改缓冲大小来提高效率,没有看到任何效果。后来改成每次读取大段内容却有效。不知道问题出在哪里。

#14


不懂,帮顶!

#15


引用 11 楼 hpsmouse 的回复:
别急着优化程序,先算算现在的 I/O 速度是多少,要是已经达到了硬盘的速度上限程序再怎么优化都是白搭。

顺序读取大文件,内存映射其实没有直接读取快,不要碰到大文件就想着内存映射。


硬件问题硬件解决

#16


给你个建议,用空间换时间。

你看看你地文本内容长度最大是多少,建议设定一个值:256或者128

然后如果每行不足256个字节,补足256字节。进行文件定位的时候直接seek到指定行。

如果内容不是简单的就知道行数,那么建议再创建一个外部索引,进行读写文件的时候只是先查看索引然后在进行读写文件指定位置。

如果你的需求更复杂一点,需要在中间插入和删除行。建议把大文件拆分成小文件。然后外部任然通过一个索引文件进行索引。文件大小或者分片与否对上层不可见。

#17


你要知道你地程序读取的IO大部分都是废IO,

那就说明程序的优化空间或者架构优化空间还非常非常大。

#18


引用 11 楼 hpsmouse 的回复:
别急着优化程序,先算算现在的 I/O 速度是多少,要是已经达到了硬盘的速度上限程序再怎么优化都是白搭。

顺序读取大文件,内存映射其实没有直接读取快,不要碰到大文件就想着内存映射。

至少对于Windows,内存映射的机制类似于异步IO,省去了同步等待ReadFile和WriteFile的时间,我做加密算法和Hash算法,处理大文件还是文件映射快

#19


引用 16 楼 hgxxx007 的回复:
给你个建议,用空间换时间。

你看看你地文本内容长度最大是多少,建议设定一个值:256或者128

然后如果每行不足256个字节,补足256字节。进行文件定位的时候直接seek到指定行。

如果内容不是简单的就知道行数,那么建议再创建一个外部索引,进行读写文件的时候只是先查看索引然后在进行读写文件指定位置。

如果你的需求更复杂一点,需要在中间插入和删除行。建议把大文件拆分成小文……

现在是有把文件分成几段,分子进程去读写,生成几个临时文件,再合并,直接用系统命令CAT,就是在合并的时候,速度又太慢,40G的文件要10分钟左右

#20


多进程写同一个文件的话,又要去加锁啥的,估计也很慢

#21


引用楼主 xiaowhy 的回复:
各位高手帮忙看看,我这有几十G级别的比如60G文件需要读写,生成一些子文件,需要对原来文件的每一行进行解析;之前用fgets每一行的方法,客户对时间不是很满意;后来改用fork多个子进程,分段读取源文件,再生成不同的子文件,最后再把子文件合在一起,效果也不是很理想,逻辑不复杂,估计主要是IO的瓶颈;内存映射空间不够大吧,而且还要一行行的解析出来。
各位亲们不知有没有更好的办法啊?


文件以缓冲的形式打开,应该能有所改善,但是,60G实在是太大了,一行行fgets才是占用时间的关键,应该在这上面想办法优化

#22


引用 19 楼 xiaowhy 的回复:
引用 16 楼 hgxxx007 的回复:

给你个建议,用空间换时间。

你看看你地文本内容长度最大是多少,建议设定一个值:256或者128

然后如果每行不足256个字节,补足256字节。进行文件定位的时候直接seek到指定行。

如果内容不是简单的就知道行数,那么建议再创建一个外部索引,进行读写文件的时候只是先查看索引然后在进行读写文件指定位置。

如果你的需求更复杂一点……
硬盘的存储速度也就这么慢吧!!!

#23


引用 18 楼 jackyjkchen 的回复:
至少对于Windows,内存映射的机制类似于异步IO,省去了同步等待ReadFile和WriteFile的时间,我做加密算法和Hash算法,处理大文件还是文件映射快

内存映射也是等到缺页的时候才去读文件的吧?也是同步的才对。

刚刚在 Linux 下分别用 MD5 和每行的字符个数统计都试了一遍,内存映射比直接分块读取是要慢那么一点点,差距大概是几个 MB/s……

#24


mmap 就节约一次从内核空间到用户空间之间的拷贝, 其他速度上差别不大.  如果用 mmap 慢了就比较奇怪了.

这东西你还是优化优化处理过程吧, 要快的时候就  fread , fwrite , mmap 信得过. 这种东西瓶颈都在IO上, 多线程/进程没啥用的...

#25


可以利用分页的思想去读吧?先读取100行显示出来,在101到200行

#26


引用 23 楼 hpsmouse 的回复:
引用 18 楼 jackyjkchen 的回复:
至少对于Windows,内存映射的机制类似于异步IO,省去了同步等待ReadFile和WriteFile的时间,我做加密算法和Hash算法,处理大文件还是文件映射快

内存映射也是等到缺页的时候才去读文件的吧?也是同步的才对。

刚刚在 Linux 下分别用 MD5 和每行的字符个数统计都试了一遍,内存映射比直接分块读取是要慢那么一点点……


操作系统区别,Windows下内存映射优势很明显

#27


引用 19 楼 xiaowhy 的回复:
现在是有把文件分成几段,分子进程去读写,生成几个临时文件,再合并,直接用系统命令CAT,就是在合并的时候,速度又太慢,40G的文件要10分钟左右

40GB 读 + 40GB 写 = 80GB I/O,平均下来有 136.5MB/s。

如果用的是单块硬盘这速度都快暴表了……你还想闹那样……