1 時間あたり 50 万回の計算を実行する最も効率的な方法

1 時間あたり 50 万回の計算を実行する最も効率的な方法

そこで、個人的な理由と学習経験から、気象データのデータベース化を始めました。データを解析し、MySQL にインポートするために wgrib2 を使用しています。データは、風の「U」と「V」成分、ケルビンなど、さまざまな単位でフォーマットされているため、風速ノット、風度半径、温度摂氏度などに変換する必要があります。

すべてのデータ値をループする bash for ループを作成しましたが、これはかなり非効率的で、もっと良い方法があるはずです。awk に大きく依存しており、約 1150 ステーションのデータを解析するのに 15 ~ 17 分かかります。各ステーションには、MySQL データベース内に 160 列のまったく同じ構造のテーブルがあります。

TK (温度ケルビン)、RH (湿度) などに設定した bash 配列には、1000、975、950、925 などの値があり、最小は 100 ミリバールまであります。

for thKey in ${!TK[@]}
do
    thRH=${RH[$thKey]}
    thTK=${TK[$thKey]}
    thTC=$(echo -| awk -v tk="$thTK" '{printf "%.1f\n", tk-273.15}')
    thWU=${WU[$thKey]}
    thWV=${WV[$thKey]}
    thTD=$(echo -| awk -v tc="$thTC" -v rh="$thRH" '{printf "%.1f\n", tc-(100-rh)/5}')
    thWD=$(echo -| awk -v wu="$thWU" -v wv="$thWV" '{printf "%.0f\n", 57.29578*(atan2(wu, wv))+180}')
    thWS=$(echo -| awk -v wu="$thWU" -v wv="$thWV" '{printf "%.1f\n", sqrt(wu*wu+wv*wv)*1.944}')
    sed -i '/\/station_id/a <'"$thKey"'T>'"$thTC"'<\/'"$thKey"'T><'"$thKey"'D>'"$thTD"'<\/'"$thKey"'D><'"$thKey"'WD>'"$thWD"'<\/'"$thKey"'WD><'"$thKey"'WS>'"$thWS"'<\/'"$thKey"'WS>' $xmlOut
done

これを見るとわかるように、明らかな問題は、awk を約 1150 * 160 回呼び出すことです... そのため、マスター配列を awk に渡し、ループごとに awk を 1 回だけ生成する (現在行っていることの 1/160!) 方が効率的でしょう。 しかし、この練習では awk 構文を正しく理解できないようです...

awk --version

GNU Awk 4.1.3、API: 1.1 (GNU MPFR 3.1.4、GNU MP 6.1.0)

次に例を示します。

TK=(325,350,231,655)
echo -| awk -v tk="${TK[*]}" '{split(tk,tka,/ /)} { for (i=0; i<=NF; i++) { printf "%.1f\n", tka[i]-273.15 } } '

-273.1 51.9

^ これは正しくありません。配列には 4 つの値があり、2 つだけを返すべきではありません。

echo -| awk -v tk="${TK[*]}" '{split(tk,tka,/ /)} { for (i=0; i<=length(tka); i++) { printf "%.1f\n", tka[i]-273.15 } } '

^ これにより無限ループが発生します。

何かアイデアはありますか? Perl を少し学んで、これらすべてを Perl スクリプトに渡すのはいかがでしょうか?

答え1

個人的には、はい、すべてを Perl で実行します。:-)

TK=(325,350,231,655)

おっと。気をつけてください。要素としてコンマ区切りの文字列を持つ単一要素配列を作成しました。

echo -| awk -v tk="${TK[*]}" '{split(tk,tka,/ /)} { for (i=0; i<=NF; i++) { printf "%.1f\n", tka[i]-273.15 } } '

awk配列はゼロではなく 1 から始まります。

変数を割り当てるため、実際には NF 値以外の目的で STDIN データを使用することはありません (ただし、渡された要素は 1 つだけです)。NF を使用する代わりに、split明示的に結果をカウントしましょう。次のようになります。

$ TK=(325 350 231 655)
$ echo - | awk -v tk="${TK[*]}" '{fields=split(tk,tka,/ /)} { for (i=1; i<=fields; i++) { printf "%.1f\n", tka[i]-273.15 } } '
51.9
76.9
-42.1
381.9

dave_thompson_085 が述べているように、データを STDIN 経由で送信するのではなく、変数に直接割り当てることで、余分な作業を行っていることになります。より一般的なのは、おそらく次のようなものでしょう。

$ echo ${TK[*]} | awk '{for (i=1; i<=NF; i++) { printf "%.1f\n", $i-273.15 } } '
51.9
76.9
-42.1
381.9

解決策を始めたい場合は、次の手順に従ってくださいperl

$ echo ${TK[*]} | perl -lane 'for $item (@F) {print $item-273.15}'
51.85
76.85
-42.15
381.85

関連情報