2010-11-01 27 views
14

Me pregunto si hay una forma de concatenar archivos de texto unix más rápido que ejecutando cat?¿Cuál es la forma más rápida de grabar varios archivos?

Aquí está el problema que estoy enfrentando. Estoy procesando una secuencia de un archivo de texto ~ 100G en tamaño . Estoy tratando de mejorar el tiempo de ejecución dividiendo el archivo en cientos de archivos pequeños y procesándolos en paralelo. Al final, los archivos resultantes vuelven a estar juntos en orden. El tiempo de lectura/escritura del archivo en sí demora horas. Me gustaría encontrar una forma de mejorar lo siguiente:

cat file1 file2 file3 ... fileN >> newBigFile 
  1. Esto requiere el doble de espacio en disco como file1 ... fileN ocupa 100G, y luego newBigFile toma otro 100Gb, y luego file1. .. fileN obtiene eliminado

  2. los datos ya está en file1 ... fileN, haciendo las cat >> incurre leen y escribir momento en que todo lo que realmente necesito es para los cientos de archivos a vuelven a aparecer como 1 archivo ...

+0

Suena como que usted debe utilizar algo con un poco más músculo que una cáscara de Unix. –

+0

No tengo idea de lo que estoy hablando, pero ¿es posible manipular el registro del archivo o algo así? Como lo que tenía que hacer era no duplicar los datos, sino simplemente encadenar varios archivos de nuevo en 1? – Wing

Respuesta

4

solución rápida, pero no libre? Obtenga una unidad SSD o almacenamiento flash basado en PCIe. Si esto es algo que tiene que hacerse de manera regular, aumentar la velocidad de IO del disco va a ser la aceleración más rentable y más rápida que puede obtener.

+0

Gracias, pero desafortunadamente no puedo cambiar los servidores de archivos y hardware de la compañía ... – Wing

+2

Por supuesto que su circunstancia puede prohibir esto, pero si se presenta a la administración como una ADICIÓN a un almacenamiento en disco de servidores existente (en lugar de un reemplazo), puede ser considerado. Si puede tener una SSD que se usa solo para esta tarea y ahorra 2 horas de tiempo de procesamiento cada día, creo que estarían convencidos de los ahorros de costos. –

4

Tal vez dd sería más rápido porque no tendría que pasar cosas entre el gato y el caparazón. Algo así como:

mv file1 newBigFile 
dd if=file2 of=newBigFile seek=$(stat -c %s newBigFile) 
+1

definitivamente creo que dd, combinado con eliminar los archivos mientras los copia mientras Robie Basak lo sugiere, será la solución más recombinada, salvo la implementación de un comando personalizado de cp/unlink con mmap. Sin embargo, estoy convencido de que nada sería más eficiente que eliminar por completo la división. – frankc

1

todo lo que realmente necesito es para los cientos de archivos a reaparecer como 1 archivo ...

La razón por la que no es práctico para simplemente unir archivos de esa manera en un sistema de archivos nivel porque los archivos de texto no suelen llenar un bloque de disco exactamente, por lo que los datos en los archivos subsiguientes tendrían que moverse hacia arriba para llenar los espacios, causando un montón de lecturas/escrituras de todos modos.

4

¿Es posible que simplemente no divida el archivo? En su lugar, procese el archivo en fragmentos configurando el puntero del archivo en cada uno de sus trabajadores paralelos. Si el archivo debe procesarse de una manera orientada a la línea, eso lo hace más complicado, pero aún puede hacerse. Cada trabajador debe comprender que, en lugar de comenzar con la compensación que le asigna, primero debe buscar byte por byte en la siguiente nueva línea +1. Cada trabajador también debe comprender que no procesa la cantidad establecida de bytes que le asigna, sino que debe procesar la primera línea nueva después de la cantidad establecida de bytes que se le asignan para procesar.

La asignación real y la configuración del puntero de archivo es bastante sencilla. Si hay n trabajadores, cada uno procesa n bytes de tamaño de archivo y el puntero de archivo comienza en el número de trabajador * n/tamaño_archivo.

¿Hay alguna razón por la cual ese tipo de plan no sea suficiente?

+0

En lugar de modificar a los trabajadores, el shell podría proporcionar a los trabajadores un 'stdin' que ya es solo el segmento en el que debería trabajar, por ejemplo, usando' sed' para seleccionar un rango de línea.Si la salida necesita ser coordinada, GNU Parallel podría ayudar con esto. –

+0

Todo esto se hace en perl, donde la secuencia de comandos original intenta hacer manipulaciones de cadenas a través de todo el archivo 100G en serie. En este momento lo tengo dividiendo el archivo y procesando los fragmentos a través de fork(), pero ahora el tiempo de lectura/escritura está embotellando el tiempo de ejecución. No tengo que hacer la división inicial, supongo, como dijiste, pero aún tengo que escribir los fragmentos procesados ​​y luego volver a armarlos en 1 archivo, ¿verdad? – Wing

+0

Si no divido el archivo y hago que cada proceso secundario lea el archivo original 100G trabajando en líneas diferentes, ¿quedaré embotellado por 200 procesos intentando leer el mismo archivo? – Wing

6

Al concatenar los archivos de nuevo juntos, se podría eliminar los archivos pequeños a medida que se adjuntan:

for file in file1 file2 file3 ... fileN; do 
    cat "$file" >> bigFile && rm "$file" 
done 

Esto evitaría necesitan el doble de espacio.

No hay otra forma mágica de hacer que los archivos se concatenen mágicamente. La API del sistema de archivos simplemente no tiene una función que lo haga.

6

Si no necesita acceso aleatorio al archivo grande final (es decir, solo lo ha leído una vez de principio a fin), puede hacer que sus cientos de archivos intermedios aparezcan como uno solo. Donde normalmente hacer

$ consume big-file.txt 

lugar hacer

$ consume <(cat file1 file2 ... fileN) 

Esto utiliza Unix process substitution, a veces también llamados "canalizaciones con nombre anónimas."

También es posible que pueda ahorrar tiempo y espacio dividiendo su entrada y procesando al mismo tiempo; GNU Parallel tiene un --pipe switch que hará precisamente esto. También puede volver a ensamblar las salidas en un archivo grande, potencialmente usando menos espacio libre ya que solo necesita mantener número de núcleos piezas en el disco a la vez. Si literalmente ejecuta cientos de procesos al mismo tiempo, Parallel mejorará en gran medida su eficiencia al permitirle ajustar la cantidad de paralelismo de su máquina. Lo recomiendo altamente.

+0

No he probado esto, pero suena como la sugerencia más útil – Michael

+0

La sustitución del proceso se ve increíble porque no pone cosas en el disco. Entonces puede hacer "consumir <(cmd1 file1) <(cmd2 file2) <(cmd3 file3)". Sin embargo, aquí es equivalente al más tradicional "cat file1 file2 ... | consume". – dfrankow

1

Existe una excesiva concurrencia.

Una forma mejor de hacerlo sería utilizar lecturas de acceso aleatorio en el archivo en los rangos deseados y nunca dividirlo y procesar solo el número de archivos como la cantidad de CPU/núcleos físicos en la máquina. Eso es a menos que también esté inundando el disco con IOPS, entonces debe reducir hasta que el disco no sea el cuello de botella.

Lo que está haciendo es generar toneladas de IOPS y no hay forma de evitar la física de la misma.

2

Creo que esta es la manera más rápida de gato todos los archivos contenidos en la misma carpeta:

$ ls [path to folder] | while read p; do cat $p; done 
+0

Bonito =) Eso funcionó audaz. necesitaba un eco; antes del hecho. – Kieveli

Cuestiones relacionadas