我正在尝试使用mmap优化对大数据集的处理。 数据集是在千兆字节范围内。 这个想法是将整个文件映射到内存中,允许多个进程并发处理数据集(只读)。 这不是按预期工作。
作为一个简单的testing,我简单的mmap文件(使用perl的Sys :: Mmap模块,使用我认为直接映射到底层C函数的“mmap”),并让进程hibernate。 当这样做的时候,代码在从mmap调用返回之前花费了一分多时间,尽pipe这个testing什么都不做,甚至没有从mmap的文件中读取。
猜测,虽然也许linux要求整个文件在第一次mmap化时被读取,所以当文件在第一个进程(当它正在睡觉时)被映射后,我在另一个试图读取前几兆字节的文件。
令人惊讶的是,似乎第二个过程在从mmap调用返回之前花费了大量的时间,大约与mmap第一次一样。
我已经确定MAP_SHARED正在被使用,第一次映射文件的过程仍然是活动的(它没有终止,并且mmap还没有被映射)。
我期望一个mmapped文件可以让我给多个工作进程有效地随机访问这个大文件,但是如果每个mmap调用都需要先读取整个文件,那就更困难了。 我还没有使用长时间运行的进程进行testing,看看在第一次延迟之后访问是否快速,但我期望使用MAP_SHARED,而另一个单独的进程就足够了。
我的理论是,mmap会立即返回,而linux会按照需求或多或less的方式加载块,但是我所看到的行为是相反的,表明它需要通过每次调用mmap时读取整个文件。
任何想法我做错了,或者如果我完全误解了mmap应该如何工作?
好的,找到了问题。 怀疑,无论是Linux或Perl都是怪罪。 打开和访问文件我做这样的事情:
#!/usr/bin/perl # Create 1 GB file if you do not have one: # dd if=/dev/urandom of=test.bin bs=1048576 count=1000 use strict; use warnings; use Sys::Mmap; open (my $fh, "<test.bin") || die "open: $!"; my $t = time; print STDERR "mmapping.. "; mmap (my $mh, 0, PROT_READ, MAP_SHARED, $fh) || die "mmap: $!"; my $str = unpack ("A1024", substr ($mh, 0, 1024)); print STDERR " ", time-$t, " seconds\nsleeping.."; sleep (60*60);
如果你测试了这个代码,就没有像我在原代码中发现的那样延迟,并且在创建最小样本之后(总是这样做,对!)原因突然变得明显。
错误是我在我的代码中将$mh
标量当作句柄来处理,这是轻量级的,可以轻松移动(读取:按值传递)。 事实上,它实际上是一个GB长字符串,明确地说不是你想要移动而不创建一个明确的引用(perl语言的“指针”/句柄值)。 所以,如果你需要存储在一个散列或类似的,确保你存储\$mh
,当你需要像${$hash->{mh}}
使用它时,它通常作为substr中的第一个参数或类似的。
如果您有相对较新版本的Perl,则不应使用Sys :: Mmap。 你应该使用PerlIO的mmap图层。
你能发表你正在使用的代码吗?
在32位系统上, mmap()
的地址空间相当有限(并且从OS到OS也不相同)。 请注意,如果您使用的是多GB文件,而您只能在64位系统上进行测试。 (我宁愿在评论中写这个,但是我还没有足够的声望点)
有一点可以帮助表演是使用“madvise(2)”。 可能最容易通过Inline :: C完成。 'madvise'可以让你告诉内核你的访问模式是什么样的(例如顺序,随机等)。
这听起来令人惊讶。 为什么不尝试一个纯粹的C版本?
或者在不同的OS / Perl版本上尝试您的代码。
使用mmap查看宽查找器获得perl性能。 但是有一个很大的缺陷。 如果你的数据集是经典的HD,你将从多个进程中读取数据,你可以很容易地进入随机访问,你的IO将下降到不可接受的值(20〜40倍)。
好的,这是另一个更新。 使用Sys :: Mmap或PerlIO的“:mmap”属性在perl中都可以正常工作,但最多只能有2 GB的文件(magic 32 bit limit)。 文件超过2 GB后,将出现以下问题:
使用Sys :: Mmap和substr来访问文件,似乎substr只接受位置参数的32位int,即使在perl支持64位的系统上也是如此。 至少有一个关于它的bug发布:
#62646:与substr的最大字符串长度
使用open(my $fh, "<:mmap", "bigfile.bin")
,一旦文件大于2 GB,看起来perl会在第一次读取时挂起或者坚持读取整个文件我从来没有跑过这么长的时间,看看它是否完成了),导致了性能下降。
我还没有找到任何解决办法,我目前坚持慢文件(非mmap'ed)操作这些文件。 除非找到解决办法,否则我可能不得不使用C语言或其他更高级别的语言来实现处理,以便更好地支持mmap文件。
如果我可以插入我自己的模块:我建议使用File :: Map而不是Sys :: Mmap 。 它比Sys :: Mmap更容易使用,并且不易崩溃。
您对该文件的访问权限最好是随机的,以证明完整的mmap。 如果你的使用不均匀分布,你可能会更好地寻找,读到一个新鲜的mableced区域,并处理,免费,冲洗和重复。 并与4k的倍数大概工作,说64k左右。
我曾经测试了很多字符串模式匹配算法。 对整个文件进行整理是慢而毫无意义的。 读到一个静态的32kish缓冲区是更好的,但仍然不是特别好。 阅读新鲜的malloced块,处理,然后让它去允许内核在引擎盖下工作的奇迹。 速度的差异是巨大的 ,但是模式匹配又是非常复杂的,更多的是强调处理效率比通常需要的。