Desempenho do script Bash

1

Em um script bash , alguns inteiros em um intervalo são gerados, concatenados com sequências fixas e impressos em um arquivo. SO é Ubuntu 14.04 e bash versão é 4.3.11(1)-release .

As cadeias (e as linhas) a serem impressas são milhões. Aqui está o uso do disco rígido de iotop em várias vezes:

 TID  PRIO  USER     DISK READ  DISK WRITE  SWAPIN     IO>    COMMAND  
5701 be/4 myuser    408.24 B/s   97.27 K/s  0.00 %  0.23 % bash ./script.sh
5701 be/4 myuser    408.20 B/s   97.27 K/s  0.00 %  0.00 % bash ./script.sh
5701 be/4 myuser    408.41 B/s   95.72 K/s  0.00 %  0.11 % bash ./script.sh

Demora cerca de 16 minutos para imprimir 1 milhão de strings. A largura de banda do disco rígido está longe de estar saturada. De uptime após mais de 15 minutos de execução:

load average: 0,97, 1,14, 1,19

em uma CPU de 4 núcleos. Portanto, nem a CPU nem o disco rígido parece ser responsável por essa execução extremamente lenta.

  1. Como é possível acelerar este processo?

  2. É possível ainda usar bash ou devo usar outro idioma?

Partes relevantes do script são as seguintes linhas:

#!/bin/bash

i=0
a='fixedstring1'
b='fixedstring2'

while [[ $i -le 9999999 ]]; do
[...]
    ur="$a""$b""$i"
[...]
    echo $ur >> exp1/file$m
[...]
    (( i++ ))
done

file$m é o arquivo m -th gerado (o script gera vários arquivos com um número fixo de linhas em vez de um arquivo exclusivo com todas as linhas).

    
por BowPark 25.11.2016 / 12:07

2 respostas

1

O loop de Bash é extremamente lento no seu caso:

> cat test-1.sh
#!/bin/sh

i=0
a='fixedstring1'
b='fixedstring2'


while [ $i -lt ${1:-9} ]; do
  ur="$a""$b""$i"
  echo $ur
  (( i++ ))
done

> time sh test-1.sh 999999 | wc -l
...
real    1m11.488s

E usar jot (ou seq ) torna isso muito mais rápido:

> cat test-3.sh
#!/bin/sh

a='fixedstring1'
b='fixedstring2'

jot -w "$a$b" ${1:-9}
# seq -f "$a$b%g" ${1:-9}
> time sh test-3.sh 999999 | wc -l
...
real    0m0.613s

E qual é a lógica de dividir números para arquivar? Se for um número limitado de linhas, você pode usar o comando split (agora eu quero dizer GNU split devido ao uso da opção -d ):

sh test-3.sh | split -d -l "how much lines in one file" - expr1/file
    
por 25.11.2016 / 12:49
1

Você está repetidamente abrindo e fechando o mesmo arquivo (talvez centenas de vezes). Tente abrir cada arquivo no máximo uma vez.

i=0
a='fixedstring1'
b='fixedstring2'
m=0

exec 3>exp1/file$m

while [[ $i -le 9999999 ]]; do
    ur="$a$b$i"
    if ... # m changes
    then
        exec 3>exp1/file$m
    fi
    echo $ur >&3
    (( i++ ))
done

Este é o mesmo conselho que redirecionar todo o loop em vez de redirecionar um comando específico no loop.

    
por 25.11.2016 / 12:27