2012-07-09 14 views
7

Tengo una duda bastante básica. A menudo, tengo que escribir aplicaciones que usan E/S de archivos almacenados en búfer y cada vez que me enfrento al dilema de elegir el tamaño del búfer y termino haciendo pruebas y errores a menudo con resultados bastante desagradables. Quiero saber si hay algún método o algoritmo que pueda determinar automáticamente el tamaño de búfer óptimo para el trabajo en función de la plataforma subyacente, como lo hace Teracopy cuando se manejan archivos en Windows. Principalmente uso Qt para la GUI.Selección automática del tamaño del búfer para E/S de archivos

Si es posible un pequeño ejemplo en C/C++/C#/Java es muy apreciada!

Gracias!

Respuesta

15

En Java el óptimo es por lo general alrededor del tamaño de la caché L1 que suele ser de 32 KB. En Java, al menos elegir 1024 bytes o 1 MB no hace mucha diferencia (< 20%)

Si está leyendo datos secuencialmente, generalmente su sistema operativo es lo suficientemente inteligente como para detectar esto y captar previamente los datos por usted.

Lo que puede hacer es lo siguiente. Esta prueba parece mostrar una diferencia significativa en los tamaños de bloque utilizados.

public static void main(String... args) throws IOException { 
    for (int i = 512; i <= 2 * 1024 * 1024; i *= 2) 
     readWrite(i); 
} 

private static void readWrite(int blockSize) throws IOException { 
    ByteBuffer bb = ByteBuffer.allocateDirect(blockSize); 
    long start = System.nanoTime(); 
    FileChannel out = new FileOutputStream("deleteme.dat").getChannel(); 
    for (int i = 0; i < (1024 << 20); i += blockSize) { 
     bb.clear(); 
     while (bb.remaining() > 0) 
      if (out.write(bb) < 1) throw new AssertionError(); 
    } 
    out.close(); 
    long mid = System.nanoTime(); 
    FileChannel in = new FileInputStream("deleteme.dat").getChannel(); 
    for (int i = 0; i < (1024 << 20); i += blockSize) { 
     bb.clear(); 
     while (bb.remaining() > 0) 
      if (in.read(bb) < 1) throw new AssertionError(); 
    } 
    in.close(); 
    long end = System.nanoTime(); 
    System.out.printf("With %.1f KB block size write speed %.1f MB/s, read speed %.1f MB/s%n", 
      blockSize/1024.0, 1024 * 1e9/(mid - start), 1024 * 1e9/(end - mid)); 
} 

impresiones

With 0.5 KB block size write speed 96.6 MB/s, read speed 169.7 MB/s 
With 1.0 KB block size write speed 154.2 MB/s, read speed 312.2 MB/s 
With 2.0 KB block size write speed 201.5 MB/s, read speed 438.7 MB/s 
With 4.0 KB block size write speed 288.0 MB/s, read speed 733.9 MB/s 
With 8.0 KB block size write speed 318.4 MB/s, read speed 711.8 MB/s 
With 16.0 KB block size write speed 540.6 MB/s, read speed 1263.7 MB/s 
With 32.0 KB block size write speed 726.0 MB/s, read speed 1370.9 MB/s 
With 64.0 KB block size write speed 801.8 MB/s, read speed 1536.5 MB/s 
With 128.0 KB block size write speed 857.5 MB/s, read speed 1539.6 MB/s 
With 256.0 KB block size write speed 794.0 MB/s, read speed 1781.0 MB/s 
With 512.0 KB block size write speed 676.2 MB/s, read speed 1221.4 MB/s 
With 1024.0 KB block size write speed 886.3 MB/s, read speed 1501.5 MB/s 
With 2048.0 KB block size write speed 784.7 MB/s, read speed 1544.9 MB/s 

Lo que esta prueba no muestra es que el disco duro sólo es compatible con 60 MB/s de lectura y/s escribe 40 MB. Todo lo que está probando es la velocidad de entrada y salida del caché. Si esta era su única prioridad, usaría un archivo mapeado de memoria.

int blockSize = 32 * 1024; 
ByteBuffer bb = ByteBuffer.allocateDirect(blockSize); 
FileChannel out = new FileOutputStream("deleteme.dat").getChannel(); 
for (int i = 0; i < (1024 << 20); i += blockSize) { 
    bb.clear(); 
    while (bb.remaining() > 0) 
     if (out.write(bb) < 1) throw new AssertionError(); 
} 
out.close(); 

long start = System.nanoTime(); 
FileChannel in = new FileInputStream("deleteme.dat").getChannel(); 
MappedByteBuffer map = in.map(FileChannel.MapMode.READ_ONLY, 0, in.size()); 
in.close(); 
long end = System.nanoTime(); 
System.out.printf("Mapped file at a rate of %.1f MB/s%n", 
     1024 * 1e9/(end - start)); 

impresiones

Mapped file at a rate of 589885.5 MB/s 

Esto es tan rápido, ya que sólo asigna los datos en la caché de disco del sistema operativo directamente en la memoria de la aplicación (por lo que no se requiere la copia)

+0

Pure Awesomeness !!! Esto es casi como en Java ... pero no sé hasta dónde puedo implementar esto en C/C++ ya que la mayoría de mis aplicaciones son nativas y deben ser lo más rápidas posible. Una cosa más: ¿es su método bueno para las aplicaciones que operan con los datos antes de volver a escribir (aplicaciones de cifrado)? BTW, Math.pow ("¡¡¡Gracias !!!!", (10/0)); –

+0

aplicaciones de cifrado, como la mayoría de las aplicaciones que procesan, es probable que los datos estén vinculados a la CPU. En este caso, es poco probable que el tamaño del búfer importe porque el costo de la CPU es tan alto. Los tamaños de sus cachés pueden hacer una gran diferencia. En mi humilde opinión, todo lo que puede hacer en Java lo puede hacer en C o C++. –

+1

Ver buena explicación, hago +1 – aswzen

1

tengo vea este código en C:

#include <sys/types.h> 
#include <sys/stat.h> 
#include <unistd.h> 
#include <stdio.h> 

int main() 
{ 
    struct stat fi; 
    stat("/", &fi); 
    printf("%d\n", fi.st_blksize); 
    return 0; 
} 

Devuelve el tamaño de bloque óptimo. Necesitas usarlo para hacer eso. Utilizo la fuente de la secuencia al destino con un tamaño de bloque de 16 * para tener un rendimiento óptimo. Debido a que esta prueba revelará lo mejor con una PC inactiva con algunos hardware/sistema operativo. Pero no es un caso real.

Cuestiones relacionadas