Desempenho de sed
vs. tail
para remover a primeira linha de um arquivo
TL; DR
sed
é muito poderoso e versátil, mas é isso que o torna lento, especialmente para arquivos grandes com muitas linhas.
tail
faz apenas uma coisa simples, mas que faz bem e rápido, mesmo para arquivos maiores com muitas linhas.
Para arquivos pequenos e médios, sed
e tail
está executando de maneira semelhante rápida (ou lenta, dependendo de suas expectativas). No entanto, para arquivos de entrada maiores (vários MBs), a diferença de desempenho aumenta significativamente (uma ordem de magnitude para arquivos na faixa de centenas de MBs), com tail
desempenho claramente superior.sed
.
Experimentar
Preparações Gerais:
Nossos comandos para analisar são:
sed '1d' testfile > /dev/null
tail -n +2 testfile > /dev/null
Observe que estou canalizando a saída para /dev/null
cada momento para eliminar a saída do terminal ou as gravações de arquivo como gargalo de desempenho.
Vamos configurar um disco RAM para eliminar a E / S do disco como gargalo em potencial. Eu pessoalmente tenho uma tmpfs
montada, /tmp
então simplesmente coloquei a minha testfile
lá para este experimento.
Depois, estou criando um arquivo de teste aleatório contendo uma quantidade especificada de linhas $numoflines
com comprimento e dados aleatórios usando este comando (observe que definitivamente não é o ideal, fica muito lento para cerca de> 2 milhões de linhas, mas quem se importa, não é o coisa que estamos analisando):
cat /dev/urandom | base64 -w0 | tr 'n' '\n'| head -n "$numoflines" > testfile
Oh, aliás. meu laptop de teste está executando o Ubuntu 16.04, 64 bits em uma CPU Intel i5-6200U. Apenas para comparação.
Cronometrando arquivos grandes:
Configurando uma enorme testfile
:
A execução do comando acima numoflines=10000000
produziu um arquivo aleatório contendo 10 milhões de linhas, ocupando um pouco mais de 600 MB - é enorme, mas vamos começar com isso, porque podemos:
$ wc -l testfile
10000000 testfile
$ du -h testfile
611M testfile
$ head -n 3 testfile
qOWrzWppWJxx0e59o2uuvkrfjQbzos8Z0RWcCQPMGFPueRKqoy1mpgjHcSgtsRXLrZ8S4CU8w6O6pxkKa3JbJD7QNyiHb4o95TSKkdTBYs8uUOCRKPu6BbvG
NklpTCRzUgZK
O/lcQwmJXl1CGr5vQAbpM7TRNkx6XusYrO
Execute a execução cronometrada com nosso enorme testfile
:
Agora vamos fazer apenas uma única execução cronometrada com os dois comandos primeiro para estimar com que magnitude estamos trabalhando.
$ time sed '1d' testfile > /dev/null
real 0m2.104s
user 0m1.944s
sys 0m0.156s
$ time tail -n +2 testfile > /dev/null
real 0m0.181s
user 0m0.044s
sys 0m0.132s
Já vemos um resultado realmente claro para arquivos grandes, tail
é uma magnitude mais rápida que sed
. Mas apenas por diversão e para ter certeza de que não há efeitos colaterais aleatórios fazendo uma grande diferença, vamos fazer isso 100 vezes:
$ time for i in {1..100}; do sed '1d' testfile > /dev/null; done
real 3m36.756s
user 3m19.756s
sys 0m15.792s
$ time for i in {1..100}; do tail -n +2 testfile > /dev/null; done
real 0m14.573s
user 0m1.876s
sys 0m12.420s
A conclusão permanece a mesma, sed
é ineficiente para remover a primeira linha de um arquivo grande, tail
deve ser usada lá.
E sim, eu sei que as construções de loop do Bash são lentas, mas estamos fazendo relativamente poucas iterações aqui e o tempo que um loop simples leva não é significativo em comparação com os sed
/ tail
durações de qualquer maneira.
Cronometrando arquivos pequenos:
Configurando um pequeno testfile
:
Agora, para concluir, vejamos o caso mais comum de você ter um pequeno arquivo de entrada no intervalo de kB. Vamos criar um arquivo de entrada aleatória numoflines=100
, parecido com este:
$ wc -l testfile
100 testfile
$ du -h testfile
8,0K testfile
$ head -n 3 testfile
tYMWxhi7GqV0DjWd
pemd0y3NgfBK4G4ho/
aItY/8crld2tZvsU5ly
Execute a execução cronometrada com nosso pequeno testfile
:
Como podemos esperar que o tempo para esses arquivos pequenos esteja no intervalo de alguns milissegundos de experiência, vamos fazer 1000 iterações imediatamente:
$ time for i in {1..1000}; do sed '1d' testfile > /dev/null; done
real 0m7.811s
user 0m0.412s
sys 0m7.020s
$ time for i in {1..1000}; do tail -n +2 testfile > /dev/null; done
real 0m7.485s
user 0m0.292s
sys 0m6.020s
Como você pode ver, os horários são bastante semelhantes, não há muito o que interpretar ou pensar. Para arquivos pequenos, ambas as ferramentas são igualmente adequadas.
sed
é mais portátil: "+2" paratail
funciona bem no Ubuntu, que usa o GNUtail
, mas não funciona no BSDtail
.