Algún contexto por adelantado de dónde vengo. Los fragmentos de código están al final.
Cuando puedo, prefiero usar una herramienta de código abierto como H2O para hacer lecturas de archivos CSV paralelos de súper alto rendimiento, pero esta herramienta tiene un conjunto de características limitado. Terminé escribiendo mucho código para crear canalizaciones de ciencia de datos antes de alimentar al clúster H2O para el aprendizaje supervisado propiamente dicho.
He estado leyendo archivos como el conjunto de datos HIGGS de 8 GB del repositorio UCI e incluso archivos CSV de 40 GB para fines de ciencia de datos significativamente más rápido al agregar mucho paralelismo con el objeto de grupo de la biblioteca de multiprocesamiento y la función de mapa. Por ejemplo, la agrupación con búsquedas vecinas más cercanas y también los algoritmos de agrupación DBSCAN y Markov requieren cierta delicadeza de programación paralela para evitar algunos problemas de memoria y tiempo de reloj de pared muy desafiantes.
Por lo general, me gusta dividir el archivo en hileras en partes usando herramientas gnu primero y luego glob-filemask a todos para encontrarlos y leerlos en paralelo en el programa python. Yo uso algo así como más de 1000 archivos parciales comúnmente. Hacer estos trucos ayuda enormemente con la velocidad de procesamiento y los límites de memoria.
El pandas dataframe.read_csv tiene un solo subproceso, por lo que puede hacer estos trucos para hacer que los pandas sean bastante más rápidos ejecutando un mapa () para ejecución paralela. Puede usar htop para ver que con pandas secuenciales antiguos simples dataframe.read_csv, 100% de CPU en un solo núcleo es el cuello de botella real en pd.read_csv, no el disco en absoluto.
Debo agregar que estoy usando un SSD en el bus de tarjeta de video rápido, no un HD giratorio en el bus SATA6, más 16 núcleos de CPU.
Además, otra técnica que descubrí que funciona muy bien en algunas aplicaciones es que el archivo CSV paralelo lee todo dentro de un archivo gigante, comenzando cada trabajador con un desplazamiento diferente en el archivo, en lugar de dividir previamente un archivo grande en muchos archivos de partes. Use el archivo de búsqueda de python () y tell () en cada trabajador paralelo para leer el archivo de texto grande en tiras, en diferentes ubicaciones de inicio de byte y final de byte de desplazamiento en el archivo grande, todo al mismo tiempo al mismo tiempo. Puede hacer un finge regex en los bytes y devolver el recuento de saltos de línea. Esta es una suma parcial. Finalmente, sume las sumas parciales para obtener la suma global cuando la función de mapa regrese después de que los trabajadores hayan terminado.
A continuación se muestran algunos puntos de referencia de ejemplo que utilizan el truco de desplazamiento de byte paralelo:
Yo uso 2 archivos: HIGGS.csv es de 8 GB. Es del repositorio de aprendizaje automático UCI. all_bin .csv tiene 40.4 GB y es de mi proyecto actual. Utilizo 2 programas: el programa GNU wc que viene con Linux y el programa puro python fastread.py que desarrollé.
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
Eso es alrededor de 4.5 GB / s, o 45 Gb / s, velocidad de extracción de archivos. Eso no es un disco duro giratorio, amigo mío. Eso es realmente un SSD Samsung Pro 950.
A continuación se muestra el punto de referencia de velocidad para el mismo archivo que cuenta gnu wc, un programa compilado en C puro.
Lo que es genial es que puedes ver que mi programa de Python puro esencialmente coincidía con la velocidad del programa C compilado por gnu wc en este caso. Python se interpreta pero C se compila, por lo que esta es una hazaña de velocidad bastante interesante, creo que estaría de acuerdo. Por supuesto, wc realmente necesita ser cambiado a un programa paralelo, y luego realmente superaría a mi programa Python. Pero tal como está hoy, gnu wc es solo un programa secuencial. Haces lo que puedes y Python puede hacerlo en paralelo hoy. La compilación de Cython podría ayudarme (en algún otro momento). Además, los archivos asignados a la memoria aún no se exploraron.
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
Conclusión: la velocidad es buena para un programa de Python puro en comparación con un programa en C. Sin embargo, no es lo suficientemente bueno para usar el programa Python puro sobre el programa C, al menos con el propósito de contar líneas. En general, la técnica se puede utilizar para otro procesamiento de archivos, por lo que este código de Python sigue siendo bueno.
Pregunta: ¿Compilar la expresión regular solo una vez y pasarla a todos los trabajadores mejorará la velocidad? Respuesta: El precompilación de Regex NO ayuda en esta aplicación. Supongo que la razón es que la sobrecarga de la serialización y creación de procesos para todos los trabajadores es dominante.
Una cosa más. ¿La lectura de archivos CSV paralelos incluso ayuda? ¿Es el disco el cuello de botella o es la CPU? Muchas de las llamadas respuestas mejor calificadas en stackoverflow contienen la sabiduría de desarrollo común de que solo necesita un hilo para leer un archivo, lo mejor que puede hacer, dicen. ¿Están seguros, sin embargo?
Vamos a averiguar:
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
Oh si, si lo hace. La lectura de archivos paralelos funciona bastante bien. ¡Bueno, allá vas!
PD. En caso de que algunos de ustedes quisieran saber, ¿qué pasaría si el balanceFactor fuera 2 cuando utilizara un solo proceso de trabajo? Bueno, es horrible.
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 clave del 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
La definición para PartitionDataToWorkers es simplemente un código secuencial ordinario. Lo dejé fuera en caso de que alguien más quiera practicar algo sobre cómo es la programación paralela. Regale gratuitamente las partes más difíciles: el código paralelo probado y en funcionamiento, para su beneficio de aprendizaje.
Gracias a: El proyecto de código abierto H2O, de Arno y Cliff y el personal de H2O por su excelente software y videos instructivos, que me han inspirado para este lector de desplazamiento de byte paralelo de alto rendimiento de Python puro como se muestra arriba. H2O realiza la lectura paralela de archivos usando java, es ejecutable por los programas python y R, y es una locura rápida, más rápida que cualquier otra cosa en el planeta al leer grandes archivos CSV.