【问题标题】:Parallelization of image processing programs on large image set大图像集上图像处理程序的并行化
【发布时间】:2015-02-05 01:30:49
【问题描述】:

我目前有一个非常大的目录,其中包含 9000 多个文件夹,每个文件夹都包含 jpeg 图像(每个文件夹平均 40 个)。

我的程序获取图像的输入文件夹并将该文件夹中图像的特征向量输出到文本文件:

./process_image images/ output/

我还有一个脚本,用法如下:

./script.sh dirlist.txt images/ output/ 1

第一个输入 dirlist.txt 包含输入目录中的文件夹名称 第 2 和第 3 个输入是输入和输出的基本目录。 第四个参数是我要访问的目录列表中的条目的索引

上面的例子会调用,假设 imageset1 在 dirlist.txt 中的索引为 1:

./process_image images/imageset1/ output/imageset1/

如果我按顺序执行此操作,我需要几天时间来处理所有 9000 个文件夹。在这种情况下,并行化的最佳方法是什么?我是否应该编写一个将 9000 个文件夹分成块并单独运行脚本的脚本,每个脚本都运行一定范围的索引?此外,鉴于一个可执行文件的 RAM 范围为 100 MB 到 1GB,我如何确定可以运行多少程序?我有 32 GB 的 RAM。

【问题讨论】:

  • 瓶颈是什么? io或cpu或内存带宽? C++ 和这个问题有什么关系?
  • 我不确定如何解决这个问题,以及我的瓶颈是什么。该程序是用 C++ 编写的。
  • 我刚试过同时处理10个文件夹,我的CPU使用率在90%左右。可以肯定地说我的瓶颈是 CPU 吗?我在 i7-3770 上运行

标签: bash image-processing parallel-processing scientific-computing


【解决方案1】:

我每天定期处理 65,000 多张图像,而且我几乎总是使用 GNU Parallel - 请参阅 herehere。我不会费心并行化 C 代码!

它允许您指定并行运行多少个作业,或者只使用每个 CPU 核心一个作业的默认值。使用起来非常简单。您所要做的就是更改您的script.sh,这样它就不会启动作业,而是将它本来会启动的命令(每行一个)回显到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

在我的 8 核机器上,500 秒的工作在 70 秒内完成,如果我使用 parallel -j 25,则需要 21 秒。

【讨论】:

  • --noswap 已知是 hrmmm.... 不是最优的。请改用新的--memfree 1G
  • @OleTange 一如既往地感谢您的宝贵意见。
  • 感谢您的帖子。非常有用的东西。不幸的是,我现在在 Windows 上,并不能完全让 GNU Parallel 工作。我想我发现 xargs 是另一种选择。您是否知道使用 xargs 执行与“script.sh | parallel -j 8”相同的语法?
  • 我尝试了“script.sh | xargs -n8 -P8”,但当时似乎只生成了 1 个。当我在要执行的脚本行的末尾添加 & 时,它会产生超过 8 个。
  • "echo {0..100} | xargs -n 1 -P 8 ./script.sh" 完成了这项工作,但我不确定这与 GNU 并行有什么不同
【解决方案2】:
  1. 瓶颈

    CPU 使用率不是有效的瓶颈标志 确定瓶颈的最佳方法是通过测量。当您遇到瓶颈时,CPU 使用率通常会达到 100% 或接近,但这仅意味着达到了一些瓶颈。

    如果瓶颈来自 IO,那么 CPU 使用率可能会很低...要测量 CPU/MEM,您需要使用不同的 CPU 和传输速度,因此请更改 BIOS 中的设置以查看时间是否快速变化。这并不总是有助于确定源,在这种情况下,您必须测量程序各部分的运行时间并查看什么是慢的。

    然后根据那部分代码的功能自行确定,还有一些分析工具会自动执行其中的一些操作

  2. 并行化

    您只能并行化代码的线程安全部分,因此如果您使用非线程安全库,则这些部分无法并行化。此外,如果您有相互依赖的代码部分,那么在不了解更多关于您的任务的处理背景的情况下,并行化并不会获得太多收益,这很难说

    最简单和最安全的方法是每个线程处理文件夹

  3. 线程数

    我通常使用尽可能多的线程,因为我有可用的 CPU,这个数字可以从系统关联中获得(在 Windows 上)。在时序要求高的应用程序中,我使用第一个 CPU 作为主代码,其余的只作为线程。在您的情况下,使用太多线程会导致 IO 与其他线程发生冲突(除非您有 RAM/SSD 驱动器)

  4. 调度

    对于类似的任务持续时间,只需将任务均匀地分配给线程,用于非常不同的任务运行时使用某种调度,例如创建任务队列,然后定期检查所有线程是否忙碌。找到第一个空闲线程并从 que 中获取任务。

    不要忘记将 Sleep() 添加到此循环中,如果所有任务都已完成,则停止所有线程并退出

【讨论】:

  • Sleep ?你一定在开玩笑。空闲线程自己接工作要容易得多。这是即时的,并且不需要一个几乎总是在睡觉的主循环,并且通常在必须做某事时会晚半睡。
  • @MSalters (+1)我同意自雇线程更快,但需要更多经验才能正确编写它们(以线程安全的方式)以完成更长的工作,例如每个文件夹几秒钟和主循环在操作系统周围睡眠调度粒度是不那么重要的浪费(但需要在具体实现上进行衡量才能确定)
  • 线程安全如果很琐碎:只需在打开的任务列表上打一个互斥锁。读取和处理单个图像非常复杂,互斥体不会引起明显的争用。 (如果争用问题,将任务列表分解为受N个互斥锁保护的N个子列表)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-06-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-30
相关资源
最近更新 更多