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 / user-182552

wviana's questions

Martin Hope
wviana
Asked: 2022-04-10 19:53:35 +0800 CST

Quem matou meu tipo? ou Como contar valores distintos de forma eficiente de uma coluna csv

  • 14

Estou fazendo algum processamento tentando obter quantas linhas diferentes em um arquivo contendo 160.353.104 linhas. Aqui está minha saída de pipeline e stderr.

$ tail -n+2 2022_place_canvas_history.csv | cut -d, -f2 | tqdm --total=160353104 |\
  sort -T. -S1G | tqdm --total=160353104 | uniq -c | sort -hr > users

100%|████████████████████████████| 160353104/160353104 [0:15:00<00:00, 178051.54it/s]
 79%|██████████████████████      | 126822838/160353104 [1:16:28<20:13, 027636.40it/s]

zsh: done tail -n+2 2022_place_canvas_history.csv | cut -d, -f2 | tqdm --total=160353104 | 
zsh: killed sort -T. -S1G | 
zsh: done tqdm --total=160353104 | uniq -c | sort -hr > users

Minha linha de comando PS1 ou PS2 imprimiu os códigos de retorno de todos os processos do pipeline. ✔ 0|0|0|KILL|0|0|0O primeiro caractere é uma marca de seleção verde que significa que o último processo retornou 0 (sucesso). Outros números são códigos de retorno para cada um dos processos em pipeline, na mesma ordem. Então, notei que meu quarto comando obteve KILLstatus, este é meu comando de classificação sort -T. -S1Gdefinindo o diretório local para armazenamento temporário e buffer de até 1GiB.

A questão é, por que ele retornou KILL, isso significa que algo enviado KILL SIGNa ele? Existe uma maneira de saber "quem matou"?

Atualizações

Depois de ler Marcus Müller Answer , primeiro tentei carregar os dados no Sqlite.

Então, talvez este seja um bom momento para lhe dizer que, não, não use um fluxo de dados baseado em CSV. Um simples

sqlite3 place.sqlite

e nesse shell (supondo que seu CSV tenha uma linha de título que o SQLite possa usar para determinar as colunas) (é claro, substitua $second_column_name pelo nome dessa coluna)

.import 022_place_canvas_history.csv canvas_history --csv
SELECT $second_column_name, count($second_column_name)   FROM canvas_history 
GROUP BY $second_column_name;

Isso estava demorando muito, então deixei processando e fui fazer outras coisas. Enquanto eu pensava mais sobre este outro parágrafo de Marcus Müller Resposta :

Você só quer saber com que frequência cada valor apareceu na segunda coluna. Classificar isso antes só acontece porque sua ferramenta ( uniq -c) é ruim e precisa que as linhas sejam classificadas antes (literalmente não há uma boa razão para isso. Apenas não está implementado que ela possa conter um mapa de valores e sua frequência e aumentar isso à medida que eles aparecer).

Então eu pensei, eu posso implementar isso. Quando voltei ao computador, meu processo de importação do Sqlite parou por causa de um SSH Broken Pip, acho que ele não transmitiu dados por um longo tempo, fechou a conexão. Ok, que boa oportunidade para implementar um contador usando um dict/map/hashtable. Então eu escrevi o seguinte distinctarquivo:

#!/usr/bin/env python3
import sys

conter = dict()

# Create a key for each distinct line and increment according it shows up. 
for l in sys.stdin:
    conter[l] = conter.setdefault(l, 0) + 1 # After Update2 note: don't do this, do just `couter[l] = conter.get(l, 0) + 1`

# Print entries sorting by tuple second item ( value ), in reverse order
for e in sorted(conter.items(), key=lambda i: i[1], reverse=True):
    k, v = e
    print(f'{v}\t{k}')

Então eu usei pelo pipeline de comando follow.

tail -n+1 2022_place_canvas_history.csv | cut -d, -f2 | tqdm --total=160353104 | ./distinct > users2

Estava indo muito rápido, projeção de tqdmmenos de 30 minutos, mas quando chegou em 99% estava ficando cada vez mais lento. Este processo estava usando muita RAM, cerca de 1,7 GB. Máquina que estou trabalhando com esses dados, a máquina que tenho armazenamento suficiente, é um VPS com apenas 2GiB de RAM e ~1TiB de armazenamento. Pensei que poderia estar ficando tão lento porque o SO estava tendo que lidar com essa memória enorme, talvez fazendo alguma troca ou outras coisas. Eu esperei de qualquer maneira, quando finalmente chegou em 100% no tqdm, todos os dados foram enviados para o ./distinctprocesso, depois de alguns segundos obtive a seguinte saída:

160353105it [30:21, 88056.97it/s]                                                                                            
zsh: done       tail -n+1 2022_place_canvas_history.csv | cut -d, -f2 | tqdm --total=160353104 | 
zsh: killed     ./distinct > users2

Desta vez, principalmente por causa do assassino sem memória, como visto na seção Marcus Müller Answer TLDR.

Então, acabei de verificar e não tenho swap ativado nesta máquina. Desativei depois de concluir sua configuração com dmcrypt e LVM, pois você pode obter mais informações nestas minhas respostas .

Então, o que estou pensando é habilitar minha partição de troca LVM e tentar executá-la novamente. Também em algum momento acho que vi tqdm usando 10GiB de RAM. Mas tenho certeza de que vi erroneamente ou btopsaída confusa, pois depois mostrou apenas 10MiB, não acho que o tqdm usaria muita memória, pois apenas conta e atualiza algumas estatísticas ao ler um novo arquivo \n.

No comentário de Stéphane Chazelas a esta questão dizem:

Os logs do sistema possivelmente lhe dirão.

Gostaria de saber mais sobre isso, devo encontrar algo no journalctl? Se for o caso, como fazer?

De qualquer forma, como Marcus Müller Answer diz, carregar o csv no Sqlite pode ser de longe a solução mais inteligente, pois permitirá operar em dados de várias maneiras e provavelmente tem alguma maneira inteligente de importar esses dados sem falta de memória.

Mas agora estou duas vezes curioso sobre como descobrir por que o processo foi morto, como quero saber sobre o meu sort -T. -S1Ge agora sobre o meu ./distinct, o último quase certo que era sobre memória. Então, como verificar os logs que dizem por que esses processos foram eliminados?

Atualização2

Então, habilitei minha partição SWAP e aceitei a sugestão de Marcus Müller deste comentário de pergunta. Usando coleções de pythons.Counter. Então meu novo código ( distinct2) fica assim:

#!/usr/bin/env python3
from collections import Counter
import sys

print(Counter(sys.stdin).most_common())

Então, eu executei o Gnu Screen para que, mesmo se eu obtivesse um pipe quebrado novamente, eu pudesse simplesmente retomar a sessão e executá-lo no seguinte pipeline:

tail -n+1 2022_place_canvas_history.csv | cut -d, -f2 | tqdm --total=160353104 --unit-scale=1 | ./distinct2 | tqdm --unit-scale=1 > users5

Isso me deu a seguinte saída:

160Mit [1:07:24, 39.6kit/s]
1.00it [7:08:56, 25.7ks/it]

Como você pode ver, levou muito mais tempo para classificar os dados do que contá-los. Uma outra coisa que você pode notar é que a saída da segunda linha do tqdm mostra apenas 1.00it, significa que tem apenas uma única linha. Então eu verifiquei o arquivo user5 usando head:

head -c 150 users5 
[('kgZoJz//JpfXgowLxOhcQlFYOCm8m6upa6Rpltcc63K6Cz0vEWJF/RYmlsaXsIQEbXrwz+Il3BkD8XZVx7YMLQ==\n', 795), ('JMlte6XKe+nnFvxcjT0hHDYYNgiDXZVOkhr6KT60EtJAGa

Como você pode ver, ele imprimiu toda a lista de tuplas em uma única linha. Para resolver isso eu usei o bom e velho sed como segue sed 's/),/)\n/g' users5 > users6. Depois disso, verifiquei o conteúdo de users6 usando head, como segue com sua saída:

$ head users6
[('kgZoJz/...c63K6Cz0vEWJF/RYmlsaXsIQEbXrwz+Il3BkD8XZVx7YMLQ==\n', 795)
 ('JMlte6X...0EtJAGaezxc4e/eah6JzTReWNdTH4fLueQ20A4drmfqbqsw==\n', 781)
 ('LNbGhj4...apR9YeabE3sAd3Rz1MbLFT5k14j0+grrVgqYO1/6BA/jBfQ==\n', 777)
 ('K54RRTU...NlENRfUyJTPJKBC47N/s2eh4iNdAKMKxa3gvL2XFqCc9AqQ==\n', 767)
 ('8USqGo1...1QSbQHE5GFdC2mIK/pMEC/qF1FQH912SDim3ptEFkYPrYMQ==\n', 767)
 ('DspItMb...abcd8Z1nYWWzGaFSj7UtRC0W75P7JfJ3W+4ne36EiBuo2YQ==\n', 766)
 ('6QK00ig...abcfLKMUNur4cedRmY9wX4vL6bBoV/JW/Gn6TRRZAJimeLw==\n', 765)
 ('VenbgVz...khkTwy/w5C6jodImdPn6bM8izTHI66HK17D4Bom33ZrwuGQ==\n', 758)
 ('jjtKU98...Ias+PeaHE9vWC4g7p2KJKLBdjKvo+699EgRouCbeFjWsjKA==\n', 730)
 ('VHg2OiSk...3c3cr2K8+0RW4ILyT1Bmot0bU3bOJyHRPW/w60Y5so4F1g==\n', 713)

Bom o suficiente para trabalhar mais tarde. Agora acho que devo adicionar uma atualização depois de tentar verificar quem matou meu tipo usando dmesg ou journalctl. Eu também estou querendo saber se existe uma maneira de tornar este script mais rápido. Talvez criando um threadpool, mas tenho que verificar o comportamento do dict de pythons, também pensei em outras estruturas de dados, pois a coluna que estou contando é uma string de largura fixa, talvez usando uma lista para armazenar a frequência de cada user_hash diferente. Também li a implementação python do Counter, é apenas um dict, praticamente a mesma implementação que eu tinha antes, mas em vez de usar dict.setdefaultapenas used dict[key] = dict.get(key, 0) + 1, foi um uso incorreto de setdefaultnenhuma necessidade real para esse cenário.

Atualização3

Então eu estou ficando tão fundo na toca do coelho, totalmente perdido o foco do meu objetivo. Comecei a procurar por ordenação mais rápida, talvez escrever algum C ou Rust, mas percebi que já tenho os dados que vim buscar processados. Então, estou aqui para mostrar a saída do dmesg e uma dica final sobre o script python. A dica é: pode ser melhor apenas contar usando dict ou Counter, do que classificar sua saída usando a ferramenta de classificação gnu. Provavelmente classificar classifica mais rápido que a função buitin classificada em python.

Sobre o dmesg, foi bem simples encontrar memória insuficiente, basta sudo dmesg | lesspressionar Gpara ir até o fim, do ?que pesquisar de volta, do que pesquisar por Outstring. Encontrei dois deles, um para o meu script python e outro para o meu tipo, aquele que iniciou esta pergunta. Aqui estão essas saídas:

[1306799.058724] Out of memory: Killed process 1611241 (sort) total-vm:1131024kB, anon-rss:1049016kB, file-rss:0kB, shmem-rss:0kB, UID:1000 pgtables:2120kB oom_score_adj:0
[1306799.126218] oom_reaper: reaped process 1611241 (sort), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB
[1365682.908896] Out of memory: Killed process 1611945 (python3) total-vm:1965788kB, anon-rss:1859264kB, file-rss:0kB, shmem-rss:0kB, UID:1000 pgtables:3748kB oom_score_adj:0
[1365683.113366] oom_reaper: reaped process 1611945 (python3), now anon-rss:0kB, file-rss:0kB, shmem-rss:0kB

É isso, muito obrigado por ajudar até agora, espero que ajude os outros também.

pipe python
  • 2 respostas
  • 3667 Views

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