2018年1月8日星期一

15分钟神器gnu parallel 入门观止

WHY

使用gnu parallel的目的只要一個,就是為了快!
安裝快
(wget -O - pi.dk/3 || curl pi.dk/3/) | bash
作者說10秒裝好。在國內實際情況可能不夠。但是也不用太久。其實就是一個1萬多行perl單檔案指令碼或直譯式程式(是的,你沒看錯,所有模組都在這個檔案裡,這是一個特色~)。我之後都是寫fabric指令碼或直譯式程式直接拷貝到各個節點機。再chmod一下執行許可權。

然後是執行快,它將你的程序並行利用系統的多核執行:
上圖:
Paste_Image.png
grep 一個 1G 大小的log。
使用parallel ,和不使用parallel直接grep。結果顯而易見,相差 20 倍。這比用啥 ack,ag優化效果明顯多了。
備註:這是在一個48 核伺服器上執行的結果。

HOW

最簡單的方法就是類比xargs。在xargs裡面有一個參數 -P,可以利用多核。
舉個例子:
$ time echo {1..5} |xargs -n 1  sleep

real    0m15.005s
user    0m0.000s
sys    0m0.000s
這一條xargs把每個echo的數作為參數傳給sleep ,所以一共sleep了 1+2+3+4+5=15秒。
如果使用 -P 參數分給5個核,每個核各sleep 1,2,3,4,5秒,所以執行完之後總共sleep的5秒。
$ time echo {1..5} |xargs -n 1 -P 5 sleep

real    0m5.003s
user    0m0.000s
sys    0m0.000s
鋪墊結束。一般情況下,parallel的第一種模式,就是替換掉 xargs -P.
比如壓縮一下所有的html檔案。
find . -name '*.html' | parallel gzip --best

傳參數模式

第一種模式是利用 parallel傳參數。管道前面進來的作為參數傳給後面的命令,並行執行
比如
huang$ seq 5 | parallel echo pre_placehoder_{}
pre_placehoder_1
pre_placehoder_2
pre_placehoder_3
pre_placehoder_4
pre_placehoder_5
其中{}是佔位符,用來佔位傳入參數的位置。
在雲計算操作中,經常有批量操作,比如建立10個雲硬碟
seq 10 | parallel  cinder create 10 --display-name test_{}
建立50個雲主機
seq 50 | parallel nova boot --image    image_id  --flavor 1 --availability-zone  az_id   --nic vnetwork=private   --vnc-password 000000  vm-test_{}
批量刪除雲主機
nova list | grep some_pattern| awk '{print $2}' | parallel nova delete

改寫 FOR LOOP

可以看到,我其實是把很多需要寫迴圈的地方用parallel替換了,順帶享受了並行帶來的快捷。
這個道理是這樣的,在進行for迴圈的時候,是最有可能並行化的,因為被放在迴圈中的各個對象是上下文無關的。
普世抽象,shell的迴圈:
  (for x in `cat list` ; do
    do_something $x
  done) | process_output
可以直接寫成
 cat list | parallel do_something | process_output
如果loop 裡面內容太多了
 (for x in `cat list` ; do
    do_something $x
    [... 100 lines that do something with $x ...]
  done) | process_output
那麼最好寫成一個指令碼或直譯式程式
  doit() {
    x=$1
    do_something $x
    [... 100 lines that do something with $x ...]
  }
  export -f doit
  cat list | parallel doit
而且還能避免掉很多麻煩的轉義。

–PIPE模式

另一種模式就是 parallel --pipe
這時管道前面的不是作為參數,而是標準輸入傳給後面的命令
例如:
 cat my_large_log   |parallel --pipe grep pattern
如果不加 --pipe ,相當於 mylog中的每一行都變成 grep pattern line的命令展開了。而加入了--pipe,則和 cat mylog | grep pattern 沒有區別,只是分配到各個核上去執行了。
好了,基本概念就講完了!其他的都只是各個參數具體使用,比如到底用幾個核啊,place_holder的替換啊,各種花樣傳參數啊,並行執行但是保證結果順序輸出(-k),以及神奇的跨節點並行計算啊,看看man page就知道了。

BONUS

手邊有了一個轉換成並行的小工具,除了讓你日常執行快一點之外,還有一個好處,就是測並發
很多介面在並發操作下會出現一些bug,比如有一些判斷資料庫裡面沒有加鎖,是在代碼層面判斷的,結果並發請求下去,每個請求在到達伺服器的時候是判斷通過,一起寫了之後就超出限制了。之前寫for迴圈因為是串列執行的,並不會觸發這些問題。但是你要真正測並發的話,又要寫指令碼或直譯式程式,或者利用python的mulitiprocessing封裝一下。但我手邊有了parallel,又在bashrc裡面就加了以下兩個alias
alias p='parallel'
alias pp='parallel --pipe -k'
這樣製造並發太方便了,只需要管道後面加個p , 我就時時刻刻可以製造並發來觀察響應。
舉個例子
seq 50 | p -n0 -q  curl 'example.com'
以你核的個數並發請求。-n0的意思是seq輸出不作為參數傳給後面的命令。
地点: 中国江苏省苏州市