2

在 Linux 集群上,我运行许多 ( N > 10^6) 独立计算。每次计算只需要几分钟,输出是几行。很小的时候N,我能够将每个结果存储在一个单独的文件中,以便以后解析。N但是,我发现我在浪费存储空间(用于文件创建),并且由于 bash: 的内部限制,像需要ls特别小心的简单命令-bash: /bin/ls: Argument list too long

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

如何在令人尴尬的并行计算变得无法管理之前整理输出数据?

4

3 回答 3

3

1)正如你所说,这不是ls失败的;它是在启动之前进行全局扩展的外壳ls。您可以使用类似的方法轻松解决该问题

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. 其他一切都失败了,也许你可以只使用子目录。几千个目录,每个目录包含一千个文件,比一个目录包含几百万个文件更易于管理。

祝你好运。

编辑根据要求,关于 2.2 的更多详细信息:

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

所以你需要做的是:

  1. 在程序的开头,open一个带有 options 的文件O_WRONLY|O_CREATE|O_APPEND。(与我之前所说的相反,这不能保证在 NFS 上工作,因为 NFS 可能无法O_APPEND正确处理。较新版本的 NFS 理论上可以处理仅附加文件,但它们可能不会。稍后对此进行一些思考.) 您可能不想总是使用同一个文件,所以在其名称中添加一个随机数,以便您的各种工作有多种选择。O_CREAT总是原子的,afaik,即使是糟糕的 NFS 实现。

  2. 对于每个输出行,sprintf该行到一个内部缓冲区,在开头放置一个唯一的 id。(你的工作必须有某种唯一的 id;只要使用它。)[如果你偏执,用某种记录分隔符开始该行,然后是剩余行中的字节数——你必须在格式化后输入这个值——所以该行看起来像hex 01 或类似的东西^0274:xx3A7B29992A04:<274 bytes>\n在哪里。]^

  3. write文件的整行。检查返回码和写入的字节数。如果写入失败,请重试。如果写的很短,希望你遵循上面的“如果你是偏执狂”的说明,也可以再试一次。

真的,你不应该写短文,但你永远不知道。写长度很简单;多路分解有点复杂,但你可以在需要时跨过那座桥:)

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

如果文件系统不支持追加,您将有另一种选择:决定行长,并始终写入该字节数。(显然,如果所选的固定行长度比可能的最长行长,则更容易,但只要它们具有序列号,就可以编写多个固定长度的行。)您需要保证每个作业都写入不同的偏移量,您可以通过将作业的作业编号分为文件编号和交错编号,并将特定作业的交错处的所有行以交错数取模,写入名称包含文件编号的文件中。(如果作业按顺序编号,这是最简单的。)可以在文件末尾之外写入,因为 unix 文件系统将 - 或至少,

处理不支持追加但支持建议字节范围锁定(NFSv4 支持此)的文件系统的另一种方法是使用固定行长度的想法,如上所述,但在之前要写入的范围上获得锁定写它。使用非阻塞锁,如果无法获得锁,则在下一个行偏移倍数处重试。如果能获得锁,则读取该偏移处的文件,在写入之前验证它没有数据;然后释放锁。

希望有帮助。

于 2012-11-28T17:02:35.770 回答
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

于 2012-11-29T10:16:12.487 回答
1

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

于 2012-11-29T04:42:17.490 回答