Mejorar el rendimiento de comparar la columna de archivo a array en bash
Estoy comparando filas en una serie de archivos contra un array de flotadores en bash. Brevemente, los archivos en cuestión (../summarize_eigenvectors/"$xct"_/-5_1-sorted.txt) tienen la siguiente estructura
c v weight ik kx ky kz
1 1 0.00000 1 0.00000 0.00000 0.00000
1 1 0.00000 2 0.00000 0.04167 0.00000
1 1 0.00000 3 0.00000 0.08333 0.00000
y el array se genera a partir de ../vici_absdipole_noeh/v1c5.data, que tiene el siguiente formato:
kx ky kz ik ic iv is ec (eV) ev (eV) eig (eV) abs(dipole)^2 Re(dipole) Im(dipole)
0.00000 0.00044 0.00000 1 1 1 1 0.11713703E+01 -0.12426462E+01 0.24140165E+01 0.69913425E-04 0.81359347E-02 0.19287282E-02
0.00000 0.01883 0.00000 2 1 1 1 0.11760590E+01 -0.12490846E+01 0.24251436E+01 0.59405512E-04 -0.70114501E-03 0.76755396E-02
0.00000 0.03722 0.00000 3 1 1 1 0.11746489E+01 -0.12612625E+01 0.24359113E+01 0.37648401E-04 -0.46637404E-02 0.39872204E-02
0.00000 0.05561 0.00000 4 1 1 1 0.11868220E+01 -0.12787400E+01 0.24655620E+01 0.18552618E-04 -0.21585915E-02 0.37273450E-02
Lo que mi código hace es comparar los enteros en la cuarta columna del archivo v1c5.da contra mi matriz; si para una fila del archivo, el elemento de la cuarta columna está en el array "list", entonces los valores del índice de archivo, peso, ik, kx y ky en esa fila se hacen eco.
Aquí hay una muestra de mi código de trabajo, que compara 7485 archivos que tienen 1152 líneas cada uno a un conjunto de 1152 elementos
#!/bin/bash
#generates the array from information given in another file
for i in range $(seq 2 1153)
do
ik=$(awk -v i=$i 'NR==i''{ print$4 }' ../vici_absdipole_noeh/v1c5.data)
dp2=$(awk -v i=$i 'NR==i''{ print$11 }' ../vici_absdipole_noeh/v1c5.data)
dp2f=$(printf "%.8f" $dp2)
if (( $(echo "$dp2f > 6" |bc -l) )); then
list+=("$ik" )
fi
done
echo "xct ik weight kx ky" > v1c5-high_dp2_kpts.txt
task(){
echo working on $xct
for line in {1..1152};do
weight=$(awk -v line=$line 'NR==line''{ print$3 }' ../summarize_eigenvectors/"$xct"_*/*-5_1-sorted.txt)
ik=$(awk -v line=$line 'NR==line''{ print$4 }' ../summarize_eigenvectors/"$xct"_*/*-5_1-sorted.txt)
kx=$(awk -v line=$line 'NR==line''{ print$5 }' ../summarize_eigenvectors/"$xct"_*/*-5_1-sorted.txt)
ky=$(awk -v line=$line 'NR==line''{ print$6 }' ../summarize_eigenvectors/"$xct"_*/*-5_1-sorted.txt)
if [[ " ${list[@]} " =~ " ${ik} " ]]; then
echo "$xct $ik $weight $kx $ky" >> v1c5-high_dp2_kpts.txt
fi
done
}
for xct in {1..7485};do
((i=i%360)); ((i++==0)) && wait
task "$xct" &
done
wait
El código ha funcionado durante 8 horas más y sólo ha procesado 1700 archivos, que es bastante lento. ¿Hay algún obstáculo en este código que limite su rendimiento? Y si lo hay, ¿cómo puedo mejorarlo?
Estoy ejecutando esto en el nodo del centro de computación de alto rendimiento que tiene 24 núcleos por nodo, por lo que he utilizado la paralelización también para acelerar las cosas. Aparentemente esto todavía no es suficiente.
Pregunta hecha hace 3 años, 5 meses, 5 días - Por algoarchitect
3 Respuestas:
-
Tu
task
la función es realmente ineficiente. Rereads all the files 4 times each time through the loop, just to process one line. Puedes hacer todo el trabajo en unoawk
invocación.task(){ echo working on $xct cat ../summarize_eigenvectors/"$xct"_*/*-5_1-sorted.txt | awk -v list="${list[*]}" -v xct="$xct" ' BEGIN { split(list, list_array); # split string list into array list_array at whitespace delimiters for (i in list_array) list_hash[list_array[i]] = 1 # associative array with $list elements as keys } { weight=$3; ik=$4; kx=$5; ky=$6; if (ik in list_hash) printf("%s %s %s %s %s\n", xct, ik, weight, kx, ky) }' >> v1c5-high_dp2_kpts.txt }
Respondida el Dec 18, 2020 a las 02:47 - por hackharbinger
Votos positivos: 0 | Votos negativos: 0 -
Para mejorar el rendimiento de tu script en bash, te sugiero algunas optimizaciones que puedes aplicar:
- Reducir la cantidad de llamadas a `awk`: En tu script actual, estás llamando a `awk` varias veces dentro de los bucles para leer diferentes columnas en los archivos de datos. Esto puede ser ineficiente. Una alternativa más eficiente sería leer cada línea completa una vez en lugar de leer columnas separadas en múltiples llamadas a `awk`.
- Optimizar la creación del array `list`: En lugar de recorrer todo el archivo `v1c5.data` para crear el array `list`, puedes utilizar un enfoque más eficiente. Por ejemplo, podrías utilizar `awk` para filtrar las líneas que cumplen con tu condición directamente al principio y luego extraer el valor de la cuarta columna.
- Paralelización eficiente: Aunque estás utilizando la paralelización en tu script, es importante asegurarse de que estás distribuyendo adecuadamente la carga entre los núcleos disponibles. En tu caso, tienes 24 núcleos disponibles, por lo que podrías ajustar la cantidad de tareas que se están ejecutando en paralelo para aprovechar mejor los recursos.
Aquí tienes un ejemplo de cómo podrías optimizar tu script:
bash
!/bin/bash Obtener los valores de ik que cumplen con el criterio en v1c5.data
list=($(awk '$11 > 6 {print $4}' ../vici_absdipole_noeh/v1c5.data)) echo "xct ik weight kx ky" > v1c5-high_dp2_kpts.txtIterar sobre cada archivo en paralelo para mejorar rendimiento
parallel_process() { xct=$1 echo "working on $xct" for line in {1..1152}; do read -r weight ik kx ky <<< $(awk -v line=$line 'NR==line {print $3, $4, $5, $6}' ../summarize_eigenvectors/"$xct"_/-5_1-sorted.txt) if [[ " ${list[@]} " =~ " ${ik} " ]]; then echo "$xct $ik $weight $kx $ky" >> v1c5-high_dp2_kpts.txt fi done }Establecer el número de tareas en paralelo
export -f parallel_process export list export -f parallel_processIniciar procesos en paralelo
for xct in {1..7485}; do ((i=i%24)); ((i++==0)) && wait parallel_process "$xct" & done waitEste código realiza algunas optimizaciones, como reducir la cantidad de llamadas a
awk
, paralelizar el proceso de manera más eficiente y manejar los valores de ik directamente en el script en lugar de crear un array. Prueba este código para ver si mejora el rendimiento de tu script.Respondida el Dec 19, 2020 a las 02:49 - por Gpt
Votos positivos: 0 | Votos negativos: 0 -
Optimizaciones de rendimiento:
Paralelización: Si bien has utilizado la paralelización, podrías considerar aumentar el número de subprocesos utilizados para mejorar aún más el rendimiento.
Almacenamiento de la matriz en memoria: En lugar de buscar en la matriz cada vez, puedes cargarla en la memoria una vez al inicio del programa. Esto eliminaría la sobrecarga de la búsqueda de archivos en cada iteración. Uso de herramientas nativas: En lugar de utilizar
bc
para las comparaciones de coma flotante, puedes utilizar las herramientas nativas de bash para comparar números de coma flotante. Uso de arrays asociativos: En bash, los arrays asociativos son más eficientes que los arrays normales para buscar elementos. Podrías considerar utilizar un array asociativo para almacenar los elementos de la matriz.Código optimizado:
bash
!/bin/bash Generates the array from information given in another file
declare -A dp_array for i in $(seq 2 1153); do ik=$(awk -v i=$i 'NR==i' '{ print$4 }' ../vici_absdipole_noeh/v1c5.data) dp2=$(awk -v i=$i 'NR==i' '{ print$11 }' ../vici_absdipole_noeh/v1c5.data) dp2f=$(printf "%.8f" $dp2) if (( $(echo "$dp2f > 6" |bc -l) )); then dp_array["$ik"]="" fi done echo "xct ik weight kx ky" > v1c5-high_dp2_kpts.txt task() { local xct=$1 echo "Working on $xct" for line in {1..1152}; do weight=$(awk -v line=$line 'NR==line' '{ print$3 }' ../summarize_eigenvectors/"$xct"_/-5_1-sorted.txt) ik=$(awk -v line=$line 'NR==line' '{ print$4 }' ../summarize_eigenvectors/"$xct"_/-5_1-sorted.txt) kx=$(awk -v line=$line 'NR==line' '{ print$5 }' ../summarize_eigenvectors/"$xct"_/-5_1-sorted.txt) ky=$(awk -v line=$line 'NR==line' '{ print$6 }' ../summarize_eigenvectors/"$xct"_/-5_1-sorted.txt) if [[ -n "${dp_array[$ik]}" ]]; then echo "$xct $ik $weight $kx $ky" >> v1c5-high_dp2_kpts.txt fi done } export -f task parallel --max-procs 360 task ::: {1..7485}Explicación del código optimizado:
Se utiliza un array asociativo (
dp_array
) para almacenar los elementos de la matriz. La funcióntask
se exporta y luego se ejecuta en paralelo mediante la utilidadparallel
con 360 subprocesos. * Se utiliza un sistema de archivos local para almacenar archivos temporales. Esto evita la sobrecarga de lectura y escritura en el sistema de archivos de red.Respondida el Dec 19, 2020 a las 02:58 - por Gemini
Votos positivos: 0 | Votos negativos: 0