大型图像集图像处理程序的并行化
Parallelization of image processing programs on large image set
我目前有一个非常大的目录,其中包含 9000 多个文件夹,每个文件夹中都有 jpeg 图片(平均每个文件夹 40 张)。
我的程序获取图像的输入文件夹并将该文件夹中图像的特征向量输出到文本文件:
./process_image images/ output/
我还有一个脚本,用法如下:
./script.sh dirlist.txt images/ output/ 1
第一个输入 dirlist.txt 包含输入目录中的文件夹名称
第二个和第三个输入是输入和输出的基本目录。
第 4 个参数是目录列表中我要访问的条目的索引
以上示例将调用,假设 imageset1 在 dirlist.txt 中的索引 1 处:
./process_image images/imageset1/ output/imageset1/
如果我按顺序执行此操作,处理所有 9000 个文件夹需要几天时间。在这种情况下,并行化的最佳方法是什么?我是否应该编写一个脚本,将 9000 个文件夹分成多个块,然后 运行 分别编写脚本,每个 运行 指定一定范围的索引?另外,假设一个可执行文件的 RAM 范围从 100 MB 到 1GB,我如何确定我可以 运行 多少个程序?我有 32 GB 的内存。
瓶颈
CPU 使用量不是有效的瓶颈标志,确定瓶颈是什么的最佳方法是通过测量。当您遇到瓶颈时,CPU 使用率通常会达到 100% 或接近 100%,但这仅意味着达到了某些瓶颈。
如果瓶颈来自 IO,那么 CPU 使用率可能会很低...要测量 CPU/MEM,您需要使用不同的 CPU 和传输速度,因此请更改 BIOS 中的设置看看时间是否变化很快。这并不总是有助于确定源,在这种情况下,您必须测量程序部分的运行时间并查看慢速。
然后根据该部分代码的作用自行确定还有分析工具可以自动执行其中的一些操作
并行化
您只能并行化代码的线程安全部分,因此如果您使用非线程安全库,则这些部分无法并行化。此外,如果你有相互依赖的代码部分,那么在不了解更多关于任务处理背景的情况下,并行化并不会获得太多收益
最简单和最安全的方法是每个线程处理文件夹
线程数
我通常使用尽可能多的线程,因为我有 CPU 个可用线程,这个数字可以从系统关联中获得(在 windows 上)。在对时间要求很高的应用程序上,我使用 1st CPU 作为主要代码,仅将其余部分用作线程。在你的情况下使用太多线程会导致 IO 与其他线程冲突(除非你有 RAM/SSD 驱动器)
计划
对于类似的任务持续时间,只需将任务平均分配给线程,对于非常不同的任务,运行时使用某种调度,例如创建任务队列,然后定期检查所有线程是否忙碌。找到第一个空闲线程并从队列中获取任务。
不要忘记将 Sleep() 添加到此循环中 如果所有任务都已完成,则停止所有线程并退出
我每天定期处理 65,000 多张图像,而且我几乎总是使用 GNU Parallel - 参见 here and here。我不会打扰并行化 C 代码!
它允许您指定并行 运行 的作业数,或者只使用每个 CPU 核心一个作业的默认值。它使用起来非常简单。您要做的就是更改 script.sh
,这样它就不会启动作业,而只是将 all 它本应启动的命令回显到 stdout
],然后将其通过管道传输到 parallel
,就像这样
script.sh | parallel
您可以将 -j 8
之类的标志添加到 运行 8 个并行作业,或者 -k
以保持输出顺序(如果相关)。
script.sh | parallel -j 8 -k
同样,如果您担心内存使用情况,可以告诉 parallel
仅在系统至少有 1GB 可用内存时才开始新作业:
script.sh | parallel --memfree 1G
您还可以添加其他机器的列表,它会为您在它们之间分配作业:-)
这是一个小例子:
#!/bin/bash
# script.sh
for i in {0..99}; do
echo "echo Start job $i; sleep 5; echo End job $i"
done
然后
script.sh | parallel
并且 500 秒的工作在我的 8 核机器上在 70 秒内完成,如果我使用 parallel -j 25
.
则为 21 秒
我目前有一个非常大的目录,其中包含 9000 多个文件夹,每个文件夹中都有 jpeg 图片(平均每个文件夹 40 张)。
我的程序获取图像的输入文件夹并将该文件夹中图像的特征向量输出到文本文件:
./process_image images/ output/
我还有一个脚本,用法如下:
./script.sh dirlist.txt images/ output/ 1
第一个输入 dirlist.txt 包含输入目录中的文件夹名称 第二个和第三个输入是输入和输出的基本目录。 第 4 个参数是目录列表中我要访问的条目的索引
以上示例将调用,假设 imageset1 在 dirlist.txt 中的索引 1 处:
./process_image images/imageset1/ output/imageset1/
如果我按顺序执行此操作,处理所有 9000 个文件夹需要几天时间。在这种情况下,并行化的最佳方法是什么?我是否应该编写一个脚本,将 9000 个文件夹分成多个块,然后 运行 分别编写脚本,每个 运行 指定一定范围的索引?另外,假设一个可执行文件的 RAM 范围从 100 MB 到 1GB,我如何确定我可以 运行 多少个程序?我有 32 GB 的内存。
瓶颈
CPU 使用量不是有效的瓶颈标志,确定瓶颈是什么的最佳方法是通过测量。当您遇到瓶颈时,CPU 使用率通常会达到 100% 或接近 100%,但这仅意味着达到了某些瓶颈。
如果瓶颈来自 IO,那么 CPU 使用率可能会很低...要测量 CPU/MEM,您需要使用不同的 CPU 和传输速度,因此请更改 BIOS 中的设置看看时间是否变化很快。这并不总是有助于确定源,在这种情况下,您必须测量程序部分的运行时间并查看慢速。
然后根据该部分代码的作用自行确定还有分析工具可以自动执行其中的一些操作
并行化
您只能并行化代码的线程安全部分,因此如果您使用非线程安全库,则这些部分无法并行化。此外,如果你有相互依赖的代码部分,那么在不了解更多关于任务处理背景的情况下,并行化并不会获得太多收益
最简单和最安全的方法是每个线程处理文件夹
线程数
我通常使用尽可能多的线程,因为我有 CPU 个可用线程,这个数字可以从系统关联中获得(在 windows 上)。在对时间要求很高的应用程序上,我使用 1st CPU 作为主要代码,仅将其余部分用作线程。在你的情况下使用太多线程会导致 IO 与其他线程冲突(除非你有 RAM/SSD 驱动器)
计划
对于类似的任务持续时间,只需将任务平均分配给线程,对于非常不同的任务,运行时使用某种调度,例如创建任务队列,然后定期检查所有线程是否忙碌。找到第一个空闲线程并从队列中获取任务。
不要忘记将 Sleep() 添加到此循环中 如果所有任务都已完成,则停止所有线程并退出
我每天定期处理 65,000 多张图像,而且我几乎总是使用 GNU Parallel - 参见 here and here。我不会打扰并行化 C 代码!
它允许您指定并行 运行 的作业数,或者只使用每个 CPU 核心一个作业的默认值。它使用起来非常简单。您要做的就是更改 script.sh
,这样它就不会启动作业,而只是将 all 它本应启动的命令回显到 stdout
],然后将其通过管道传输到 parallel
,就像这样
script.sh | parallel
您可以将 -j 8
之类的标志添加到 运行 8 个并行作业,或者 -k
以保持输出顺序(如果相关)。
script.sh | parallel -j 8 -k
同样,如果您担心内存使用情况,可以告诉 parallel
仅在系统至少有 1GB 可用内存时才开始新作业:
script.sh | parallel --memfree 1G
您还可以添加其他机器的列表,它会为您在它们之间分配作业:-)
这是一个小例子:
#!/bin/bash
# script.sh
for i in {0..99}; do
echo "echo Start job $i; sleep 5; echo End job $i"
done
然后
script.sh | parallel
并且 500 秒的工作在我的 8 核机器上在 70 秒内完成,如果我使用 parallel -j 25
.