一种基于类UNIX系统进行高效并行执行的方法

本文介绍一种利用GNU parallel进行高并发运行计算密集型并行任务的方案。上次用这软件是在写爬虫批量下载漫画…没想到科研中也遇到了类似的问题。遂记录下来与大家分享。

系统要求

首先,Windows系统是不可以的。从官网上看,GNU parallel没有win32 build。或者,我正在找替代或者解决方案。

已知的尝试是在git-bash里安装parallel,但是安装是能安装,却不能正常工作。在git-bash里安装parallel参考了stackoverflow一篇回答

我在git-bash里运行了以下指令成功安装parallel,虽然安装过程中不停地报错。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$ (wget -O - pi.dk/3 || lynx -source pi.dk/3 || curl pi.dk/3/ || \
fetch -o - http://pi.dk/3 ) > install.sh
$ bash ./install.sh
$ parallel --version
GNU parallel 20211222
Copyright (C) 2007-2021 Ole Tange, http://ole.tange.dk and Free Software
Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
GNU parallel comes with no warranty.

Web site: https://www.gnu.org/software/parallel

When using programs that use GNU Parallel to process data for publication
please cite as described in 'parallel --citation'.

问题描述

图1: 任务节点依赖关系图

本文将要处理的问题具有1所示结构,即某一个步骤完全由有限个并行执行的任务组成。这些并行任务之间并没有任何依赖关系,但下一步依赖于所有这些任务,即当这些并行任务都结束时,下一项任务才能开始。

以下载一话漫画为例。

假设某连载漫画的第5话有20页图片,在某漫画网站上,该漫画信息页面的路径为/comic/:comic_id,获取一张图片的路径为/gallery/:gallery_id/:pic_file,其中:pic_file是以.png或者.jpg结尾的图片名。

在漫画信息页面上显示有comic_idgallery_id的对应关系,每一个comic_id有且仅有一个gallery_id与之对应。 :pic_file的列表和次序可以通过带有:gallery_id请求某API获取。

好吧,这些都不重要,不要乱爬漫画站。

解决方案

可以看到,上述任务可以使用1所示结构来描述,获取comic_idgallery_id以及:pic_file列表可以看作是并行任务集合的前置准备。所有图片的下载可以是高并发的,彼此之间并没有什么依赖关系。任务完成时所有图片都已经下载完毕。

那么,如何使用GNU parallel写出最快速的漫画爬虫呢? shell脚本的部分内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# comic_id := user defined number
# gallery_id := get gallery_id with comic_id
# pic_list := Array of pic_files fetched with gallery_id

# download_pic could download a picture at once.
#
# @param $pic filename of picture
# @param $order order of picture
function download_pic() {
local pic=$1
local order=$2

# Suffix of the picture, e.g., 'png', 'jpg'.
local suffix=${pic##*\.}

curl -s $PIC_API/gallery/$pic -o "${order}.${suffix}"
}

# Export the function so that parallel could load it.
export -f download_pic

parallel -j 4 \
download_pic \
::: ${pic_list[@]}

其中,:::非常有特点,既不是一个:也不是两个:

-j选项指定使用多少个CPU核心参与并行任务的执行,未指定则默认全部(物理核数)。 download_pic为要并行执行的任务,:::后边是任务参数。

当有多个:::时会自动枚举全部选项,比如,parallel echo ::: 1 2 ::: a b c的一种可能的输出结果为:

1
2
3
4
5
6
7
$ parallel echo ::: 1 2 ::: a b c
1 a
1 b
1 c
2 a
2 b
2 c

P.S. 为了最高的效率,parallel不会很及时地进行输出,而是先缓存起来。因此命令行或者文件输出和任务执行情况不同步。

注意事项

正如系统要求parallel的输出中所提到的,当论文数据处理用到了该工具的时候,应该按照parallel的作者给出的形式[1]进行引用。

1
2
3
4
5
6
7
8
$ parallel --citation
# ...
Academic tradition requires you to cite works you base your article on.
When using programs that use GNU Parallel to process data for publication
please cite:

O. Tange (2011): GNU Parallel - The Command-Line Power Tool,
;login: The USENIX Magazine, February 2011:42-47.

Fin.

参考文献

[1]
O. Tange, GNU parallel - the command-line power tool,” The USENIX Magazine, vol. 36, no. 1, pp. 42–47, 2011.
0条搜索结果。