Script usando “split” em um loop sobre arquivos grandes

3

Estou tentando fazer com que um script básico funcione e com erros terríveis ao longo do caminho. O objetivo do script é dividir vários arquivos de texto grandes, em vários arquivos, com uma contagem de linha lida do arquivo.

#!/bin/bash
DIR="$( cd "$( dirname "$0" )" && pwd )" 
for i in 21 22 23 24 25 26 27 28 29 30 33 34 35 36 37 38 39 210 211 212 213 214 215 216 217 218 310 311 312 313 314 315 316 317 318
do
    lines="'head -1 $DIR/C$i/DOPC-C$i.xyz'"
    echo $lines
    lines=$((lines+2))
    split -a4 -d -l$lines $DIR/C$i/DOPC-C$i.xyz $DIR/C$i/DOPC-C$i-
done

O arquivo de texto grande tem o número de moléculas como sua primeira linha, então estou usando o comando head para lê-lo, passando isso para dividir como o número de linhas para. Tem uma formatação semelhante a:

3
Comment
C 0.41238 0.2301928 0.123123
H 0.123123 0.123233 0.5234234
H 0.123123 0.123233 0.5234234
3
Comment
C 0.41238 0.2301928 0.123123
H 0.123123 0.123233 0.5234234
H 0.123123 0.123233 0.5234234

No entanto, quando eu executo este terminal, o uso de memória do meu sistema a partir do free -m dispara de 1,5 GB para 16 GB em uso e fica extremamente sem resposta. Ele funciona corretamente para os dois primeiros arquivos e os divide como eu queria. Algum conselho?

EDIT: Os arquivos de origem são todos ~ 200-300MB. Quando eu corro o comando split diretamente em qualquer um dos arquivos ele funciona bem. Existem 30 arquivos que precisam ser divididos dessa maneira, C21, C22, C23, etc. Eu reran o script e ele passou pelos dez primeiros arquivos antes de atingir o limite de memória dessa vez.

EDIT2: Então, eu fiz uma espécie de trabalho muito pesado ao redor. Depois de executá-lo através de três arquivos, eu simplesmente coloco

echo 3 | tee /proc/sys/vm/drop_caches

Eu notei que após o comando split minha memória em uso aumentaria acentuadamente de acordo com o free -m O pico também não desapareceria ao fechar a janela do terminal onde eu executei o comando split. Acredito que haja algum problema na configuração do cache de disco em meu sistema: o Linux deve estar armazenando em cache os arquivos dos quais estou escrevendo ou gravando e não limpando. Ao colocar isso para executar cada terceiro arquivo, o script é executado, embora de forma relativamente lenta, através de todos os arquivos, e meu sistema permanece estável depois. Minha suspeita é que esse cache também pode estar relacionado em parte ao fato de ser um sistema de arquivos NTFS no qual estou trabalhando.

    
por Decimak 04.08.2014 / 01:05

2 respostas

4

Explicação adicional baseada no desempenho do sistema de arquivos NTFS

Depois de escrever a seção inferior desta resposta, o OP apontou que o script está sendo executado em um disco NTFS e suspeita que isso possa ser parte do problema.

Isso não seria muito surpreendente: há problemas de desempenho com o NTFS especiffically relacionados ao manuseio de muitos arquivos pequenos. E estamos criando arquivos pequenos na ordem de milhões - por arquivo de entrada.

Assim, o desempenho ruim do NTFS seria uma explicação alternativa para a degradação do desempenho, enquanto o uso extremo do memmory parece ainda estar relacionado ao mmap ().

Desempenho ruim do NTFS
Configurando o sistema de arquivos NTFS para desempenho

Explicando o problema de memória pelo strong uso de mmap ()

O problema de memória que ocorre com split em seu script parece estar relacionado ao uso do mmap em 'split'.

strace mostra as seguintes chamadas para cada arquivo de saída:

28892 open("xx02", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
28892 fstat(3, {st_mode=S_IFREG|0664, st_size=0, ...}) = 0
28892 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f821582f000
28892 write(3, "sometext\n", 30) = 30
28892 close(3)                          = 0
28892 munmap(0x7f821582f000, 4096)      = 0


Com base nos exemplos, para uma estimativa aproximada dos arquivos a serem tratados,
assumimos arquivos de entrada de 300MB, arquivos de saída de 100B:

Isso nos dá cerca de 3000000 arquivos para escrever. Nós escrevemos apenas um de uma vez. Mas usamos mmap() . Isso significa que, para cada um dos arquivos, pelo menos uma página de memória é usada, que tem 4096 B de tamanho.

Levando isso em consideração, tocamos cerca de 12 GB de memória (1) para um arquivo de entrada (mas não todos de uma vez). Três milhões de arquivos e 12 GB, parece que poderia causar algum trabalho para o kernel.

Basicamente, parece que split é apenas não criado para este trabalho , porque usa mmap () .
Isso é bom em outras situações.
Mas neste caso extremo de entrada, ele irá bagunçar o gerenciamento de memória mal - o que então precisa de algum tempo para limpar. (2)

(2) Ele não usará muito memmory ao mesmo tempo, mas sim um grande número de pequenos arquivos em pouco tempo.

(1) Ou apenas espaço de endereçamento?

    
por 04.08.2014 / 07:24
2

Estou curioso para resolver o problema de memória com a solução split da questão, mas de forma independente, essa abordagem alternativa pode ser útil:

Você pode usar csplit em vez de split para dividir esse tipo de arquivo.

Para csplit , você precisa definir um padrão para definir onde dividir, e você pode usar as linhas com um único número como separador - se você souber que não há essas linhas nos comentários.

Não está claro para mim qual é o problema de memória, mas usar uma ferramenta diferente pode contornar isso.

Mas também há a vantagem de o comando ficar mais simples, não é necessário obter o número primeiro.

O comando seria algo como:

csplit --elide-empty-files -n4 in.txt '/^[0-9]\+$/' '{*}'

    
por 04.08.2014 / 02:00