Algum contexto na frente de onde eu estou vindo. Os trechos de código estão no final.
Quando posso, prefiro usar uma ferramenta de código aberto como o H2O para fazer leituras paralelas de arquivos CSV de alto desempenho, mas essa ferramenta é limitada no conjunto de recursos. Acabo escrevendo muito código para criar pipelines de ciência de dados antes de alimentar o cluster de H2O para o aprendizado supervisionado adequado.
Tenho lido arquivos como o conjunto de dados HIGGS de 8 GB do repositório UCI e até arquivos CSV de 40 GB para fins de ciência de dados significativamente mais rapidamente, adicionando muito paralelismo ao objeto de pool da biblioteca de multiprocessamento e à função de mapa. Por exemplo, o agrupamento com pesquisas de vizinhos mais próximos e também os algoritmos de agrupamento DBSCAN e Markov requer alguma sutileza de programação paralela para contornar alguns problemas seriamente desafiadores da memória e do relógio de parede.
Eu geralmente gosto de dividir o arquivo em linhas em partes usando as ferramentas gnu primeiro e depois a glob-filemask todas para encontrar e ler em paralelo no programa python. Eu uso algo como 1000 arquivos parciais comumente. Fazer esses truques ajuda imensamente com a velocidade de processamento e os limites de memória.
O dataframe.read_csv do pandas é de thread único, para que você possa executar esses truques para tornar o panda muito mais rápido executando um map () para execução paralela. Você pode usar o htop para ver que, com os antigos pandas sequenciais dataframe.read_csv, 100% da CPU em apenas um núcleo é o gargalo real no pd.read_csv, e não o disco.
Devo acrescentar que estou usando um SSD no barramento rápido da placa de vídeo, não um HD giratório no barramento SATA6, além de 16 núcleos de CPU.
Além disso, outra técnica que eu descobri que funciona muito bem em alguns aplicativos é a leitura paralela de arquivos CSV em um arquivo gigante, iniciando cada trabalhador com um deslocamento diferente no arquivo, em vez de pré-dividir um arquivo grande em muitos arquivos de peça. Use o arquivo python seek () e tell () em cada trabalhador paralelo para ler o arquivo de texto grande em tiras, em diferentes locais de deslocamento de bytes e de início e de bytes no arquivo grande, tudo ao mesmo tempo simultaneamente. Você pode fazer uma localização regex nos bytes e retornar a contagem de feeds de linha. Esta é uma soma parcial. Finalmente, some as somas parciais para obter a soma global quando a função de mapa retornar após a conclusão dos trabalhadores.
A seguir, alguns exemplos de benchmarks usando o truque de deslocamento de bytes paralelos:
Eu uso 2 arquivos: HIGGS.csv é 8 GB. É do repositório de aprendizado de máquina da UCI. all_bin .csv tem 40,4 GB e é do meu projeto atual. Eu uso dois programas: o programa GNU wc, que acompanha o Linux, e o programa python fastread.py puro, que eu desenvolvi.
HP-Z820:/mnt/fastssd/fast_file_reader$ ls -l /mnt/fastssd/nzv/HIGGS.csv
-rw-rw-r-- 1 8035497980 Jan 24 16:00 /mnt/fastssd/nzv/HIGGS.csv
HP-Z820:/mnt/fastssd$ ls -l all_bin.csv
-rw-rw-r-- 1 40412077758 Feb 2 09:00 all_bin.csv
ga@ga-HP-Z820:/mnt/fastssd$ time python fastread.py --fileName="all_bin.csv" --numProcesses=32 --balanceFactor=2
2367496
real 0m8.920s
user 1m30.056s
sys 2m38.744s
In [1]: 40412077758. / 8.92
Out[1]: 4530501990.807175
Isso significa uma velocidade de slurping de arquivo de 4,5 GB / s ou 45 Gb / s. Não há disco rígido girando, meu amigo. Na verdade, é um SSD Samsung Pro 950.
Abaixo está a referência de velocidade para o mesmo arquivo sendo contado em linha pelo gnu wc, um programa compilado em C puro.
O legal é que você pode ver que meu programa python puro correspondia essencialmente à velocidade do programa C compilado pelo gnu wc nesse caso. Python é interpretado, mas C é compilado, então esse é um feito bastante interessante de velocidade, acho que você concorda. Obviamente, o wc realmente precisa ser alterado para um programa paralelo e, em seguida, seria realmente melhor do que o meu programa python. Mas como está hoje, o gnu wc é apenas um programa seqüencial. Você faz o que pode e o python pode fazer paralelo hoje. A compilação do Cython pode me ajudar (por outro tempo). Os arquivos mapeados na memória também não foram explorados.
HP-Z820:/mnt/fastssd$ time wc -l all_bin.csv
2367496 all_bin.csv
real 0m8.807s
user 0m1.168s
sys 0m7.636s
HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=16 --balanceFactor=2
11000000
real 0m2.257s
user 0m12.088s
sys 0m20.512s
HP-Z820:/mnt/fastssd/fast_file_reader$ time wc -l HIGGS.csv
11000000 HIGGS.csv
real 0m1.820s
user 0m0.364s
sys 0m1.456s
Conclusão: A velocidade é boa para um programa python puro comparado a um programa em C. No entanto, não é bom o suficiente usar o programa python puro sobre o programa C, pelo menos para fins de contagem de linha. Geralmente, a técnica pode ser usada para outro processamento de arquivo, portanto esse código python ainda é bom.
Pergunta: Compilar o regex apenas uma vez e passá-lo a todos os trabalhadores aumentará a velocidade? Resposta: A pré-compilação do Regex NÃO ajuda neste aplicativo. Suponho que a razão é que a sobrecarga da serialização e criação de processos para todos os trabalhadores esteja dominando.
Mais uma coisa. A leitura paralela de arquivos CSV ajuda mesmo? O disco é o gargalo ou a CPU? Muitas das chamadas respostas mais bem classificadas no stackoverflow contêm a sabedoria comum do desenvolvedor de que você só precisa de um thread para ler um arquivo, o melhor que pode fazer, dizem eles. Eles têm certeza?
Vamos descobrir:
HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=16 --balanceFactor=2
11000000
real 0m2.256s
user 0m10.696s
sys 0m19.952s
HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=1 --balanceFactor=1
11000000
real 0m17.380s
user 0m11.124s
sys 0m6.272s
Ah sim, sim. A leitura paralela de arquivos funciona muito bem. Bem, lá vai você!
Ps. Caso alguns de vocês quisessem saber, e se o balanceFactor fosse 2 ao usar um único processo de trabalho? Bem, é horrível:
HP-Z820:/mnt/fastssd/fast_file_reader$ time python fastread.py --fileName="HIGGS.csv" --numProcesses=1 --balanceFactor=2
11000000
real 1m37.077s
user 0m12.432s
sys 1m24.700s
Partes principais do programa python fastread.py:
fileBytes = stat(fileName).st_size # Read quickly from OS how many bytes are in a text file
startByte, endByte = PartitionDataToWorkers(workers=numProcesses, items=fileBytes, balanceFactor=balanceFactor)
p = Pool(numProcesses)
partialSum = p.starmap(ReadFileSegment, zip(startByte, endByte, repeat(fileName))) # startByte is already a list. fileName is made into a same-length list of duplicates values.
globalSum = sum(partialSum)
print(globalSum)
def ReadFileSegment(startByte, endByte, fileName, searchChar='\n'): # counts number of searchChar appearing in the byte range
with open(fileName, 'r') as f:
f.seek(startByte-1) # seek is initially at byte 0 and then moves forward the specified amount, so seek(5) points at the 6th byte.
bytes = f.read(endByte - startByte + 1)
cnt = len(re.findall(searchChar, bytes)) # findall with implicit compiling runs just as fast here as re.compile once + re.finditer many times.
return cnt
A definição para PartitionDataToWorkers é apenas um código seqüencial comum. Eu o deixei de fora caso alguém mais quisesse praticar como é a programação paralela. Dei de graça as partes mais difíceis: o código paralelo testado e funcionando, para seu benefício de aprendizado.
Obrigado a: O projeto H2O de código aberto, de Arno e Cliff e a equipe de H2O, por seus ótimos softwares e vídeos instrutivos, que me deram a inspiração para este leitor de offset de bytes paralelos de alto desempenho python puro, como mostrado acima. O H2O faz a leitura paralela de arquivos usando java, é passível de chamada pelos programas python e R e é louco, rápido, mais rápido do que qualquer coisa no planeta ao ler grandes arquivos CSV.