令人尴尬的并行工作流创建了太多的输出文件

时间:2012-11-28 15:40:30

标签: bash file-io parallel-processing embarrassingly-parallel

在Linux集群上,我运行了许多(N > 10^6)独立计算。每次计算只需几分钟,输出就是少数几行。当N很小时,我能够将每个结果存储在一个单独的文件中,以便稍后解析。但是,对于大N,我发现我浪费了存储空间(用于文件创建),而像ls这样的简单命令需要额外注意,因为bash的内部限制:-bash: /bin/ls: Argument list too long

每个计算都需要运行qsub调度算法,因此我无法创建只将输出数据聚合到单个文件的主程序。当两个程序同时完成并交错输出时,附加到单个的简单解决方案会失败。我没有对集群的管理员访问权限,因此无法安装系统范围的数据库。

如何在难以管理之前将输出数据与令人尴尬的并行计算进行整理?

3 个答案:

答案 0 :(得分:3)

1)正如你所说,它不是ls失败的;它是在启动ls之前进行全局扩展的shell。您可以使用类似

之类的东西轻松解决该问题
find . -type f -name 'GLOB' | xargs UTILITY

例如:

find . -type f -name '*.dat' | xargs ls -l

您可能希望对输出进行排序,因为find(为了提高效率)不会对文件名进行排序(通常)。 find还有许多其他选项(比如设置目录递归深度,以更复杂的方式进行过滤等)和xargs(每次调用的最大参数数,并行执行等)。请阅读man页面了解详情。

2)我不知道你是如何创建单个文件的,所以提供具体的解决方案有点困难,但这里有几个想法:

  1. 如果您自己创建文件,并且可以将文件创建延迟到作业结束(例如,通过缓冲输出),并且文件存储在支持建议锁定的文件系统或某些文件系统上其他锁定机制,如原子链接,然后您可以通过在喷出输出之前锁定它,然后解锁,将各种作业复用到一个文件中。但这有很多要求。在群集中,您可以使用单个文件为单个主机上运行的所有作业执行此操作,但您可能不会再这样做。

  2. 同样,如果您自己创建文件,则可以将每一行原子写入共享文件。 (即使NFS支持原子写入,但它不支持原子追加,见下文。)您需要为每一行添加一个唯一的作业标识符,以便您可以对其进行解复用。但是,如果你正在使用某些自动机制,例如"我的工作写入stdout然后调度框架将其复制到文件",这是不可行的。可悲的是常见的。 (实质上,这个建议与MapReduce策略非常相似。也许你可以使用它?)

  3. 其他一切都失败了,也许你可以只使用子目录。每千个文件的几千个目录比一个拥有几百万个文件的目录更易于管理。

  4. 祝你好运。

    修改根据要求,有关2.2的更多详细信息:

    你需要使用Posix I / O函数,因为,afaik,C库不提供原子写入。在Posix中,write函数始终以原子方式写入,前提是您在打开文件时指定O_APPEND。 (实际上,它在任何情况下都是原子地写的,但是如果你没有指定O_APPEND,那么每个进程都会将它自己的位置保留在文件中,因此它们最终会相互覆盖。)< / p>

    所以你需要做的是:

    1. 在程序开始时,open包含选项O_WRONLY|O_CREATE|O_APPEND的文件。 (与我之前所说的相反,这不能保证在NFS上工作,因为NFS可能无法正确处理O_APPEND。理论上,较新版本的NFS理论上可以处理仅附加文件,但它们可能并不适用。稍后对此有一些想法。)你可能不想总是使用相同的文件,所以在其名称的某处放一个随机数,以便你的各种工作有各种各样的选择。即使是糟糕的NFS实现,O_CREAT仍然是原子的,afaik。

    2. 对于每个输出行,sprintf到内部缓冲区的行,在开头放置一个唯一的id。 (你的工作必须有某种独特的ID;只需使用它。)[如果你是偏执狂,用某种记录分隔符开始行,接着是剩余行中的字节数 - 你&#39 ; ll必须在格式化之后输入这个值 - 所以该行看起来像^0274:xx3A7B29992A04:<274 bytes>\n,其中^是十六进制01或其他一些。]

    3. write文件的整行。 检查返回代码和写入的字节数。如果写入失败,请再试一次。如果写作很短,希望你跟着&#34;如果你是偏执狂&#34;上面的说明,也可以再试一次。

    4. 真的,你不应该写短文,但你永远不会知道。写长度非常简单;解复用有点复杂,但是当你需要时,你可以越过那座桥:)

      使用NFS的问题有点烦人。与2.1一样,最简单的解决方案是尝试在本地编写文件,或使用一些正确支持追加的集群文件系统。 (NFSv4允许您仅请求&#34;追加&#34;权限而不是&#34;写&#34;权限,这会导致服务器拒绝写入,如果某些其他进程已设法写入偏移量您即将使用。在这种情况下,您需要寻找文件的末尾并再次尝试写入,直到最终成功。但是,我的印象是这个功能实际上没有实现。我可以是错的。)

      如果文件系统不支持追加,您将有另一个选择:决定行长度,并始终写入该字节数。 (显然,如果所选的固定线长度比最长的线长,则更容易,但只要它们具有序列号,就可以写出多条固定长度的线。)你&#39 ; ll需要保证每个作业写入不同的偏移量,你可以通过将作业的作业号分成文件号和交错号来完成,并在交错模式下写入特定作业的所有行。交错数,到名称中包含文件号的文件中。 (如果作业按顺序编号,这是最简单的。)可以在文件末尾写入,因为unix文件系统将 - 或者至少应该 - 插入NUL或创建不连续的文件(浪费更少的空间,但取决于文件的块大小。)

      处理不支持附加但支持咨询字节范围锁定(NFSv4支持此操作)的文件系统的另一种方法是使用固定行长度的想法,如上所述,但获得对范围的锁定即将在写之前写。使用非阻塞锁定,如果无法获得锁定,请在下一个行偏移倍数处再试一次。如果可以获得锁定,则在该偏移处读取文件以验证在写入之前它没有数据;然后释放锁。

      希望有所帮助。

答案 1 :(得分:2)

如果你只关心空间:

parallel --header : --tag computation {foo} {bar} {baz} ::: foo 1 2 ::: bar I II ::: baz . .. | pbzip2 > out.bz2

或更短:

parallel --tag computation ::: 1 2 ::: I II ::: . .. | pbzip2 > out.bz2

GNU Parallel确保输出不会混合。

如果您关心的是找到结果的子集,请查看--results。

观看介绍视频以了解详情:https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

答案 2 :(得分:1)

另一种可能性是使用N个文件,其中N大于或等于集群中的节点数,并以循环方式将文件分配给您的计算。这应该避免对任何文件的并发写入,只要您对计算的执行顺序有合理的保证。