Pregunta

Tengo un método que crea un MessageDigest (un hash) a partir de un archivo, y necesito hacer esto con muchos archivos (> = 100,000). ¿Qué tamaño debería tener el búfer utilizado para leer de los archivos para maximizar el rendimiento?

La mayoría de la gente está familiarizada con el código básico (que repetiré aquí por si acaso):

MessageDigest md = MessageDigest.getInstance( "SHA" );
FileInputStream ios = new FileInputStream( "myfile.bmp" );
byte[] buffer = new byte[4 * 1024]; // what should this value be?
int read = 0;
while( ( read = ios.read( buffer ) ) > 0 )
    md.update( buffer, 0, read );
ios.close();
md.digest();

¿Cuál es el tamaño ideal del búfer para maximizar el rendimiento? Sé que esto depende del sistema, y ??estoy bastante seguro de que depende de su sistema operativo, sistema de archivos, y HDD, y quizás haya otro hardware / software en la mezcla.

(Debo señalar que soy algo nuevo en Java, así que esto podría ser una llamada a la API de Java que no conozco)

Editar: No sé de antemano los tipos de sistemas en los que se utilizará, por lo que no puedo asumir mucho. (Estoy usando Java por esa razón).

Editar: En el código anterior faltan cosas como intentar ... capturar para que la publicación sea más pequeña

¿Fue útil?

Solución

El tamaño óptimo del búfer está relacionado con varias cosas: el tamaño del bloque del sistema de archivos, el tamaño de la memoria caché de la CPU y la latencia de la memoria caché.

La mayoría de los sistemas de archivos están configurados para usar tamaños de bloque de 4096 u 8192. En teoría, si configura el tamaño de su búfer de modo que esté leyendo unos pocos bytes más que el bloque de disco, las operaciones con el sistema de archivos pueden ser extremadamente ineficientes ( es decir, si configuró su búfer para leer 4100 bytes a la vez, cada lectura requeriría 2 lecturas de bloque por el sistema de archivos). Si los bloques ya están en el caché, entonces terminará pagando el precio de la RAM - > Latencia de caché L3 / L2. Si no tiene suerte y los bloques aún no están en el caché, pagará el precio de la latencia del disco - > RAM también.

Esta es la razón por la que ve que la mayoría de los búferes tienen el tamaño de una potencia de 2, y en general son más grandes (o iguales) que el tamaño del bloque de disco. Esto significa que una de sus lecturas de flujo podría dar como resultado múltiples lecturas de bloque de disco, pero esas lecturas siempre usarán un bloque completo, sin lecturas inútiles.

Ahora, esto se compensa bastante en un escenario de transmisión típico porque el bloque que se lee desde el disco aún estará en la memoria cuando llegue a la siguiente lectura (estamos haciendo lecturas secuenciales aquí, después de todo) - así que terminas pagando la memoria RAM - > Precio de latencia de caché L3 / L2 en la siguiente lectura, pero no la latencia de RAM de disco > En términos de orden de magnitud, la latencia del disco > RAM es tan lenta que casi invade cualquier otra latencia con la que esté tratando.

Por lo tanto, sospecho que si realizó una prueba con diferentes tamaños de caché (no he hecho esto por mí mismo), probablemente encontrará un gran impacto del tamaño de caché hasta el tamaño del bloque del sistema de archivos. Por encima de eso, sospecho que las cosas se nivelarán bastante rápido.

Hay una tonelada de condiciones y excepciones aquí: las complejidades del sistema son bastante asombrosas (solo se consigue un control de las transferencias de caché L3 - > L2 es muy complejo, y cambia con cada tipo de CPU).

Esto conduce a la respuesta del "mundo real": si su aplicación tiene un 99% como máximo, configure el tamaño del caché en 8192 y continúe (aún mejor, elija la encapsulación sobre el rendimiento y use BufferedInputStream para ocultar los detalles). Si se encuentra en el 1% de las aplicaciones que dependen en gran medida del rendimiento del disco, cree su implementación para poder intercambiar diferentes estrategias de interacción con el disco, y proporcione los mandos y diales para permitir que los usuarios prueben y optimicen sistema de auto-optimización).

Otros consejos

Sí, es probable que dependa de varias cosas, pero dudo que haga mucha diferencia. Tiendo a optar por 16K o 32K como un buen equilibrio entre el uso de memoria y el rendimiento.

Tenga en cuenta que debe tener un bloque try / finally en el código para asegurarse de que la transmisión está cerrada incluso si se produce una excepción.

En la mayoría de los casos, realmente no importa mucho. Solo elige un buen tamaño, como 4K o 16K y apégate a él. Si está seguro de que este es el cuello de botella en su aplicación, entonces debe comenzar a perfilar para encontrar el tamaño de búfer óptimo. Si elige un tamaño demasiado pequeño, perderá tiempo realizando operaciones adicionales de E / S y llamadas a funciones adicionales. Si elige un tamaño demasiado grande, comenzará a ver una gran cantidad de fallas de caché que realmente lo harán más lento. No uses un búfer más grande que el tamaño de tu caché L2.

En el caso ideal, deberíamos tener suficiente memoria para leer el archivo en una sola operación de lectura. Ese sería el mejor desempeño porque permitimos que el sistema administre el Sistema de archivos, las unidades de asignación y el HDD a voluntad. En la práctica, tiene la fortuna de saber el tamaño de los archivos con anticipación, solo use el tamaño de archivo promedio redondeado a 4K (unidad de asignación predeterminada en NTFS). Y lo mejor de todo: cree un punto de referencia para probar múltiples opciones.

Podrías usar BufferedStreams / lectores y luego usar sus tamaños de búfer.

Creo que BufferedXStreams está usando 8192 como tamaño de búfer, pero como dijo Ovidiu, probablemente debería realizar una prueba en un montón de opciones. Realmente dependerá del sistema de archivos y las configuraciones de disco en cuanto a cuáles son los mejores tamaños.

La lectura de archivos con FileChannel y MappedByteBuffer de Java NIO probablemente resultará en una solución que será mucho más rápida que cualquier solución que involucre FileInputStream. Básicamente, asigne en memoria los archivos grandes y use buffers directos para los pequeños.

En la fuente de BufferedInputStream & # 8216; s encontrará: int estático privado DEFAULT_BUFFER_SIZE = 8192;
Por lo tanto, es una buena opción usar ese valor predeterminado.
Pero si puede averiguar algo más de información, obtendrá más respuestas valiosas.
Por ejemplo, su adsl puede preferir un búfer de 1454 bytes, eso es debido a la carga útil de TCP / IP. Para los discos, puede usar un valor que coincida con el tamaño de bloque de su disco.

Como ya se mencionó en otras respuestas, use BufferedInputStreams.

Después de eso, supongo que el tamaño del búfer realmente no importa. O bien el programa está enlazado a E / S, y el aumento del tamaño del búfer sobre el valor predeterminado de BIS no tendrá un gran impacto en el rendimiento.

O el programa está enlazado a la CPU dentro del MessageDigest.update (), y la mayoría del tiempo no se gasta en el código de la aplicación, por lo que no será de utilidad ajustarla.

(Hmm ... con varios núcleos, los subprocesos pueden ayudar.)

1024 es apropiado para una amplia variedad de circunstancias, aunque en la práctica puede ver un mejor rendimiento con un tamaño de búfer más grande o más pequeño.

Esto dependería de varios factores, incluido el bloqueo del sistema de archivos Tamaño y hardware de la CPU.

También es común elegir una potencia de 2 para el tamaño del búfer, ya que la mayoría de los subyacentes el hardware está estructurado con bloques de fleje y tamaños de caché que son una potencia de 2. El Buffered Las clases le permiten especificar el tamaño del búfer en el constructor. Si no se proporciona ninguno, use un valor predeterminado, que es una potencia de 2 en la mayoría de las JVM.

Independientemente del tamaño de búfer que elija, el mayor aumento de rendimiento lo hará see se está moviendo desde el acceso no almacenado al archivo en búfer. Ajustando el tamaño del búfer puede Mejore ligeramente el rendimiento, pero a menos que esté utilizando un dispositivo extremadamente pequeño o extremadamente pequeño. gran tamaño de búfer, es poco probable que tenga un impacto significativo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top