Bash

如何為此用途優化 GNU 並行?

  • January 29, 2020

我出於無聊創建了這個腳本,其唯一目的是使用/測試 GNU 並行,所以我知道它不是特別有用或優化,但我有一個腳本可以計算所有質數,直到 n:

#!/usr/bin/env bash

isprime () {
   local n=$1
   ((n==1)) && return 1
   for ((i=2;i<n;i++)); do
       if ((n%i==0)); then
           return 1
       fi
   done
   printf '%d\n' "$n"
}

for ((f=1;f<=$1;f++)); do
   isprime "$f"
done

使用循環執行時:

$ time ./script.sh 5000 >/dev/null

real    0m28.875s
user    0m38.818s
sys     0m29.628s

我希望用 GNU 並行替換 for 循環會使它執行得更快,但這不是我的經驗。平均而言,它只快了大約 1 秒:

#!/usr/bin/env bash

isprime () {
   local n=$1
   ((n==1)) && return 1
   for ((i=2;i<n;i++)); do
       if ((n%i==0)); then
           return 1
       fi
   done
   printf '%d\n' "$n"
}

export -f isprime

seq 1 $1 | parallel -j 20 -N 1 isprime {}

並行執行:

$ time ./script.sh 5000 >/dev/null

real    0m27.655s
user    0m38.145s
sys     0m28.774s

我對優化isprime()函式並不感興趣,我只是想知道是否可以做些什麼來優化 GNU 並行?

在我的測試seq中實際上執行得比執行時更快,for ((i=1...))所以我認為這與執行時沒有太大關係


有趣的是,如果我將 for 循環修改為:

for ((f=1;f<=$1;f++)); do
   isprime "$f" &
done | sort -n

它執行得更快:

$ time ./script.sh 5000 >/dev/null

real    0m5.995s
user    0m33.229s
sys     0m6.382s

GNU Parallel 每個作業花費 2-10 毫秒的成本。可以通過使用降低一點-u,但這意味著您可能會從不同的工作中獲得混合輸出。

如果您的工作在 ms 範圍內並且執行時很重要,那麼 GNU Parallel 並不理想:成本通常太大。

您可以通過執行多個 GNU Parallels 將成本分散到多個核心:

seq 5000 | parallel --pipe --round-robin -N100 parallel isprime

您仍然需要支付成本,但現在您至少有更多的核心需要支付。

更好的方法是進行更改isprime,使其需要多個輸入,從而需要更長的時間來執行:

isprime() {
 _isprime () {
     local n=$1
     ((n==1)) && return 1
     for ((i=2;i<n;i++)); do
         if ((n%i==0)); then
             return 1
         fi
     done
     printf '%d\n' "$n"
 }
 for t in "$@"; do
   _isprime $t
 done
}
export -f isprime

seq 5000 | parallel -X isprime
# If you do not care about order, this is faster because higher numbers always take more time
seq 5000 | parallel --shuf -X isprime

引用自:https://unix.stackexchange.com/questions/564051