この目的のためにGNU並列性をどのように最適化できますか?

この目的のためにGNU並列性をどのように最適化できますか?

私はGNUパラレルを使用/テストするために退屈でこのスクリプトを作成しました。

#!/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

私はforループをGNU並列処理に置き換えると作業速度がはるかに速くなると期待していましたが、私の経験はそうではありませんでした。平均して約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

ベストアンサー1

GNU Parallelには、ジョブあたり2〜10ミリ秒のオーバーヘッドがあります。を使用すると少し下げることができますが、-uこれは他のジョブの出力を混在させることができます。

タスクがミリ秒の範囲内にあり、ランタイムが重要である場合、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

おすすめ記事