網(wǎng)站域名被做網(wǎng)站的公司擅自更改寧波優(yōu)化網(wǎng)站哪家好
最近要批量解壓歸檔文件和壓縮包,所以就想能不能并行執(zhí)行這些工作。因?yàn)?code>tar自身不支持并行解壓,但是像make
卻可以支持生成一些文件,所以我才有了這種想法。
方法有兩種,第一種不用安裝任何軟件或工具,直接bash
或其他 Shell 中就可以使用;第二種需要安裝 GNU parallel 這個(gè)工具來(lái)進(jìn)行。二者在使用上都很簡(jiǎn)單,但是后者更人性化(應(yīng)該可以用這個(gè)詞來(lái)形容)一些。最后還介紹了一種比較奇特的方法,是無(wú)意中看到的,雖然沒啥用但是有點(diǎn)意思。
直接在命令最后使用&
這個(gè)方法需要在命令最后使用&
,也就是將這個(gè)命令放入后臺(tái)執(zhí)行。如下是并行解壓提取當(dāng)前文件夾下所有的歸檔文件的方法:
for tarfile in *.tar; do tar xvf $tarfile &
done;
可以看到這個(gè)方法可以說(shuō)是非常簡(jiǎn)單了,但是最大的問(wèn)題就是它會(huì)給每個(gè)歸檔文件創(chuàng)建一個(gè)進(jìn)程,并不會(huì)自動(dòng)根據(jù)設(shè)備的線程數(shù)而創(chuàng)建合適數(shù)量的進(jìn)程(tar
由于需要大量 I/O,所以也無(wú)法維持高 CPU 使用率),如下:
如果是小數(shù)量的解壓提取可能沒什么問(wèn)題,但是如果特別大數(shù)量的歸檔文件解壓提取,那么可能會(huì)造成調(diào)度損耗過(guò)大。如果需要根據(jù)實(shí)際線程數(shù)量生成,那么就復(fù)雜多了。
使用GNU parallel
這個(gè)工具很好用,不光可以設(shè)置最大并行任務(wù)數(shù)量,還可以通過(guò)--bar
選項(xiàng)顯示當(dāng)前總進(jìn)度如何。使用方法如下(還是解壓提取一堆歸檔文件):
parallel tar xvf ::: *.tar
這種方法但是像time
,將需要并行化的命令放到parallel
后面即可,而不同命令之間不同的地方(參數(shù)部分)使用:::
標(biāo)注出來(lái)。
而且相比上一種方法,默認(rèn)情況下最多只會(huì)創(chuàng)建 CPU 的線程數(shù)的進(jìn)程,而不是一次性全部生成。如下:
可以看到一開始只生成了線程數(shù)量的進(jìn)程,也就是8
個(gè)進(jìn)程。如果想手動(dòng)設(shè)定最大并行進(jìn)程數(shù)量,那么使用-j 數(shù)量
即可(和make
的-j
選項(xiàng)一樣,有沒有空格都行)。
管道傳遞參數(shù)
上面是直接可以獲取參數(shù)的情況,并不存在不同程序之間通過(guò)管道(pipe)傳遞信息的情況。那么面對(duì)這種情況該怎么辦呢?
使用{}
在下一個(gè)程序的參數(shù)部分,作為即將傳遞的參數(shù)字符串的占位符,而且parallel
也要使用在下一個(gè)程序前面。需要注意的是:傳遞的參數(shù)是分散開傳遞的。比如說(shuō)一個(gè)多行字符串"1234\n1234\n1234"
會(huì)被傳遞成三個(gè)單行字符串"1234","1234","1234"
。
假設(shè)一個(gè)文本文件中,每一行都是一個(gè)地址,我們想并行下載所有鏈接的文件,那么可以使用:
cat abc.txt | parallel wget {}
但是面對(duì)比如說(shuō)使用grep
批量查詢abc.txt
中含有abc
的行有哪些,如果還使用上面這樣的傳遞,由于是分散開傳遞的,那么這個(gè)單獨(dú)的字符串會(huì)被當(dāng)作文件名:
cat abc.txt | parallel grep abc {}
結(jié)果如下:
$ cat abc.txt | parallel grep abc {}
grep: bfjksa: No such file or directory
grep: afhjha,fsj: No such file or directory
grep: abcshjagf: No such file or directory
grep: a;hfahabc: No such file or directory
grep: ahsfhmabc: No such file or directory
在這種就不要使用并行化,因?yàn)樽x取硬盤上的文件實(shí)際上是串行的,對(duì)單個(gè)或多個(gè)文件使用并行讀取或?qū)懭霂缀醪粫?huì)有任何性能提升,有時(shí)甚至還會(huì)降低(跳來(lái)跳去比順序讀取當(dāng)然慢了)。
比如說(shuō)官方有個(gè)例子是查找當(dāng)前目錄下所有文件中含有某一字符串的行,這里我查找main
這個(gè)字符串:
$ time find . -type f | parallel grep -H -n main {}
./mem_disk_speedtest_in_C/.git/config:10:[branch "main"]
...real 0m26.651s
user 0m3.351s
sys 0m1.030s
而不使用 GNU Parallel 的命令為(并不是直接刪除parallel
部分就行了,需要做出一些調(diào)整):
$ time grep -H -n main $(find . -type f)
./mem_disk_speedtest_in_C/.git/config:10:[branch "main"]
...real 0m22.247s
user 0m3.204s
sys 0m0.809s
可以看到慢了 18%。這是比較壞的情況,一般情況下,用不用 GNU Parallel 速度都沒什么變化。
更多選項(xiàng)請(qǐng)見官方文檔:GNU Parallel Tutorial
二者的速度區(qū)別
實(shí)際測(cè)試上,直接在命令最后使用&
要比使用GNU parallel慢一些(應(yīng)該就是因?yàn)檎{(diào)度損耗了一部份性能),如下:
方法 | 運(yùn)行時(shí)間(秒) |
---|---|
串行 | 237.9 |
& | 152.1 |
GNU parallel | 121.3 |
但是由于這里使用的 CPU 緩存較少,所以解壓速度也沒有 8 倍的提升,但是提升一倍也是不錯(cuò)的了。
擴(kuò)展
正如開頭所說(shuō),make
是可以并行生成一些文件,而且可以通過(guò)-j
選項(xiàng)設(shè)置最大并行任務(wù)數(shù)量。我們也可以利用這點(diǎn)來(lái)解壓提取文件,但這并不是一個(gè)正經(jīng)的辦法,僅限于開拓眼界,因?yàn)橛悬c(diǎn)“脫褲子放屁”的感覺(因?yàn)樯?code>Makefile中的target
部分需要使用CMake
或者Bash
來(lái)自動(dòng)生成),正經(jīng)使用的時(shí)候還是不要使用這種方法。
這個(gè)方法是我在 Running commands in parallel with a limit of simultaneous number of commands - superuser 中看到的,進(jìn)行了一些嘗試,可以說(shuō)除了奇特毫無(wú)優(yōu)點(diǎn)(通用性比不過(guò)&
,易用性比不過(guò) GNU Parallel),所以不推薦使用。
參考資料
Parallelize a Bash FOR Loop - Unix StackExchange
Can I use pipe output as a shell script argument? - superuser stackexchange