【问题标题】:How can I use the parallel command to exploit multi-core parallelism on my MacBook?如何使用并行命令在 MacBook 上利用多核并行性?
【发布时间】:2020-11-15 23:04:06
【问题描述】:

我经常在 Linux 和 macOS 上使用find 命令。我刚刚发现了parallel 命令,如果可能的话,我想将它与find 命令结合起来,因为find 命令在我们将特定文件搜索到大目录时需要很长时间。

我已搜索过此信息,但结果不够准确。似乎有很多可能的语法,但我不知道哪个是相关的。

如何将parallel 命令与find 命令(或任何其他命令)结合使用,以便从我的 MacBook 上的所有 16 个内核中受益?

更新

@OleTange,我想我找到了我感兴趣的那种命令。

所以,要了解更多关于这些命令的信息,我想知道字符{}:::在以下命令中的用处:

parallel -j8 find {} ::: *

1) 这些字符是强制性的吗?

2) 如何插入find 命令的经典选项,例如-type f-name '*.txt

3) 目前我在.zshrc 中定义了函数:

ff () {
    find $1 -type f -iname $2 2> /dev/null
}

如何用固定数量的作业做等价物(我也可以将其设置为 shell 参数)?

【问题讨论】:

  • 您的问题太模糊,无法回答。请添加您需要使用的命令类型的详细信息...您是在寻找具有特定名称或具有特定内容的文件吗?您的搜索可能完全受 I/O 限制,因此投入更多内核也无济于事。
  • GNU parallel 几乎不再是新的了。请注意,尽管语法发生了变化,许多年前的热心示例不再有效。
  • @tripleee 语法发生了什么变化?
  • @OleTange Tollef Fog Heen 的原始parallel 使用破折号,而后来的版本将使用三个冒号或类似的东西。 Ubuntu 中有一个错误,文档使用新语法,但默认配置已启用旧语法。但这是很多年前的事了。看起来像stackoverflow.com/questions/16448887/…(虽然我猜你可能比我更了解背后的故事,或者可以猜到)。
  • 当您提供 50 点赏金时,我想您很想得到一个好的答案。您可以通过回复我上面的评论来帮助自己,因为您正在执行的搜索类型可能会产生巨大的影响。

标签: linux macos command-line find gnu-parallel


【解决方案1】:

您似乎希望能够在 ma​​cOS 下的大目录中快速定位文件。我认为该工作的正确工具是mdfind

我在我的主目录下创建了一个包含 10,000,000 个文件的层次结构,所有文件都具有类似于 UUID 的唯一名称,例如80104d18-74c9-4803-af51-9162856bf90d。然后我试图找到一个:

mdfind -onlyin ~ -name 80104d18-74c9-4803-af51-9162856bf90d

结果是瞬时的,而且速度太快,无法测量时间,所以我进行了 100 次查找,用时不到 20 秒,因此平均查找需要 0.2 秒。


如果您真的想找到 100 个文件,可以将它们分组到一个搜索中,如下所示:

mdfind -onlyin ~ 'kMDItemDisplayName==ffff4bbd-897d-4768-99c9-d8434d873bd8 || kMDItemDisplayName==800e8b37-1f22-4c7b-ba5c-f1d1040ac736 || kMDItemDisplayName==800e8b37-1f22-4c7b-ba5c-f1d1040ac736'

而且它执行得更快。


如果你只知道部分文件名,你可以使用:

mdfind -onlyin ~ "kMDItemDisplayName = '*cdd90b5ef351*'"
/Users/mark/StackOverflow/MassiveDirectory/800f0058-4021-4f2d-8f5c-cdd90b5ef351

您还可以在搜索中使用创建日期、文件类型、作者、视频时长或标签。例如,您可以像这样查找名称中包含“25DD954D73AF”的所有 PNG 图片:

mdfind -onlyin ~ "kMDItemKind = 'PNG image' && kMDItemDisplayName = '*25DD954D73AF*'"
/Users/mark/StackOverflow/MassiveDirectory/9A91A1C4-C8BF-467E-954E-25DD954D73AF.png

如果您想知道可以搜索哪些字段,请获取您希望能够查找的类型的文件,然后在其上运行mdls,您将看到 ma​​cOS 知道:

mdls SomeMusic.m4a
mdls SomeVideo.avi
mdls SomeMS-WordDocument.doc

更多示例here

另外,与locate 不同的是,不需要经常更新数据库。

【讨论】:

  • 我已经尝试过mdfind如果我可以说这是灾难性的。确实,索引的构建完全是一个黑盒子:top'htop看到的只是mdworker_shared一直在运行,我们不知道它们到底在做什么,数据库本身也完全被遮蔽了,研究结果是两次尝试一次发现的,所以我对这个工具感到失望。我也多次尝试从头开始重新索引数据库,以获得新的、完整的和完整的文件索引(实际上数据库不仅包含文件名)。
  • 但是有些问题仍然存在,因为有些文件找到了,有些没有,所以我想我会放弃mdfind除非我只有这个工具可用。
【解决方案2】:

正如其他人所写的那样,find 的 I/O 很重,而且很可能不受 CPU 的限制。

但根据您的磁盘,可能并行运行作业会更好。

NVMe 磁盘以在并行运行 4-8 个访问时性能最佳而著称。一些网络文件系统还可以更快地处理多个进程。

因此,某种程度的并行化可能是有意义的,但您确实必须进行测量才能确定。

find 与并行运行的 8 个作业并行化:

parallel -j8  find {} ::: *

如果您在一个有许多子目录的目录中,这最有效:然后将并行搜索每个子目录。否则这可能会更好:

parallel -j8  find {} ::: */*

基本相同的想法,但现在使用 dirs 的子目录。

如果您希望在找到结果后立即打印结果(而不是在 find 完成后)使用 --line-buffer(或 --lb):

parallel --lb -j8  find {} ::: */*

要了解 GNU Parallel,请花 20 分钟阅读 https://doi.org/10.5281/zenodo.1146014 的第 1+2 章并打印备忘单:https://www.gnu.org/software/parallel/parallel_cheat.pdf

你的命令行会感谢你的。

【讨论】:

  • 感谢您的快速回复。这就是我想使用的那种命令。我将在我的原始帖子中放置一个 UPDATE 1 以更好地探索这些命令,尤其是您提供的不同命令中::: 字符的功能,以及如何插入find 的经典选项.请通知我,问候
  • @youpilat13 ::: 和 {} 在第 2 章中解释:doi.org/10.5281/zenodo.1146014
【解决方案3】:

当您的工作是CPU bound(CPU 完成工作,外围设备大多处于空闲状态)时,并行处理是有意义的,但在这里,您正试图提高I/O bound 任务的性能(CPU 是大部分是空闲的,等待一个繁忙的外围设备)。在这种情况下,增加并行性只会增加拥塞,因为多个任务将争夺它们之间已经匮乏的 I/O 带宽。

在 macOS 上,系统已经为您的所有数据建立了索引(包括文字处理文档、PDF、电子邮件等的内容);右上角的菜单栏上有一个友好的放大镜,您可以在其中访问更快、更通用的搜索,称为 Spotlight。 (虽然我同意find 的一些更复杂的控件丢失了;而且“用户友好”的设计在猜到我想要什么并且猜错时妨碍了我。)

一些 Linux 发行版提供了类似的功能;我希望这将成为当今任何带有 GUI 的东西的规范,尽管系统之间的细节会有所不同。

在任何类 Unix 系统上更传统的解决方案是 locate 命令,它执行类似但更有限的任务;它会在文件名上创建一个(非常活泼的)索引,所以你可以说

locate fnord

快速获取名称与fnord 匹配的每个文件。该索引只是昨晚运行find 的结果的副本(或者您安排后端运行)。该命令已安装在 macOS 上,但如果要使用它,则必须启用后端。 (只需运行 locate locate 以获取更多说明。)

如果您发现自己经常寻找具有特定权限集和特定所有者的文件,您可以自己构建类似的东西,例如(这些不是locate 记录的功能);只需运行每晚(或每小时等)find,它将这些功能收集到数据库中——甚至只是一个文本文件——然后您几乎可以立即搜索。

对于并行运行作业,您实际上并不需要 GNU parallel,尽管它确实为许多用例提供了许多便利和增强功能;你已经有xargs -P。 (macOS 上源自 BSD 的 xargs 比 GNU xargs 更受限制,这是您在许多 Linux 上都能找到的;但它确实有 -P 选项。)

例如,下面是如何使用xargs -P 运行八个并行的find 实例:

printf '%s\n' */ | xargs -I {} -P 8 find {} -name '*.ogg'

(假设通配符与包含单引号或换行符或其他恶作剧的目录不匹配;GNU xargs 具有 -0 选项来修复大量这样的极端情况;那么您将使用 @ 987654343@ 作为printf 的格式字符串。)


正如the parallel documentation 很容易解释的那样,它的一般语法是

parallel -options command ...

其中{} 将替换为当前输入行(如果缺少,它将隐式添加到command ... 的末尾)并且(显然是可选的)::: 特殊标记允许您指定一个命令行上的输入源,而不是标准输入。

这些特殊标记之外的任何内容都是逐字传递的,因此您只需逐字指定find 选项即可。

parallel -j8 find {} -type f -name '*.ogg' ::: */

我不会说 zsh,但重构为常规 POSIX sh 你的函数可能类似于

ff () {
    parallel -j8 find {} -type f -iname "$2" ::: "$1"
}

虽然我可能会切换参数,以便您可以指定名称模式和要搜索的文件列表,à la grep

ff () {
    # "local" is not POSIX but works in many sh versions
    local pat=$1
    shift
    parallel -j8 find {} -type f -iname "$pat" ::: "$@"
}

但同样,旋转磁盘以查找已编入索引的内容可能是您应该停止做的事情,而不是促进。

【讨论】:

    【解决方案4】:

    只需在每个一级路径分别使用后台运行

    在下面的示例中将创建 12 个子目录分析

     $ for i in [A-Z]*/ ; do find "$i" -name "*.ogg" & >> logfile ; done 
    [1] 16945
    [2] 16946
    [3] 16947
    # many lines
    [1]   Done                    find "$i" -name "*.ogg"
    [2]   Done                    find "$i" -name "*.ogg"
    #many lines
    [11]   Done                    find "$i" -name "*.ogg"
    [12]   Done                    find "$i" -name "*.ogg"
     $
    

    这样做会创建许多查找进程,系统将像其他任何内核一样在不同的内核上分派。

    注意 1: 这样做看起来有点像猪,但它确实有效..

    注意 2:find 命令本身并没有占用 cpus/cores 这 99% 的用例只是无用,因为查找过程将花费时间等待用于来自磁盘的 I/O。然后使用并行或类似的命令将不起作用*

    【讨论】:

    • 我修复了 shell 引用,但您可能还想更改重定向,尽管我不完全确定您希望它完成什么。可能你想在这里done >logfile
    • 如果您希望Done 输出显示它完成了哪个"$i",您必须添加一个丑陋的eval。这总是会让你从安全意识中得到一些尖叫,但如果你首先检查你没有来自通配符的令人惊讶的匹配,它应该是可以接受的。
    • 通配符可能根本不匹配任何东西,当然不能保证完全匹配十二个。我猜你碰巧有 12 个名称以大写字母开头的目录?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-04-29
    • 2020-09-15
    • 1970-01-01
    • 2012-09-02
    • 2012-06-16
    • 2011-07-17
    • 1970-01-01
    相关资源
    最近更新 更多