Bash 大型图像集上图像处理程序的并行化

Bash 大型图像集上图像处理程序的并行化,bash,image-processing,parallel-processing,scientific-computing,Bash,Image Processing,Parallel Processing,Scientific Computing,我目前有一个非常大的目录,包含9000多个文件夹,每个文件夹中平均有40个jpeg图像 我的程序获取一个图像输入文件夹,并将该文件夹中图像的特征向量输出到文本文件: ./process_image images/ output/ 我还有一个脚本,其用法如下: ./script.sh dirlist.txt images/ output/ 1 第一个输入dirlist.txt包含输入目录中的文件夹名称 第二和第三输入是输入和输出的基本目录。 第四个参数是我要访问目录列表中哪个条目的索引 假设i

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

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

./process_image images/ output/
我还有一个脚本,其用法如下:

./script.sh dirlist.txt images/ output/ 1
第一个输入dirlist.txt包含输入目录中的文件夹名称 第二和第三输入是输入和输出的基本目录。 第四个参数是我要访问目录列表中哪个条目的索引

假设imageset1位于dirlist.txt中的索引1,则上面的示例将调用:

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

瓶颈

CPU使用率不是一个有效的瓶颈标志——通过测量来决定什么是瓶颈的最佳方法。当遇到瓶颈时,CPU使用率通常达到100%或接近100%,但这只意味着达到了某个瓶颈

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

然后根据这部分代码的功能自行确定。还有一些分析工具可以自动完成部分工作

并行化

您只能并行化代码中线程安全的部分,因此如果您使用非线程安全的libs,那么这些部分就不能并行化。此外,如果代码的各个部分相互依赖,那么在不了解任务的处理背景的情况下,并行化也不会太快

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

线程数

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

日程安排

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

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

瓶颈

CPU使用率不是一个有效的瓶颈标志——通过测量来决定什么是瓶颈的最佳方法。当遇到瓶颈时,CPU使用率通常达到100%或接近100%,但这只意味着达到了某个瓶颈

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

然后根据这部分代码的功能自行确定。还有一些分析工具可以自动完成部分工作

并行化

您只能并行化代码中线程安全的部分,因此如果您使用非线程安全的libs,那么这些部分就不能并行化。此外,如果代码的各个部分相互依赖,那么在不了解任务的处理背景的情况下,并行化也不会太快

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

线程数

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

日程安排

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

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


我每天定期处理65000多张图像,而且我几乎总是使用GNU并行-参见 和我不会费心去并行C代码

它允许您指定并行运行的作业数,或者只使用默认的每个CPU核心一个作业。它使用起来非常简单。您所要做的就是更改script.sh,这样它就不会启动作业,而是将它本来会启动的所有命令(每行一个)回显到stdout,然后通过管道将其并行化,如下所示

script.sh | parallel
您可以添加-j8这样的标志来并行运行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
然后


在我的8核机器上,500秒的工作只需70秒就可以完成,如果我使用parallel-J25,则需要21秒。

我每天定期处理65000多张图像,我几乎总是使用GNU parallel-请参阅和。我不会费心去并行C代码

它允许您指定并行运行的作业数,或者只使用默认的每个CPU核心一个作业。它使用起来非常简单。您所要做的就是更改script.sh,这样它就不会启动作业,而是将它本来会启动的所有命令(每行一个)回显到stdout,然后通过管道将其并行化,如下所示

script.sh | parallel
您可以添加-j8这样的标志来并行运行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
然后



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

瓶颈是什么?io或cpu或内存带宽?C++和这个问题有什么关系?我不知道如何解决这个问题,我的瓶颈是什么。这个程序是用C++编写的。我只是尝试同时处理10个文件夹,我的CPU使用率大约是90%。可以安全地说我的瓶颈是CPU吗?我运行的是i7-3770,瓶颈是什么?io或cpu或内存带宽?C++和这个问题有什么关系?我不知道如何解决这个问题,我的瓶颈是什么。这个程序是用C++编写的。我只是尝试同时处理10个文件夹,我的CPU使用率大约是90%。可以安全地说我的瓶颈是CPU吗?我在一台i7-3770上运行-noswap被称为hrmmm。。。。非最优。改用新的memfree 1G。@OleTange一如既往地感谢您的宝贵意见。谢谢您的帖子。非常有用的东西。不幸的是,我现在在Windows上,无法让GNU并行工作。我想我发现xargs是另一种选择。您知道使用xargs执行与script.sh | parallel-j8相同的语法吗?我尝试了script.sh | xargs-n8-P8,但当时似乎只生成了1个。当我在我想要执行的脚本行的末尾添加&时,它会产生超过8.echo{0..100}xargs-n1-p8的路径。/script.sh完成了这项工作,尽管我不确定这与GNU parallel有什么不同-noswap被称为hrmmm。。。。非最优。改用新的memfree 1G。@OleTange一如既往地感谢您的宝贵意见。谢谢您的帖子。非常有用的东西。不幸的是,我现在在Windows上,无法让GNU并行工作。我想我发现xargs是另一种选择。您知道使用xargs执行与script.sh | parallel-j8相同的语法吗?我尝试了script.sh | xargs-n8-P8,但当时似乎只生成了1个。当我在我想要执行的脚本行的末尾添加&时,它会产生超过8.echo{0..100}xargs-n1-p8的结果。/script.sh完成了这项工作,尽管我不确定这与GNU有什么不同?你一定是在开玩笑。对于空闲线程来说,自己完成工作要容易得多。那是即时的,这样就不需要一个几乎总是在睡觉的主循环,而且在必须完成某些事情时通常会晚睡半个觉。@MSalters+1我同意自雇线程速度更快,但需要更多的经验才能以线程安全的方式正确编写这些线程,以完成更长的作业,如每个文件夹几秒钟,以及围绕操作系统调度粒度的主循环睡眠浪费不是很严重,但需要在特定的实现中进行度量,以确保线程安全(如果很小的话):只需在打开的任务列表上添加一个互斥对象。读取和处理单个图像非常复杂,互斥锁不会引起明显的争用。如果争用是一个问题,请在N个受N个互斥锁保护的子列表中分解任务列表?你一定是在开玩笑。对于空闲线程来说,自己完成工作要容易得多。那是
It’很快,这样就不需要一个几乎总是在睡觉的主循环,而且在必须完成某些事情时通常会晚睡半个觉。@MSalters+1我同意自雇线程速度更快,但需要更多的经验才能以线程安全的方式正确编写这些线程,以完成更长的作业,如每个文件夹几秒钟,以及围绕操作系统调度粒度的主循环睡眠浪费不是很严重,但需要在特定的实现中进行度量,以确保线程安全(如果很小的话):只需在打开的任务列表上添加一个互斥对象。读取和处理单个图像非常复杂,互斥锁不会引起明显的争用。如果争用是一个问题,请在N个子列表中分解任务列表,这些子列表由N个互斥体保护