2010-07-08 10 views
6

Mi programa lee docenas de archivos muy grandes en paralelo, solo una línea a la vez. Parece que el mayor cuello de botella de rendimiento es el tiempo de búsqueda de HDD de un archivo a otro (aunque no estoy completamente seguro de cómo verificar esto), así que creo que sería más rápido si pudiera almacenar la entrada.Cómo cambiar el tamaño del búfer con boost :: iostreams?

estoy usando C++ código como este para leer mis archivos a través de impulso :: iostreams "corrientes de filtrado":

input = new filtering_istream; 
input->push(gzip_decompressor()); 
file_source in (fname); 
input->push(in); 

De acuerdo con la documentation, file_source no tiene ninguna manera de establecer el tamaño del búfer, pero filtering_stream :: empuje parece:

void push(const T& t, 
    std::streamsize buffer_size, 
    std::streamsize pback_size); 

así que traté input->push(in, 1E9) y de hecho uso de la memoria de mi programa se dispararon, pero la velocidad no cambió en absoluto.

¿Estaba simplemente equivocado que leer el almacenamiento en búfer mejoraría el rendimiento? ¿O lo hice mal? ¿Puedo almacenar en buffer un file_source directamente, o necesito crear un filtering_streambuf? Si esto último, ¿cómo funciona eso? La documentación no está exactamente llena de ejemplos.

Respuesta

2

Debes hacer un perfil también para ver dónde está el cuello de botella.

Quizás esté en el kernel, tal vez esté al límite de su hardware. Hasta que lo perfile para descubrir que está tropezando en la oscuridad.

EDIT:

Ok, una respuesta más a fondo este tiempo, entonces. De acuerdo con la documentación de Boost.Iostreams basic_file_source es solo un contenedor alrededor de std::filebuf, que a su vez se basa en std::streambuf. Para citar la documentación:

CopyConstruible y Contenedor asignable para std :: basic_filebuf abierto en modo de solo lectura.

streambuf proporciona un método pubsetbuf (no la mejor referencia tal vez, pero el primer Google se volvió hacia arriba) que se puede, al parecer, utilizar para controlar el tamaño del búfer.

Por ejemplo:

#include <fstream> 

int main() 
{ 
    char buf[4096]; 
    std::ifstream f; 
    f.rdbuf()->pubsetbuf(buf, 4096); 
    f.open("/tmp/large_file", std::ios::binary); 

    while(!f.eof()) 
    { 
     char rbuf[1024]; 
     f.read(rbuf, 1024); 
    } 

    return 0; 
} 

En mis pruebas (optimizaciones de descanso, sin embargo) De hecho, me puso peor rendimiento con un buffer de 4096 bytes de un buffer de 16 bytes, pero tu caso es distinto - un buen ejemplo de por qué usted debería siempre perfil primero :)

Pero, como dices, el basic_file_sink no proporciona ningún medio para acceder a esto ya que oculta el filebuf subyacente en su private part.

Si usted piensa que esto es un error que podía:

  1. instan a los desarrolladores de Boost para exponer dicha funcionalidad, utilice la lista de correo o el trac.
  2. Cree su propia envoltura filebuf que expone el tamaño del búfer. Hay un section en el tutorial que explica cómo escribir fuentes personalizadas que pueden ser un buen punto de partida.
  3. Escriba una fuente personalizada basada en lo que sea, que hace todo el almacenamiento en caché que desee.

Recuerde que su disco duro y el kernel ya almacenan en caché y almacenan en búfer las lecturas de archivos, lo que no creo que aumente mucho el rendimiento del almacenamiento en caché aún más.

Y para terminar, una palabra sobre la creación de perfiles. Hay un montón de potentes herramientas de creación de perfiles disponibles para Linux y ni siquiera conozco la mitad de ellas por su nombre, pero por ejemplo está iotop, que es bastante ordenado porque es muy fácil de usar. Es bastante parecido a la parte superior, pero en su lugar muestra métricas relacionadas con el disco. Por ejemplo:

Total DISK READ: 31.23 M/s | Total DISK WRITE: 109.36 K/s 
TID PRIO USER  DISK READ DISK WRITE SWAPIN  IO> COMMAND   
19502 be/4 staffan 31.23 M/s 0.00 B/s 0.00 % 91.93 % ./apa 

me dice que mi progam gasta más del 90% de su tiempo de espera para IO, es decir, es obligado IO. Si necesitas algo más poderoso, estoy seguro de que Google puede ayudarte.

Y recuerde que la evaluación comparativa de un caché frío o caliente afecta en gran medida el resultado.

+0

Tal vez esta es una pregunta para otra publicación, pero ¿cómo podría perfilarlo? Sé cómo usar gprof, pero solo me dice sobre el tiempo de CPU, y aquí estoy bastante seguro de que el cuello de botella es E/S de disco. O si alguien puede decirme cómo configurar correctamente el tamaño del búfer, solo puedo probarlo y ver si me ayuda. – user387250

+0

@jwfoley: Me gusta [Valgrind] (http://valgrind.org/) callgrind profiler. En lo que respecta a mi experiencia (lea como: no puedo garantizar nada), también informa el tiempo pasado en las llamadas al kernel, algo que nunca podría hacer gprof. Lo uso para perfilar una aplicación con OpenGL, por ejemplo, e informa correctamente el tiempo pasado en el código del controlador de video. Es muy fácil de usar (valgrind --tool = callgrind ./your-app). Utilice [KCachegrind] (http://kcachegrind.sourceforge.net/html/Home.html) para interpretar los resultados. El único inconveniente es que su aplicación se ejecutará 20 veces más o menos durante el perfilado. – Staffan

+0

@Staffan: Bien, probé callgrind + KCachegrind y estoy impresionado con el generador de perfiles, pero todavía no sé lo que estoy buscando. Los resultados son bastante similares a los de gprof. Algo llamado T.3577 tiene un alto "Incl." pero bajo "Yo"; la mayor parte de su tiempo parece gastarse en std :: basic_ios. Tal vez esa es la E/S de disco? Todavía me gustaría una respuesta a mi pregunta original de cómo configurar el tamaño del búfer. Si es fácil, entonces puedo probarlo y ver si me ayuda, pero sería útil saber de todos modos. – user387250

Cuestiones relacionadas