2009-06-27 75 views
9

我在尝试优化使用mmap处理大型数据集。数据集是在千兆字节范围内。这个想法是将整个文件映射到内存中,允许多个进程同时处理数据集(只读)。它虽然没有按预期工作。Linux/perl mmap性能

作为一个简单的测试,我简单地mmap文件(使用perl的Sys :: Mmap模块,使用我认为直接映射到底层C函数的“mmap”子),并让进程处于睡眠状态。在执行此操作时,代码花费超过一分钟才从mmap调用返回,尽管此测试无法执行任何操作 - 即使是读取操作,也不会执行mmap的文件。

猜测,虽然也许linux需要整个文件在第一次mmap化时被读取,所以在文件在第一个进程中映射(当它正在休眠时)之后,我在另一个进程中调用了一个简单的测试,试图读取文件的前几兆字节。

令人惊讶的是,似乎第二个过程在从mmap调用返回之前花费了大量的时间,大约与第一次mmap处理文件的时间相同。

我已经确定MAP_SHARED正在被使用,并且第一次映射文件的过程仍然是活动的(它没有终止,并且mmap没有被映射)。

我期望一个mmapped文件可以让我给多个工作进程有效地随机访问大文件,但是如果每个mmap调用都需要先读取整个文件,那么这有点困难。我还没有测试过使用长时间运行的进程来查看第一次延迟后访问是否快速,但我期望使用MAP_SHARED,而另一个单独的进程就足够了。

我的理论是,mmap会立即返回,linux会根据需要或多或少地加载块,但是我看到的行为是相反的,表明它需要读取每个文件的整个文件调用mmap。

任何想法我做错了,或者如果我完全误解了mmap应该如何工作?

回答

15

好的,发现问题了。正如所怀疑的,无论是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 lingua为“指针”/句柄值)的东西。因此,如果您需要以散列形式或类似形式存储,请确保您存储\$mh,并且在需要使用${$hash->{mh}}时将其解压缩,通常作为substr或类似的第一个参数。

+3

+1以了解详细说明。 – RichieHindle 2009-06-27 21:06:25

+3

使用3 arg形式的open()。 – 2009-06-28 03:55:23

0

这听起来令人惊讶。为什么不尝试纯C版本?

或者在不同的OS/Perl版本上尝试您的代码。

+0

我已经看了perl操作系统界面,它直接或多或少地调用C版本,但除非我弄清楚,否则我可能会测试一个C版本。 至于OS/Perl版本,我已经在两个x86_64系统上进行了测试。一个是Ubuntu 8.04.2(linux 2.6.24-22,perl 5.8.8)和另一个Ubuntu 9.04(linux 2.6.28-13,perl 5.10.0)。同样的行为。第二个系统是一台笔记本电脑,当我从我的测试中调用mmap时,我可以确定地确认存在严重的磁盘IO。 – 2009-06-27 14:36:09

8

如果你有一个相对较新版本的Perl,你不应该使用Sys :: Mmap。您应该使用PerlIO的mmap图层。

你可以发布你使用的代码吗?

+0

同意,PerlIO mmap图层可能更受欢迎,因为它也允许相同的代码通过简单地添加/删除mmap属性来使用/不使用mmap来运行。无论如何,我发现问题,发布代码,解决问题。 – 2009-06-27 15:12:03

+0

将问题解决到2GB。对于较大的文件perl仍然有问题,请参阅我的与此相关的其他答案。 – 2009-06-29 11:04:11

+0

PerlIO的mmap层是否可用于访问/ dev/mem块的读/写? – donaldh 2013-11-25 23:39:40

3

在32位系统上,mmap() s的地址空间相当有限(因操作系统而异)。请注意,如果您使用的是多GB文件,而您只能在64位系统上进行测试。 (我本来希望在评论中写这个,但是我还没有足够的声望点)

0

请参阅Wide Finder以获得permap的mmap性能。但是有一个很大的缺陷。如果您的数据集将采用传统HD,并且您将从多个进程中读取数据,则您可以轻松进入随机访问,并且IO将降至不可接受的值(20至40倍)。

1

有一点可以帮助表现的是使用'madvise(2)'。可能最容易通过Inline :: C完成 。 'madvise'可以让你告诉内核你的访问模式是什么样的(例如顺序,随机等)。

0

好的,这是另一个更新。使用Sys :: Mmap或PerlIO的“:mmap”属性都可以在perl中正常工作,但只能达到2 GB文件(魔术32位限制)。一旦文件超过2 GB,将出现以下问题:

使用Sys :: Mmap和substr来访问该文件,似乎substr只接受位置参数的32位int,即使在系统中perl支持64位。有至少一个错误贴吧:

#62646: Maximum string length with substr

使用open(my $fh, "<:mmap", "bigfile.bin"),一旦文件大于2 GB,似乎perl的将挂起/或坚持阅读第一读取整个文件(不确定哪一个,我从来没有跑过它足够长的时间来看它是否完成),导致性能下降缓慢。

我还没有找到任何解决方法,其中任何一个,我目前坚持缓慢的文件(非mmap'ed)操作处理这些文件。除非找到解决方法,否则我可能不得不使用C语言或其他更高级别的语言来实现处理,以更好地支持mmap处理大型文件。

0

如果我可以插入我自己的模块:我会建议使用File::Map而不是Sys::Mmap。它比Sys :: Mmap更容易使用,并且不易崩溃。

0

您对该文件的访问权限最好是随机的,以证明完整的mmap。如果你的使用不是均匀分布的,你可能更适合寻求,读到一个新鲜的混合区域,然后进行处理,免费,漂洗和重复。并与4k的倍数的大块工作,说64k左右。

我曾经对很多字符串模式匹配算法进行了基准测试。对整个文件进行整理是慢而毫无意义的。读到一个静态的32kish缓冲区更好,但仍不是特别好。通过阅读新鲜的malloced块,处理它,然后让它去使内核在引擎盖下运行奇迹。速度上的差异是巨大的,但是然后再次模式匹配复杂性非常快,并且必须比通常需要更多地强调处理效率。