Самый эффективный способ выполнять полмиллиона математических операций в час

Самый эффективный способ выполнять полмиллиона математических операций в час

Поэтому для личного и учебного опыта я начал создавать базу данных погодных данных. Я использую wgrib2 для разбора данных и импорта в MySQL. Поскольку данные отформатированы в разных единицах - компоненты ветра "U" и "V", кельвины и т. д. Мне нужно преобразовать их в узлы скорости ветра, радиусы ветра и температуру в градусах Цельсия и т. д.

Я создал bash for loop, который перебирает все значения данных, но он довольно неэффективен, и я уверен, что есть лучшие способы сделать это. Он во многом опирается на awk... и занимает 15-17 минут, чтобы разобрать данные для примерно 1150 станций, каждая станция имеет таблицу с идентичной структурой и 160 столбцами в базе данных MySQL.

Массивы bash, которые я настроил для TK (температура по Кельвину), RH (влажность) и т. д., имеют значения 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

Как вы можете видеть, очевидная проблема в том, что он делает около 1150 * 160 вызовов awk... так что, вероятно, передача основных массивов в awk и создание awk только один раз за цикл (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, а не с нуля.

Поскольку вы назначаете переменную, вы на самом деле не используете данные STDIN ни для чего, кроме значения NF (но вы передали только один элемент). Вместо использования 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

Связанный контент