Vazamento de memória do Gawk com frequência de palavras

1

Eu tenho o seguinte script:

#!/usr/bin/env bash

grep -e '^[a-zA-Z]\{4,8\}$' data/words3.txt | tr '[:upper:]' '[:lower:]' | sort -u > data/passphrase-words.txt

function wordfrequency() {
  awk '{ for (i=1; i<=NF; i++) { word = tolower($i); words[word]++ } } END { for (w in words) printf("%3d %s\n", words[w], w) } ' | sort -rn
}

function getArticleText() {
  awk '/<text xml:space="preserve">/,/<\/text>/' | sed 's/<.*>//'
}

function reduceWikiText() {
  tr ' [:punct:]' '[\n*]' | sed '/^$/d' | tr '[:upper:]' '[:lower:]'
}

bzcat data/enwiki-20161020-pages-articles.xml.bz2 | getArticleText | reduceWikiText | grep -F -f data/passphrase-words.txt | wordfrequency > data/wordFreqs.txt

Tenho certeza de que poderia ser simplificado de várias maneiras, mas é o que eu criei. data / passphrase-words é uma lista de cerca de 170k palavras, uma palavra por linha. data/enwiki-* é 12GB de XML compactado (é um dump da Wikipedia). A partir daí, getArticleText pega o texto de cada artigo, reduceWikiText "reduz" esse texto para uma palavra por linha, eliminando todos os xml e pontuação, e a palavra frequencia conta quantas vezes cada palavra aparece.

Se eu estiver lendo meu gerenciador de tarefas corretamente, o gawk dentro de wordFrequency () está usando quantidades massivas de memória; 695MB, se eu deixar correr o suficiente, mais de 1GB de RAM.

O comando grep que não faz parte de nenhuma função limita o número de palavras diferentes que o gawk verá, e ocupa uma constante de 36 MB. Eu poderia ver o gawk precisando de 50 MB ou até 100 MB, mas com mais de 1 GB? Isso parece errado. E a taxa de crescimento implica que crescerá sem limites.

Alguém pode me ajudar a descobrir por que o gawk está usando tanta memória? Devido ao tamanho do arquivo BZ2, não posso deixar que o Gawk saia muito do controle ...

E a razão pela qual eu não estou usando o tipo | uniq -c | sort -nr é porque eu realmente quero que a agregação de contagem de palavras ocorra na memória; Eu sei que pode se encaixar, pelo número de palavras com as quais estou lidando. Menos uso de HDD = mais rápido, certo?

Para referência, o Linux Subsystem for Windows e:

$ gawk --version
GNU Awk 4.0.1
Copyright (C) 1989, 1991-2012 Free Software Foundation.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/.

EDIT: Publicou o que eu tenho (menos o arquivo de 12GB .xml.bz2) em link se isso ajudar. Usando mawk, conforme as sugestões nos comentários, não parece fazer nada, mas eu parei o processo em 200MB de RAM. Permitirá que o processo seja executado durante a noite sem usar o awk, apenas para ver o que acontece.

EDIT2: Após substituir em | sort | uniq -c pelo awk problemático, o processo foi concluído dentro das 6-7 horas que eu estava fora. Eu vou fazer alguns ajustes para tentar remover o uso de HTML em artigos (se livrar de "" causando tanta poluição), e tempo de novo, mas pelo menos ele é executado em uma quantidade "razoável" de tempo agora .

    
por proegssilb 13.11.2016 / 05:12

1 resposta

0

Então, houve algumas coisas que ajudaram, mas a principal coisa que conseguiu isso foi usar sort | uniq -c em vez de gawk, de acordo com Gregory Nisbet .

Também acabei usando o @ comment dave_thompson_085 sobre %código%. O sinalizador tr -sc '[:alpha:]' '\n' combina repetições, o que significa que não preciso excluir linhas em branco e -s inverte o conjunto de caracteres a ser encontrado. Um efeito colateral de -c é que você pode usar apenas um caractere de substituição, em vez de um conjunto. Também agradecemos a Dave por informações sobre grep e linha exata ( %código%). Se eu tivesse a reputação de defender esse comentário, eu o faria.

Por último, tive que usar algum código extra para remover entidades XML ( -c ) e remover o html (excesso -x ). Em &quot; , o novo comando sed é <ref /> . Cada expressão ( getArticleText para comandos em cadeia) lida com uma entidade HTML diferente. Eu tentei algumas opções mais completas (como usar perl como por StackOverflow ), mas eles não funcionaram no meu caso devido a problemas específicos da máquina. O script final pode ser visto em meu repositório wordcount .

O script foi concluído em 3hrs20min na minha máquina, mas também é uma AMD de 6 núcleos de anos atrás com um HDD. Sua milhagem pode variar, mas isso foi bom o suficiente para mim.

Evitarei aceitar essa resposta para que, se @Gregory Nisbet ou @ dave_thompson_085 quiserem postar suas próprias respostas, eles possam.

    
por 14.11.2016 / 03:38