GNU 与 for 循环并行?

GNU 与 for 循环并行?

我找到了与此接近的答案,但无法理解如何在我的情况下使用它们(我对 Bash 相当陌生)...所以,我正在尝试处理包含大图像序列(100k+ 文件)的文件夹使用 Imagemagick 并希望使用 GNU Parallel 来加快速度。

这是我使用的代码(一次处理 100 帧以避免耗尽内存):

calcmethod1=mean;
allframes=(*.png)
cd out1

for (( i=0; i < "${#allframes[@]}" ; i+=100 )); do 
    convert "${allframes[@]:i:100}" -evaluate-sequence "$calcmethod1" \
        -channel RGB -normalize ../out2/"${allframes[i]}"
done

我将如何“并行化”这个?我发现的大多数解决方案都不使用循环而是使用管道 - 但这样做我遇到了一个问题,即我的脚本会因为我的参数列表变得太长而中断......

我想我想做的是分割parallel负载,比如将前 100 帧交给核心 1,将帧 100-199 交给核心 2 等等?

答案1

命令

您的示例程序似乎并不关心您正在构造的数组的顺序*.pngallframes但您的评论让我相信顺序很重要。

我想我想做的是并行分割负载,例如将前 100 帧交给核心 1,将帧 100-199 交给核心 2 等?

重击

因此,我首先对脚本进行修改,如下所示,更改allframes数组的结构,以便文件按数字顺序存储。

allframes=($(printf "%s\n" *.png | sort -V | tr '\n' ' '))

这可以使用以下方法进一步简化sort -zV

allframes=($(printf "%s\0" *.png | sort -zV | tr '\0' ' '))

这会对构建convert ...命令产生影响,使它们现在看起来像这样:

$ convert "0.png 1.png 2.png 3.png 4.png 5.png 6.png 7.png 8.png 9.png \
          10.png 11.png 12.png 13.png 14.png 15.png 16.png 17.png 18.png \
          19.png 20.png 21.png 22.png 23.png 24.png 25.png 26.png 27.png \
          28.png 29.png 30.png 31.png 32.png 33.png 34.png 35.png 36.png \
          37.png 38.png 39.png 40.png 41.png 42.png 43.png 44.png 45.png \
          46.png 47.png 48.png 49.png 50.png 51.png 52.png 53.png 54.png \
          55.png 56.png 57.png 58.png 59.png 60.png 61.png 62.png 63.png \
          64.png 65.png 66.png 67.png 68.png 69.png 70.png 71.png 72.png \
          73.png 74.png 75.png 76.png 77.png 78.png 79.png 80.png 81.png \
          82.png 83.png 84.png 85.png 86.png 87.png 88.png 89.png 90.png \
          91.png 92.png 93.png 94.png 95.png 96.png 97.png 98.png 99.png" \
          -evaluate-sequence "mean" -channel RGB -normalize ../out2/0.png

相似之处

在 eschwartz 的示例的基础上,我整理了一个parallel示例,如下所示:

$ printf '%s\n' *.png | sort -V | parallel -n100 --dryrun convert {} \
   -evaluate-sequence 'mean' -channel RGB -normalize ../out2/{1}

再次,更简单地使用sort -zV

$ printf '%s\0' *.png | sort -zV | parallel -0 -n100 --dryrun "convert {} \
   -evaluate-sequence 'mean' -channel RGB -normalize ../out2/{1}

笔记:上面有一个回显“...”作为parallel开始的操作。这样做有助于可视化正在发生的事情:

$ convert 0.png 1.png 2.png 3.png 4.png 5.png 6.png 7.png 8.png 9.png 10.png \
         11.png 12.png 13.png 14.png 15.png 16.png 17.png 18.png 19.png \
         20.png 21.png 22.png 23.png 24.png 25.png 26.png 27.png 28.png \
         29.png 30.png 31.png 32.png 33.png 34.png 35.png 36.png 37.png \
         38.png 39.png 40.png 41.png 42.png 43.png 44.png 45.png 46.png \
         47.png 48.png 49.png 50.png 51.png 52.png 53.png 54.png 55.png \ 
         56.png 57.png 58.png 59.png 60.png 61.png 62.png 63.png 64.png \ 
         65.png 66.png 67.png 68.png 69.png 70.png 71.png 72.png 73.png \ 
         74.png 75.png 76.png 77.png 78.png 79.png 80.png 81.png 82.png \
         83.png 84.png 85.png 86.png 87.png 88.png 89.png 90.png 91.png \
         92.png 93.png 94.png 95.png 96.png 97.png 98.png 99.png \
         -evaluate-sequence mean -channel RGB -normalize ../out2/0.png

如果您对此输出感到满意,只需将开关移至--dryrunparallel然后重新运行即可。

$ printf '%s\0' *.png | sort -zV | parallel -0 -n100 convert {} \ 
    -evaluate-sequence 'mean' -channel RGB -normalize

参考

答案2

正确的解决方案是使用 shell 内置命令打印文件名,printf '%s\0' *.png该命令不易受到命令行参数长度的 ARG_MAX 限制的影响,然后通过管道将其parallel --null读取这些文件名并根据需要对作业进行批处理。

parallel我们将使用其中的一些功能:

  • --null需要在空字符上合理地分割文件名,以防止奇怪的文件名出现奇怪的问题
  • -n 100就像 xargs 一样,每次调用都会处理 100 个文件
  • {}包含这 100 个文件名
  • ../out2/{1}仅包含第一个

所以,这将变成:

calcmethod1=mean
printf '%s\0' *.png | parallel --null -n 100 convert {} -evaluate-sequence $calcmethod1 -channel RGB -normalize {} ../out2/{1}

您认为为什么管道不起作用?管道工作正常,只有外部分支命令才可以不是从管道读取,有问题参数长度。管道实际上就是parallel.

答案3

可以convert在自己的子 shell 中运行每个进程:

#!/bin/bash

for (( i=1; i<=1000; i++ )) do
(
command --options ) &
disown
done

exit 0

要了解它是如何工作的,请尝试以下脚本:

#!/bin/bash

echo "Hi!"

for (( i=1; i<=1000; i++ )) do
(
sleep 30
echo "Bye, "$i"!" ) &
disown
done

exit 0

指定脚本名称par.sh并随后检查进程:

ps aux | grep par.sh

我们可以假设本机 Linux CPU 负载平衡器应该在 CPU 核心之间均匀分布进程,因为每个子 shell 都有一个单独的 pid。无论如何,类似的东西cpuset总是可以使用的。

相关内容