시간당 500,000개의 수학 연산을 수행하는 가장 효율적인 방법

시간당 500,000개의 수학 연산을 수행하는 가장 효율적인 방법

그래서 개인적, 학습적 경험상의 이유로 날씨 데이터를 데이터베이스화하기 시작했습니다. 저는 wgrib2를 사용하여 데이터를 구문 분석하고 이를 MySQL로 가져옵니다. 데이터가 바람 "U" 및 "V" 구성요소, 켈빈 등 다양한 단위 형식을 갖고 있기 때문에 이를 풍속(노트), 풍도 반경, 온도(섭씨) 등으로 변환해야 합니다.

모든 데이터 값을 반복하기 위해 bash for 루프를 만들었지만 이는 매우 비효율적이며 이를 수행하는 더 좋은 방법이 있다고 확신합니다. 이는 awk에 크게 의존하며 약 1150개 스테이션의 데이터를 구문 분석하는 데 15~17분이 소요됩니다. 각 스테이션에는 160개 열이 있는 MySQL 데이터베이스에 동일하게 구조화된 테이블이 있습니다.

TK(켈빈 온도), RH(습도) 등에 대해 설정한 bash 배열의 값은 1000, 975, 950, 925... 등 최대 100mbar입니다.

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를 생성할 수 있습니다(지금 내가 하고 있는 작업 중 하나/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배열은 0이 아닌 1부터 시작합니다.

변수를 할당했기 때문에 실제로 NF 값 외에는 STDIN 데이터를 사용하지 않았습니다(그러나 하나의 요소만 전달했습니다). 우리는 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

관련 정보