【问题标题】:Linux/perl mmap performanceLinux/perl mmap 性能
【发布时间】:2010-11-06 08:51:49
【问题描述】:

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

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

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

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

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

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

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

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

【问题讨论】:

    标签: linux perl random mmap


    【解决方案1】:

    好的,找到问题了。正如怀疑的那样,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-&gt;{mh}} 一样使用它时取消引用它,通常作为 substr 或类似的第一个参数。

    【讨论】:

      【解决方案2】:

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

      你能发布你正在使用的代码吗?

      【讨论】:

      • 同意,PerlIO mmap 层可能更可取,因为它还允许通过简单地添加/删除 mmap 属性,在有/没有 mmap 的情况下运行相同的代码。无论如何,我找到了问题,发布了代码,问题解决了。
      • 解决这个问题最多 2GB。对于较大的文件 perl 仍然有问题,请参阅我的其他与此相关的答案。
      • PerlIO 的 mmap 层是否可用于访问 /dev/mem 的读/写块?
      • @donaldh 我不知道,如果你发给我一些代码(C 或其他),我可以尝试在 Perl 中复制它。
      【解决方案3】:

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

      【讨论】:

      • +1。看起来是一个有效的答案,可以解决我提出的问题,所以感谢您没有将其作为评论发布。
      • 正如我在其他答案中发布的那样,即使在 64 位系统上,较大的文件 (>2GB) 仍然存在问题。你的答案是正确的。我的所有机器都已经是 64 位了,甚至笔记本电脑,所以这对我来说不是问题。
      【解决方案4】:

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

      【讨论】:

        【解决方案5】:

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

        【讨论】:

        • 根据我对本线程中描述的 perl 的观察(内存映射文件最多只能工作 2 GB),这是对一个非常有用的新功能的建议;如果用户映射大于 2 GB 的文件,请使用分段方法和“自定义”读取功能,根据需要自动取消映射/映射。至少在 2 GB perl“错误”被修复之前..
        【解决方案6】:

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

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

        【讨论】:

        • 我查看了 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。
        【解决方案7】:

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

        【讨论】:

        • 我正在尝试做的是通过设计从多个进程随机访问,确保只有最常访问的文件部分始终保留在内存中。如果需要从多个进程和大文件进行随机访问,您会建议什么模式?
        • 如果你真的需要随机访问大文件,没有更好的解决方案。
        • 更好的解决方案是将一堆读取请求排队,受短暂超时限制,然后以最佳顺序从磁盘读取块,以最大限度地减少寻道时间。我不确定是否有任何文件系统已经这样做了,我认为 ZFS 没有。它最适合运行许多并发进程,例如Web 服务器,或使用不同的 IO API。
        • @Sam Watkins:很好,您只需提供 IO 调度程序的工作即可。你可以做得更好,但它需要做很多工作并且涉及很多技巧,禁用操作系统缓存,禁用操作系统预读等等。祝你在这项任务中好运。
        【解决方案8】:

        好的,这是另一个更新。使用 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, "&lt;:mmap", "bigfile.bin"),一旦文件大于 2 GB,似乎 perl 会挂起/或坚持在第一次读取时读取整个文件(不确定是哪个,我从来没有运行足够长的时间来查看它是否完成),导致性能非常缓慢。

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

        【讨论】:

        • 尝试直接使用 Sys::Mmap 中的 mmap 在标量中创建滑动窗口。
        • 谢谢,这当然是一种解决方法。这将需要跟踪指向文件的指针并在必要时映射/取消映射,这可能会影响性能。但它可能仍然比直接文件 IO 更快。
        • 做了一些基准测试,确认使用 2 GB 的段大小动态映射/取消映射,并假设段切换相当少见,使用带取消映射/映射的 mmap 的速度比使用 mmap 快 30-40% 3 GB 文件上的直接文件 IO。在 2 GB 文件上,差异较小,但我怀疑这是由于我的笔记本电脑在随机访问期间缓存了大部分文件。所以至少我有一个可行的解决方案,虽然不像我希望的那样干净。不过现阶段无需进一步优化。
        【解决方案9】:

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

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

        【讨论】:

          猜你喜欢
          • 2015-03-21
          • 2011-07-27
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2012-08-08
          • 2019-03-21
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多