AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / unix / Perguntas / 782407
Accepted
αғsнιη
αғsнιη
Asked: 2024-08-23 11:34:41 +0800 CST2024-08-23 11:34:41 +0800 CST 2024-08-23 11:34:41 +0800 CST

O comando grep falha com erro de falta de memória

  • 772

Encontrei um problema de OOM (isso acontece sempre que eu o executo) ao executar

grep -Fxvf file1 file2

tamanho do arquivo1: ~200MB
tamanho do arquivo2: ~300MB
número de registros em cada arquivo: ~300K
comprimento médio de registros: ~1K (somente caracteres ASCII)
a diferença entre dois arquivos é de aproximadamente 18K registros

Memória livre disponível: ~16GB

Tentei com diversas grepversões diferentes e em VM, WSL e também em servidor físico, mas obtive o mesmo resultado.

Observe que executei o mesmo comando com apenas algumas linhas de ambos os arquivos para identificar que ele não entra em um loop infinito por ter algum caractere especial nos arquivos e foi bem-sucedido.

Isso é normal?

Estou tentando gerar registros file2que não existem no arquivo file1.

Já resolvi meu requisito no awkmesmo ambiente e obtive a saída em menos de 10 segundos, mas estou me perguntando por que os grepresultados em OOM.

Eu usei o mesmo comando quase sempre quando precisava consultar os mesmos requisitos e até comparei dois arquivos muito grandes, como dois arquivos com ~ 2 GB de tamanho e cada um com ~ 90 milhões de registros e registros contendo no máximo ~ 20 caracteres ASCII sem qualquer problema em as mesmas caixas.

Usei GNU grep2.7, 2.16 no SLES12 e GNU grep3.7 no Ubnutu 22.04 no WSL.

linux
  • 2 2 respostas
  • 471 Views

2 respostas

  • Voted
  1. Best Answer
    Stéphane Chazelas
    2024-08-24T00:53:07+08:002024-08-24T00:53:07+08:00

    Se você olhar o código da implementação GNU degrep para saber o que acontece quando invocado como grep -Fxvf file1 file2, você verá:

    1. lê todo o conteúdo da file1memória
    2. desduplica as linhas dessa lista usando uma tabela hash (para a qual são necessários alguns bytes extras de memória por linha, embora essa memória seja recuperada após a desduplicação)¹.
    3. em seguida, ele compila uma árvore de padrões usando o algoritmo Aho – Corasick (o mesmo Aho do ain awke o mesmo algoritmo usado no original fgrepnos anos 70), de modo a minimizar o número de comparações quando se trata de procurar linhas correspondentes em o assunto.

    3 acontece mesmo quando -Fcombinado com -xo qual, para que mais do que algumas strings correspondam, provavelmente seria muito mais eficiente usar uma tabela hash (e, ironicamente, uma foi construída apenas no estágio 2).

    É provável que a última parte 3 consuma mais CPU e exija mais memória.

    Correndo:

    base64 -w 1000 /dev/urandom | head -n 10000 |
      \time -f %MKiB grep -Fvxf - /dev/null
    

    E variando 10.000, vejo que ele aloca cerca de 96 bytes de RAM para cada byte do arquivo de padrão, então seriam 18GiB para seu arquivo de 200MiB.

    Observando isso gdb, vemos que ele aloca a struct trie(64 bytes) para a maioria, se não para todos os bytes no arquivo de padrão, pelo menos.

    grepnormalmente é usado para procurar uma string ou regexp ou uma pequena lista de strings ou regexps em entradas de tamanho arbitrário, não foi projetado para comparar arquivos.

    Para isso, use uma tabela hash como faria em awkou perlcom:

    awk '!file1_processed {hash[$0]; next}
         ! ($0 in hash)' file1 file1_processed=1 file2
    

    É o caminho a seguir. Isso só precisa armazenar todo o conteúdo file1na memória (mais a sobrecarga da tabela hash). E a correspondência file2é apenas uma pesquisa na tabela hash.

    Para arquivos já classificados, usar commseria ainda mais eficiente.


    Agora, olhando para outras grepimplementações, podemos encontrar ou construir no GNU/Linux:

    $ () { \time -f '%MKiB %E' grep -Fvxf $1 $1; } =(base64 -w 1000 /dev/urandom | head -n 20000)
    Command exited with non-zero status 1
    1918504KiB 0:04.01
    
    $ () { \time -f '%MKiB %E' busybox grep -Fvxf $1 $1; } =(base64 -w 1000 /dev/urandom | head -n 20000)
    Command exited with non-zero status 1
    24644KiB 0:15.55
    
    $ () { \time -f '%MKiB %E' toybox grep -Fvxf $1 $1; } =(base64 -w 1000 /dev/urandom | head -n 20000)
    Command exited with non-zero status 1
    22892KiB 0:00.11
    
    $ () { \time -f '%MKiB %E' ast-open-grep -Fvxf $1 $1; } =(base64 -w 1000 /dev/urandom | head -n 20000)
    Command exited with non-zero status 1
    48416KiB 0:21.90
    
    $ () { \time -f '%MKiB %E' heirloom/grep/grep_su3 -Fvxf $1 $1; } =(base64 -w 1000 /dev/urandom | head -n 20000)
    Command exited with non-zero status 1
    805476KiB 0:13.58
    

    O GNU e o baú de ferramentas herdado (do OpenSolaris provavelmente baseado no original fgrep) usam muita memória, mas são "relativamente" rápidos. busybox e ast-open usam menos memória, mas são muito mais lentos, possivelmente usando um algoritmo mais burro .

    Toybox' é muito rápido (embora ainda significativamente mais lento que a awkabordagem) sem usar muita memória (pouco acima do tamanho de file1). Ele usa uma espécie de tabela hash grosseira em que o "hash" é o primeiro byte da string, portanto, embora faça comparações simples, ele só precisa comparar com a lista de strings para as quais o primeiro byte é o mesmo, que em o caso de strings aleatórias codificadas em base64, como em meus testes, significa que o número de comparações a serem realizadas é dividido por 64.

    Se em vez de 20.000 linhas longas de 1.000 bytes de texto base64 aleatório, usarmos a saída seq -f %01000g 20000onde apenas os últimos bytes das linhas mudam, os tempos se tornam bem diferentes, com GNU/heirloom sendo de longe o mais rápido (menos de um segundo, mostrando o benefício de usar o algoritmo Aho – Corasick), busybox/ast-open/toybox aparentemente demorando uma eternidade (desisti de esperar depois de 5 minutos), toybox' ficando atrás de ast-open (não busybox que se torna o mais lento de longe²) como o o primeiro byte é sempre o mesmo, e também porque todas as comparações precisam comparar os primeiros 990+ bytes, então podemos esperar que seja cerca de 64*990 tão lento quanto para dados aleatórios.


    ¹ Ele também registra em outra tabela onde os padrões foram lidos, embora seja usado apenas ao relatar erros de regexp, portanto, é inútil para strings fixas.

    ² Talvez porque usestrstr() para pesquisar um string dentro de um string em vez de strcmp()mesmo com -x.

    • 17
  2. Deeepdigger
    2024-08-23T18:50:40+08:002024-08-23T18:50:40+08:00

    Conforme mencionado nos comentários e para responder à sua pergunta: Falha porque grep está lendo o conteúdo completo do arquivo1 e usando cada linha desse arquivo como um padrão de texto estático para pesquisar/corresponder no arquivo2

    Embora isso funcione em princípio, é muito ineficiente quando você deseja obter linhas iguais em ambos os arquivos.

    Conforme sugerido, use:comm

    comm -23 <(sort file2) <(sort file1)

    Isso comparará o conteúdo classificado e as linhas de saída exclusivas do arquivo2 (-2 suprime as linhas exclusivas do arquivo1 e -3 suprime as linhas comuns aos dois arquivos).

    (ou classifique os dois arquivos antes de passá-los para o comando comm)

    • -1

relate perguntas

  • Existe uma maneira de fazer ls mostrar arquivos ocultos apenas para determinados diretórios?

  • Inicie/pare o serviço systemd usando o atalho de teclado [fechado]

  • Necessidade de algumas chamadas de sistema

  • astyle não altera a formatação do arquivo de origem

  • Passe o sistema de arquivos raiz por rótulo para o kernel do Linux

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Possível firmware ausente /lib/firmware/i915/* para o módulo i915

    • 3 respostas
  • Marko Smith

    Falha ao buscar o repositório de backports jessie

    • 4 respostas
  • Marko Smith

    Como exportar uma chave privada GPG e uma chave pública para um arquivo

    • 4 respostas
  • Marko Smith

    Como podemos executar um comando armazenado em uma variável?

    • 5 respostas
  • Marko Smith

    Como configurar o systemd-resolved e o systemd-networkd para usar o servidor DNS local para resolver domínios locais e o servidor DNS remoto para domínios remotos?

    • 3 respostas
  • Marko Smith

    apt-get update error no Kali Linux após a atualização do dist [duplicado]

    • 2 respostas
  • Marko Smith

    Como ver as últimas linhas x do log de serviço systemctl

    • 5 respostas
  • Marko Smith

    Nano - pule para o final do arquivo

    • 8 respostas
  • Marko Smith

    erro grub: você precisa carregar o kernel primeiro

    • 4 respostas
  • Marko Smith

    Como baixar o pacote não instalá-lo com o comando apt-get?

    • 7 respostas
  • Martin Hope
    user12345 Falha ao buscar o repositório de backports jessie 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl Por que a maioria dos exemplos do systemd contém WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky Como exportar uma chave privada GPG e uma chave pública para um arquivo 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll status systemctl mostra: "Estado: degradado" 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim Como podemos executar um comando armazenado em uma variável? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S Por que /dev/null é um arquivo? Por que sua função não é implementada como um programa simples? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 Como ver as últimas linhas x do log de serviço systemctl 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - pule para o final do arquivo 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla Por que verdadeiro e falso são tão grandes? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis Substitua a string em um arquivo de texto enorme (70 GB), uma linha 2017-12-30 06:58:33 +0800 CST

Hot tag

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve