10

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

作为一个简单的测试,我只是简单地映射文件(使用 perl 的 Sys::Mmap 模块,使用我认为直接映射到底层 C 函数的“mmap”子)并让进程休眠。执行此操作时,代码在从 mmap 调用返回之前会花费超过一分钟,尽管此测试没有执行任何操作 - 甚至没有读取 mmap 文件。

猜猜,我虽然也许 linux 需要在第一次映射时读取整个文件,所以在文件在第一个进程中映射后(当它处于睡眠状态时),我在另一个进程中调用了一个简单的测试来尝试读取文件的前几兆字节。

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

我确保正在使用 MAP_SHARED 并且第一次映射文件的进程仍然处于活动状态(它没有终止,并且 mmap 没有被取消映射)。

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

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

知道我做错了什么,或者我完全误解了 mmap 应该如何工作?

4

9 回答 9

17

好的,找到问题了。正如怀疑的那样,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 或类似的第一个参数。

于 2009-06-27T15:02:13.830 回答
9

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

您可以发布您正在使用的代码吗?

于 2009-06-27T13:43:22.960 回答
3

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

于 2009-06-28T14:58:28.327 回答
1

有助于提高性能的一件事是使用“madvise(2)”。可能最容易通过 Inline::C 完成。'madvise' 让您告诉内核您的访问模式将是什么样的(例如顺序、随机等)。

于 2009-06-28T23:10:35.833 回答
1

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

于 2009-06-29T21:57:25.030 回答
0

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

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

于 2009-06-27T13:38:32.673 回答
0

有关使用 mmap 的 perl 性能,请参阅Wide Finder。但是有一个很大的陷阱。如果您的数据集将在经典 HD 上并且您将从多个进程中读取,那么您很容易陷入随机访问,并且您的 IO 将下降到不可接受的值(20~40 次)。

于 2009-06-28T19:56:27.177 回答
0

好的,这是另一个更新。使用 Sys::Mmap 或 PerlIO 的 ":mmap" 属性在 perl 中都可以正常工作,但最多只能是 2 GB 文件(神奇的 32 位限制)。一旦文件超过 2 GB,就会出现以下问题:

使用 Sys::Mmap 和 substr 访问文件,似乎 substr 只接受 32 位 int 作为位置参数,即使在 perl 支持 64 位的系统上也是如此。至少有一个关于它的错误:

#62646:使用 substr 的最大字符串长度

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

我还没有找到任何解决方法,而且我目前在处理这些文件时遇到了慢速文件(非映射)操作。除非我找到解决方法,否则我可能不得不用 C 或其他更支持 mmap'ing 大文件的高级语言来实现处理。

于 2009-06-29T10:52:18.480 回答
0

您对该文件的访问最好是随机的,以证明完整的 mmap 是合理的。如果您的使用量分布不均,则最好先搜索,读取到新分配的区域并处理,释放,冲洗并重复。并使用 4k 的倍数块,比如 64k 左右。

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

于 2012-12-11T19:39:47.683 回答