erlang进程和消息传递体系结构

时间:2015-06-10 10:50:57

标签: concurrency process erlang messages

我手头的任务是读取大文件的行,处理它们,并返回有序的结果。

我的算法是:

  1. 从主进程开始,该进程将评估工作负载(写在文件的第一行)
  2. 产生工作进程:每个工作程序将使用pread / 3读取文件的一部分,处理此部分,并将结果发送给master
  3. master接收所有子结果,排序和返回 所以工人之间基本上不需要沟通。
  4. 我的问题:

    1. 如何找到erlang进程数和核心数之间的最佳平衡?所以,如果我为每个处理器核心生成一个进程,我会利用我的cpu吗?
    2. pread / 3如何到达指定的行;它会迭代文件中的所有行吗?并且pread / 3是并行文件读取的好计划吗?
    3. 从进程A向B发送一条大消息或发送N条小消息会更好吗?我在下面的链接中找到了部分答案,但我希望进一步阐述 erlang message passing architecture

1 个答案:

答案 0 :(得分:1)

  1. Erlang流程很便宜。您可以自由(并鼓励)使用多于您拥有的核心。可能存在对您的问题的实际上限(每行一个进程中加载​​1TB数据需要多少时间,具体取决于行大小)。

    当您不知道时,最简单的方法是让用户决定。这意味着您可以决定生成N工作人员,并在他们之间分配工作,等待回复。如果您不喜欢它的运行方式,请在更改N时重新运行该程序。

    更狡猾的方法是对一大堆时间进行基准测试,选择您认为有意义的最大值,将其粘贴在池库中(如果您愿意;某些池用于预分配资源,有些用于可调整大小的资源)数量),并解决什么是一个通用的解决方案。

    但实际上,没有简单的'最佳核心数'。如果需要,您可以在50个进程以及65,000个进程上运行它;如果任务是令人尴尬的并行,那么VM应该能够使用它们中的大部分并使核心饱和。

  2. -

    1. 并行文件读取是一个有趣的问题。它可能会或可能不会更快(正如直接评论所提到的),如果每行的工作量足够小以至于读取文件的成本最高,它可能仅代表加速。

      棘手的一点是pread/2-3之类的函数需要一个字节偏移量。您的问题是措辞,以至于您担心文件的。因此,您交给工人的字节偏移可能最终跨越一条线。如果您的广告资源最终位于my中的this is my line\nhere it goes\n字词,则一位工作人员会发现自己的行不完整,而另一位工作人员只会报告my line\n,而错过之前的this is }。

      一般来说,这种烦人的东西会导致你让第一个进程拥有该文件并筛选它,只是将一些文本传递给工人;然后,该过程将充当某种协调者。

      这个策略的好处在于,如果主进程知道作为消息发送的所有内容,它还知道何时收到所有响应,从而很容易知道何时返回结果。如果一切都是不相交的,你必须相信首发和工人都要告诉你“我们都失业了”,作为一组独立的独立信息要知道。

      在实践中,您可能会发现,最有帮助的是帮助您了解有关文件操作的硬件生命的操作,而不是“有多少人可以一次读取文件”。只有一个硬盘(或SSD),所有数据都必须通过它;并行性最终可能会限制访问那里。

    2. -

      1. 使用对您的程序有意义的消息。性能最高的程序将有许多进程能够在不需要传递消息,通信或获取锁的情况下完成工作。

        一个更现实的非常高性能的程序将使用非常少的非常小的消息。

        这里有趣的是,您的问题本质上是基于数据的。所以你可以做一些事情:

        • 确保以二进制格式阅读文字;大型二进制文件(> 64b)在全局二进制堆上分配,在参考计数周围共享和GC'd
        • 提供有关需要做什么的信息,而不是用于做的数据;这个需要测量,但是引导过程可以遍历文件,注意行结束的地方,只是手工字节偏移给工人,这样他们就可以自己去读取文件了;请注意,你最终会读取文件两次,所以如果内存分配不是你的主要开销,这可能会更慢
        • 确保以rawram模式阅读该文件;其他模式使用中间人进程来读取和转发数据(如果您在集群Erlang节点中通过网络读取文件,这将非常有用); rawram模式将文件描述符直接提供给调用进程,速度更快。
        • 首先担心编写一个清晰,可读和正确的程序。只有当它太慢时才应该尝试重构和优化它;你可能会在第一次尝试时发现它很好。
      2. 我希望这会有所帮助。

        P.S。你可以先尝试一下非常简单的东西:

        1. 或者:

          • 使用{ok, Bin} = file:read_file(Path)和分割线(使用binary:split(Bin, <<"\n">>, [global])),
          • 一次阅读整个文件
          • 使用{ok, Io} = file:open(File, [read,ram]),然后在文件描述符上重复使用file:read_line(Io)
          • 使用{ok, Io} = file:open(File, [read,raw,{read_ahead,BlockSize}]),然后在文件描述符上重复使用file:read_line(Io)
        2. 调用rpc:pmap({?MODULE, Function}, ExtraArgs, Lines)自动并行运行所有内容(每行会生成一个进程)

        3. 在结果上调用lists:sort/1

        4. 然后,如果您将其识别为有问题,则可以从中完善每个步骤。