Em um bash
script, preciso de vários valores dos /proc/
arquivos. Até agora, tenho dezenas de linhas cumprindo os arquivos diretamente assim:
grep -oP '^MemFree: *\K[0-9]+' /proc/meminfo
Em um esforço para tornar isso mais eficiente, salvei o conteúdo do arquivo em uma variável e confirmei que:
a=$(</proc/meminfo)
echo "$a" | grep -oP '^MemFree: *\K[0-9]+'
Em vez de abrir o arquivo várias vezes, basta abri-lo uma vez e cumprimentar o conteúdo da variável, que presumi ser mais rápido - mas, na verdade, é mais lento:
bash 4.4.19 $ time for i in {1..1000};do grep ^MemFree /proc/meminfo;done >/dev/null
real 0m0.803s
user 0m0.619s
sys 0m0.232s
bash 4.4.19 $ a=$(</proc/meminfo)
bash 4.4.19 $ time for i in {1..1000};do echo "$a"|grep ^MemFree; done >/dev/null
real 0m1.182s
user 0m1.425s
sys 0m0.506s
O mesmo vale para dash
e zsh
. Suspeitei do estado especial dos /proc/
arquivos como motivo, mas quando copio o conteúdo de /proc/meminfo
um arquivo normal e uso os resultados são os mesmos:
bash 4.4.19 $ cat </proc/meminfo >meminfo
bash 4.4.19 $ time for i in $(seq 1 1000);do grep ^MemFree meminfo; done >/dev/null
real 0m0.790s
user 0m0.608s
sys 0m0.227s
O uso de uma string here para salvar o pipe o torna um pouco mais rápido, mas ainda não tão rápido quanto nos arquivos:
bash 4.4.19 $ time for i in $(seq 1 1000);do <<<"$a" grep ^MemFree; done >/dev/null
real 0m0.977s
user 0m0.758s
sys 0m0.268s
Por que abrir um arquivo mais rápido do que ler o mesmo conteúdo de uma variável?
/proc/
arquivos como uma razão, mas quando eu copiar o conteúdo de/proc/meminfo
um arquivo regular e uso que os resultados são os mesmos:” É não especial para/proc/
arquivos, a leitura de arquivos regulares também é mais rápida!Respostas:
Aqui, não se trata de abrir um arquivo em vez de ler o conteúdo de uma variável, mas mais de criar um processo extra ou não.
grep -oP '^MemFree: *\K[0-9]+' /proc/meminfo
bifurca um processo que executagrep
que é aberto/proc/meminfo
(um arquivo virtual, na memória, sem E / S de disco envolvido) o lê e corresponde ao regexp.A parte mais cara disso é bifurcar o processo e carregar o utilitário grep e suas dependências de biblioteca, fazer o vínculo dinâmico, abrir o banco de dados de localidade, dezenas de arquivos que estão no disco (mas provavelmente armazenados em cache na memória).
A parte sobre leitura
/proc/meminfo
é insignificante em comparação, o kernel precisa de pouco tempo para gerar as informações egrep
precisa de pouco tempo para lê-las.Se você executar
strace -c
isso, verá que as chamadas de umopen()
e umread()
sistemas usadas para ler/proc/meminfo
são amendoins, em comparação com tudo o quegrep
faz para iniciar (strace -c
não conta a bifurcação).Em:
Na maioria dos shells que suportam esse
$(<...)
operador ksh, o shell apenas abre o arquivo e lê seu conteúdo (e retira os caracteres de nova linha à direita).bash
é diferente e muito menos eficiente, pois bifurca um processo para fazer essa leitura e transmite os dados ao pai por meio de um canal. Mas aqui, é feito uma vez, para que não importe.Em:
O shell precisa gerar dois processos, que estão sendo executados simultaneamente, mas interagem entre si por meio de um pipe. A criação, desmontagem e escrita e leitura do cano têm um pequeno custo. O custo muito maior é a desova de um processo extra. O agendamento dos processos também tem algum impacto.
Você pode achar que o uso do
<<<
operador zsh o torna um pouco mais rápido:No zsh e no bash, isso é feito escrevendo o conteúdo de
$a
um arquivo temporário, que é mais barato do que gerar um processo extra, mas provavelmente não trará nenhum ganho em comparação com a obtenção direta dos dados/proc/meminfo
. Isso ainda é menos eficiente do que a sua abordagem que copia/proc/meminfo
no disco, pois a gravação do arquivo temporário é feita a cada iteração.dash
não suporta strings here, mas seus heredocs são implementados com um pipe que não envolve gerar um processo extra. Em:A concha cria um tubo, bifurca um processo. O filho executa
grep
com seu stdin como a extremidade de leitura do canal, e o pai grava o conteúdo na outra extremidade do canal.Mas é provável que esse manuseio de tubos e sincronização de processos seja mais caro do que apenas obter os dados imediatamente
/proc/meminfo
.O conteúdo de
/proc/meminfo
é curto e não leva muito tempo para produzir. Se você quiser salvar alguns ciclos da CPU, remova as partes caras: processos de bifurcação e execução de comandos externos.Gostar:
Evite,
bash
porém, cuja correspondência de padrões seja muito ineficiente. Comzsh -o extendedglob
, você pode reduzi-lo para:Note que isso
^
é especial em muitos shells (Bourne, fish, rc, es e zsh com a opção extendedglob pelo menos), eu recomendo citá-lo. Observe também queecho
não pode ser usado para gerar dados arbitrários (daí o usoprintf
acima).fonte
printf
você dizer que o shell precisa gerar dois processos, masprintf
um shell não está embutido?grep
e executa.A | B
, existem alguns shells, como o AT&T ksh ou zsh, que são executadosB
no processo atual do shell, se for um comando integrado, composto ou de função, não conheço nenhum que seja executadoA
no processo atual. De qualquer forma, para fazer isso, eles teriam que lidar com o SIGPIPE de maneira complexa, como seA
estivesse executando no processo filho e sem encerrar o shell para que o comportamento não seja muito surpreendente quandoB
sair cedo. É muito mais fácil executarB
no processo pai.<<<
bash
que não apoiava<<<
, apenas que o operador veiozsh
como$(<...)
veio do ksh.No seu primeiro caso, você está apenas usando o utilitário grep e encontrando algo no arquivo
/proc/meminfo
./proc
É um sistema de arquivos virtual, portanto o/proc/meminfo
arquivo está na memória e requer muito pouco tempo para buscar seu conteúdo.Mas, no segundo caso, você está criando um canal, passando a saída do primeiro comando para o segundo comando usando esse canal, o que é caro.
A diferença é devido a
/proc
(porque está na memória) e ao pipe, veja o exemplo abaixo:fonte
Você está chamando um comando externo nos dois casos (grep). A chamada externa requer um subshell. A bifurcação dessa concha é a causa fundamental do atraso. Ambos os casos são semelhantes, portanto: um atraso semelhante.
Se você quiser ler o arquivo externo apenas uma vez e usá-lo (de uma variável) várias vezes, não saia do shell:
O que leva apenas cerca de 0,1 segundos, em vez de 1 segundo completo para a chamada grep.
fonte