2009-04-09 14 views
11

Estoy escribiendo un programa donde el rendimiento es bastante importante, pero no crítico. Actualmente estoy leyendo en texto de FILE* línea por línea y uso fgets para obtener cada línea. Después de usar algunas herramientas de rendimiento, descubrí que del 20% al 30% de las veces que se ejecuta mi aplicación, está dentro de fgets.¿Lees una línea de entrada más rápido que fgets?

¿Hay formas más rápidas de obtener una línea de texto? Mi aplicación es de un solo subproceso sin intenciones de usar múltiples hilos. La entrada puede ser desde stdin o desde un archivo. Gracias por adelantado.

+0

¿Cuál es la longitud promedio (y posible stdev) de las líneas que analiza su programa? Esto ayuda a determinar la forma más rápida de acceder a ellos. – Juliano

+0

@Juliano, las líneas tienen siempre menos de 260 caracteres de longitud. Ya he evitado un ciclo de construcción de líneas. – dreamlax

+0

¿Controla el formato de entrada? ¿Podrías hacerlo más compacto? – Dave

Respuesta

7

No dice en qué plataforma se encuentra, pero si es de tipo UNIX, entonces puede intentar la llamada al sistema read(), que no realiza la capa adicional de almacenamiento en el búfer que fgets() et al do Esto puede acelerar un poco las cosas, por otro lado, puede ralentizar las cosas, la única manera de descubrirlo es chupar y ver.

+0

Este resultó ser el método más rápido de todos. Finalmente bajé por esta ruta. Era más simple de lo que había pensado hacer "mi propio buffering" y resultó ser mucho, mucho más rápido (casi 4 veces) que usar 'fgets()'. – dreamlax

+0

Irónicamente, para mí, pread tuvo un rendimiento 4 veces peor que los errores. – abirvalg

2

Si los datos provienen del disco, podría estar vinculado a IO.

Si ese es el caso, obtenga un disco más rápido (pero primero compruebe que está aprovechando al máximo el existente ... algunas distribuciones de Linux no optimizan el acceso al disco de fábrica (hdparm)) , coloque los datos en la memoria (por ejemplo, copiándolos en un disco RAM) antes de tiempo o prepárese para esperar.


Si no está vinculado a IO, puede estar perdiendo mucho tiempo copiando. Puede beneficiarse de los llamados métodos de copia cero. Algo así como la memoria mapean el archivo y solo lo acceden a través de punteros.

Eso es un poco más allá de mi experiencia, por lo que debe leer un poco o esperar a obtener más ayuda con conocimientos.

BTW-- Puede que te estés metiendo en más trabajo de lo que vale el problema; tal vez una máquina más rápida sería resolver todos sus problemas ...

NB-- No es claro que se puede asignar memoria de la entrada estándar, ya sea ...

+0

A veces proviene del disco, a veces se alimenta a través de stdin, pero en ambos casos el tiempo que se pasa en fgets es más o menos el mismo. Incluso la creación de un disco RAM para el archivo no acelera demasiado las cosas. – dreamlax

+0

Después de la edición: el problema es que esta aplicación se ejecutará en la computadora del usuario final, por eso el rendimiento es bastante importante. – dreamlax

3

Usted puede tratar de reducir al mínimo la cantidad de tiempo que pasa leyendo desde el disco leyendo grandes cantidades de datos en la RAM y luego trabajando en eso. Leer desde el disco es lento, así que minimiza la cantidad de tiempo que pasas haciéndolo leyendo (idealmente) todo el archivo una vez, y luego trabajando en ello.

Como la forma en que la memoria caché de la CPU minimiza el tiempo que la CPU realmente regresa a la RAM, puede usar la RAM para minimizar el número de veces que va al disco.

+0

Stdio ya está en el búfer, ¿no es así? –

+0

Creo que sí, pero estoy seguro de que es menos de un megabyte, por lo que leer más de eso todavía debería ayudar. – GManNickG

2

Dependiendo de su entorno, el uso de setvbuf() para aumentar el tamaño del búfer interno utilizado por las secuencias de archivos puede o no mejorar el rendimiento.

Esta es la sintaxis -

setvbuf (InputFile, NULL, _IOFBF, BUFFER_SIZE); 

Dónde InputFile es un archivo * a un archivo que acaba de abrir con fopen() y BUFFER_SIZE es el tamaño de la memoria intermedia (que se le asigna en la presente convocatoria de usted).

Puede probar varios tamaños de búfer para ver si alguno tiene una influencia positiva. Tenga en cuenta que esto es completamente opcional, y su tiempo de ejecución no puede hacer absolutamente nada con esta llamada.

4
  1. Uso fgets_unlocked(), pero lea con atención lo que hace primero

  2. Obtener los datos con fgetc() o fgetc_unlocked() en lugar de fgets().Con fgets(), sus datos se copian en la memoria dos veces, primero en la biblioteca de tiempo de ejecución de C desde un archivo a un búfer interno (la E/S de flujo se almacena en búfer) y luego desde ese búfer interno a una matriz en su programa

+0

Gracias por la sugerencia, pero olvidé mencionar que estoy usando Mac OS X. fgets_unlocked no está disponible porque es una extensión de GNU. Examinaré usar fgetc_unlocked. – dreamlax

+0

Bueno, OS X está ejecutando GCC, debería obtener las extensiones de GNU, ¿verdad? –

+1

@Martin: no es una extensión del compilador GNU, sino la biblioteca GNU C runtime. – dreamlax

4

Lea todo el archivo de una vez en un búfer.

Procese las líneas de ese búfer.

Esa es la solución más rápida posible.

0

Si el sistema operativo lo admite, puede intentar la lectura asíncrona de archivos, es decir, el archivo se lee en la memoria mientras la CPU está ocupada haciendo otra cosa. Por lo tanto, el código es algo como: ​ ​ ​ ​ ​

start asynchronous read 
loop: 
    wait for asynchronous read to complete 
    if end of file goto exit 
    start asynchronous read 
    do stuff with data read from file 
    goto loop 
exit: 

Si usted tiene más de una CPU a continuación, una CPU lee el archivo y analiza los datos en las líneas, la otra CPU toma cada línea y la procesa .

0

Mire en fread(). Se lee mucho más rápido para mí, especialmente si el buffer para fread está configurado en 65536. Contras: tienes que trabajar mucho y esencialmente escribir tu propia función getline para convertir de lectura binaria a texto. Salida: file I/O

Cuestiones relacionadas