Por que a exclusão de arquivos por nome é dolorosamente lenta e também excepcionalmente rápida?

11

Faux Pas: O método "rápido" que mencionei abaixo, não é 60 vezes mais rápido que o lento. É 30 vezes mais rápido. Eu culpo o erro na hora (03:00 não é a minha melhor hora do dia para um pensamento claro :) ..

Atualização: adicionei um resumo dos tempos de teste (abaixo).
Parece haver dois problemas envolvidos com o fator de velocidade:

  • A escolha do comando usado (comparações de tempo mostradas abaixo)
  • A natureza de um grande número de arquivos em um diretório ... Parece que "grande é ruim". As coisas ficam desproporcionalmente mais lentas à medida que os números aumentam.

Todos os testes foram feitos com 1 milhão de arquivos.
(os horários real, do usuário e do sys estão nos scripts de teste)
Os scripts de teste podem ser encontrados em paste.ubuntu.com

#
# 1 million files           
# ===============
#
#  |time   |new dir   |Files added in  ASCENDING order  
#  +----   +-------   +------------------------------------------------- 
#   real    01m 33s    Add files only (ASCENDING order) ...just for ref.
#   real    02m 04s    Add files, and make 'rm' source (ASCENDING order) 
#                      Add files, and make 'rm' source (DESCENDING order) 
#   real    00m 01s    Count of filenames
#   real    00m 01s    List of filenames, one per line
#   ----    -------    ------
#   real    01m 34s    'rm -rf dir'
#   real    01m 33s    'rm filename' via rm1000filesPerCall   (1000 files per 'rm' call)
#   real    01m 40s    'rm filename' via  ASCENDING algorithm (1000 files per 'rm' call)
#   real    01m 46s    'rm filename' via DESCENDING algorithm (1000 files per 'rm' call)
#   real    21m 14s    'rm -r dir'
#   real    21m 27s    'find  dir -name "hello*" -print0 | xargs -0 -n 1000 rm'
#   real    21m 56s    'find  dir -name "hello*" -delete'
#   real    23m 09s    'find  dir -name "hello*" -print0 | xargs -0 -P 0 rm'
#   real    39m 44s    'rm filename' (one file per rm call) ASCENDING
#   real    47m 26s    'rm filename' (one file per rm call) UNSORTED
#                                                       

Recentemente, criei e excluí 10 milhões de arquivos de teste vazios. Excluindo arquivos por nome (por exemplo rm filename), descobri da maneira mais difícil que existe uma enorme diferença de tempo entre dois métodos diferentes ...

Ambos os métodos usam exatamente o mesmo rm filenamecomando.

Atualização: como se vê, os comandos não eram exatamente os mesmos ... Um deles estava enviando 1000 nomes de arquivos de cada vez para 'rm' ... Era um problema de expansão de chave de shell em que eu pensava que cada nome de arquivo estava sendo escrito para o arquivo do alimentador em uma linha própria, mas na verdade era de 1000 por linha

Os nomes dos arquivos são fornecidos por meio de um 'arquivo alimentador' em um while readloop.
O arquivo alimentador é a saída de ls -1 -f
Os métodos são idênticos em todos os aspectos, exceto por uma coisa:

  • o método slow usa o arquivo do alimentador não classificado diretamente dels -1 -f
  • o método rápido usa uma versão classificada do mesmo arquivo não classificado

Não tenho certeza se a classificação é o problema aqui, ou talvez o arquivo do alimentador classificado coincida com a sequência na qual os arquivos foram criados (usei um algoritmo inteiro ascendente simples)

Para 1 milhão de arquivos, o método rápido rm filename é 60 vezes mais rápido que o lento ... novamente, não sei se é um problema de "classificação" ou um problema de tabela de hash nos bastidores ... suspeito não é uma questão simples de classificação, porque por que ls -1 -fme forneceria intencionalmente uma lista sem classificação de uma sequência "ordenada" de nomes de arquivos recém-adicionados ...

Só estou me perguntando o que está acontecendo aqui, por isso não levo dias (sim dias) para excluir os próximos 10 milhões de arquivos :) .... Eu digo "dias" porque tentei tantas alternativas, e o os tempos envolvidos aumentam desproporcionalmente ao número de arquivos envolvidos .. então eu só testei 1 milhão em detalhes

BTW: excluir os arquivos por meio da "lista classificada" de nomes é realmente mais rápido que rm -rfpor um fator de 2.
e: rm -rera 30 vezes mais lento que o método "lista classificada"

... mas está "resolvido" o problema aqui? ou está mais relacionado a um método de armazenamento em hash (ou qualquer outro) usado pelo ext4?

O que me intriga bastante é que cada chamada rm filenamenão está relacionada à anterior .. (bem, pelo menos é assim da perspectiva do 'bash')

Estou usando a unidade Ubuntu / bash / 'ext4' / SATA II.

Peter.O
fonte
1
Você está fazendo isso errado! (tm) Já ouviu falar find -delete?
alex
Seus 2 testes iniciam em condições desiguais (não pretendo que isso seja realmente importante): um lê os nomes de arquivos de um arquivo e o outro lê os nomes de arquivos de um arquivo que foi criado (classificado) imediatamente antes do teste. Pode ser que o arquivo que está sendo armazenado em cache no segundo caso reproduza alguns (ou talvez não, quem sabe). Para que os testes estejam em condições mais iguais, talvez você deva fazer um catarquivo simples para um novo antes do 1º teste - em vez de sortantes do 2º teste.
imz - Ivan Zakharyaschev 22/03
E eu recomendo que você apresente suas observações e perguntas de uma maneira mais clara. Por favor, uma coisa de cada vez: compare apenas dois casos em uma pergunta, traga os dois casos importantes para o primeiro plano, todos os outros são apenas informações de base; por favor, deixe isso claro. Não misture várias observações em uma postagem, por favor.
imz - Ivan Zakharyaschev 22/03
A apresentação do tempo do sistema e do espaço do usuário também pode ser importante para resolver o quebra-cabeça. Por isso, inclua-o na sua pergunta. Qual deles faz a grande diferença em seus testes?
imz - Ivan Zakharyaschev 22/03
1
Otimização prematura é a raiz de todo o mal. :) Quando você excluirá 10 milhões de arquivos? 100 000 por segundo parece rápido o suficiente para mim (arruinar seu sistema).
usuário desconhecido

Respostas:

2

Espera-se que rm -r seja lento como recursivo. Um primeiro percurso de profundidade deve ser feito na estrutura de diretórios.

Agora, como você criou 10 milhões de arquivos? você usou algum script que faz um loop em alguma ordem? 1.txt, 2.txt, 3.txt ... se sim, esses arquivos também podem ser alocados na mesma ordem em blocos contíguos no hdd.so excluir na mesma ordem será mais rápido.

"ls -f" ativará -aU, que lista em ordem de diretório, que é novamente recursiva.

rajaganesh87
fonte
1
McAlot: Não vejo como 'recursiva' importaria nesse caso , pois não há subdiretórios envolvidos ... Sim, eu usei "1.txt, 2.txt, 3.txt". Talvez haja vários coisas interagindo: por exemplo, por que leva apenas 1min 30s para criar 1 milhão de arquivos, mas são necessários 7m 10s para criar 2 milhões e, após excluí-los, recriar o 1 milhão leva muito mais tempo (9m 30s), é estranho; tudo está funcionando lentamente, de repente. Isso já aconteceu antes também. Eu acho que (?) excluir o diretório o corrigiu. Existe um daemon de arquivo envolvido (nautilus; localizar) talvez? Para continuar ...
Peter.O
Em geral, os sistemas de arquivos não são otimizados para lidar com um grande número de arquivos no mesmo diretório. Não estou familiarizado com o ext4 especificamente, mas para outros formatos as entradas do diretório foram marcadas como não utilizadas quando os arquivos foram excluídos. Isso significa que eles ainda devem ser ignorados ao executar operações no diretório. Isso explicaria o comportamento que você está vendo.
precisa saber é o seguinte
1
Eu apaguei o diretório 'agora mais lento' e usei um nome diferente para um novo diretório. O tempo para criar 1 milhão de arquivos agora está reduzido a 1m 33s (vs 9m 30s quando o diretório "contém" 2 milhões de arquivos excluídos, o primeiro milhão com o mesmo nome que o 1 milhão adicionado recentemente) ... interessante registra com o seu comentário "... apenas marcado como não utilizado" ... chegando lá; está começando a fazer sentido :)
Peter.O
@ fred.bear Meu mal, eu realmente não sabia a hierarquia real e minha resposta foi adivinhar. seu teste também enfatiza os metadados, mas não os arquivos reais, pois são arquivos vazios. A melhor maneira de avaliar esse tipo de problema é obter arquivos de / var ou cache do servidor da web. mesmo assim, seu teste também parece interessante, você pode tentar excluir com dois métodos listados em diretórios diferentes ... como /sample1/1.txt,2.txt ... e /sample2/1.txt,2.txt ..
rajaganesh87
@ Mr.Confused.A.Lot ... Obrigado pela ajuda. Sua explicação me ajudou a entender mais sobre o sistema de arquivos e alguns de seus maneirismos ... Agora, tenho uma noção razoável do que estava causando os diferentes problemas de velocidade ... alguns eram apenas opções de comandos do bash e outros eram simplesmente problemas do sistema de arquivos ( eu estou à esquerda com um novo lema: "grande é ruim" para diretórios ... (para algumas ações, pelo menos) ...
Peter.O
2

Você deve otimizar a estrutura de arquivos. Então, ao invés de

for i in $(seq 1 1000); do touch file.$i; done

faça algo mais inteligente como (bash assumido):

function bucklocate() 
{ 
    hash=$(echo -n "$1"|md5sum|cut -f1); 
    echo -n "${hash:1:1}/${hash:7:1}/${hash:9:2}/$1"; 
}

hexdig="{0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f}"
eval mkdir -p $hexdig/$hexdig/$hexdig$hexdig


for i in $(seq 1 1000); do touch $(bucklocate file.$i); done

Agora, este exemplo é bastante lento por causa do uso de md5sum [1], use algo como o seguinte para obter respostas muito mais rápidas, desde que você não precise de nomes de arquivos específicos, as duplicatas não são motivo de preocupação e não é necessário hash repetível de um determinado nome :)

mkdir -pv {0,1,2,3,4,5,6}/{0,1,2,3,4,5,6,7,8,9,10,12}
for  a in $(seq 1 100); do i=$RANDOM; echo touch "$(($i%7))/$(($i%13))/file.$i"; done

É claro que tudo isso é um conceito de empréstimo de hashtables

ver
fonte
Eu acho que você está dizendo "use diretórios menores" ... Essa é uma ideia interessante; um DBMS desenvolvido em casa que cria uma árvore a partir de um grupo de arquivos 'sem árvores'. Alguns podem chamar de planejamento avançado :) ... Se funcionar (e provavelmente funciona), é uma boa idéia ! :) ... Estou começando a ter a idéia de que 'grande é ruim' quando se trata do número de arquivos em um diretório (pelo menos para o ext4) ... Você apresentou uma solução alternativa preventiva (+1) e eu ' m lentamente ficando uma idéia do porquê de alguns métodos de exclusão são mais rápidos do que outros em qualquer diretório, pequenas ou grandes ... Obrigado
Peter.O
Yup desculpe por não ser mais explícito sobre a idéia de manter dirs pequena
sehe