Comparando rendimiento de MappedByteBuffer con respecto a métodos tradicionales de I/O
lunes, diciembre 1, 2014 at 5:54PM
jcarmonaloeches
Hola a tod@s, estaba analizando diferentes métodos de lectura escritura sobre ficheros, aquí os dejó un código....
package es.jcarmonaloeches;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/**
 * Clase orientada a comparar diferentes algoritmos utilizados para leer
 * ficheros de texto
 * 
 * @author jaimecl
 *
 */
public class LecturaFichero {
	/**
	 * Método principal de ejecución
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		EscribeFichero escribeFichero = new EscribeFichero();
		StringBuffer linea1 = new StringBuffer("Linea ");
		StringBuffer linea2 = new StringBuffer(
				" 1234567890123456789012345678901234567890123456789012345678901234567890"
						+ "1234567890123456789012345678901234567890123456789012345678901234567890"
						+ "1234567890123456789012345678901234567890123456789012345678901234567890"
						+ "1234567890123456789012345678901234567890123456789012345678901234567890"
						+ "1234567890123456789012345678901234567890123456789012345678901234567890"
						+ "1234567890123456789012345678901234567890123456789012345678901234567890"
						+ "123456789012345678901234567890123456789012345678901234567890");
		String fichero = escribeFichero.metodoPrincipal(5000, linea1, linea2);

		LecturaFichero lecturaFichero = new LecturaFichero();
		lecturaFichero.metodoPrincipal(fichero);
	}

	/**
	 * Compara la lectura de un fichero (en tiempo) con diferentes algoritmos
	 * 
	 * @param nombreFichero
	 *            : el nombre del fichero
	 */
	private void metodoPrincipal(String nombreFichero) {
		System.out.println("********** LEYENDO EL FICHERO **********");
		long time_start, time_end = 0;
		time_start = System.currentTimeMillis();

		lecturaBR(nombreFichero);

		time_end = System.currentTimeMillis();
		System.out
				.print(" BufferedReader ->" + (time_end - time_start) + " ms");

		long time_start2, time_end2 = 0;
		time_start2 = System.currentTimeMillis();

		lecturaMB(nombreFichero);

		time_end2 = System.currentTimeMillis();
		System.out.println(" MappedByteBuffer ->" + (time_end2 - time_start2)
				+ " ms");
		System.out.println("***************** LEIDO *****************");
	}

	/**
	 * Lee un fichero haciendo uso de Buffered Reader
	 * 
	 * @param nombreFichero
	 *            : el nombre del fichero a leer
	 * 
	 */
	private void lecturaBR(String nombreFichero) {

		BufferedReader br = null;
		try {

			String linea = "";
			br = new BufferedReader(new FileReader(nombreFichero));

			while (linea != null) {
				linea = br.readLine();
			}

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (null != br)
					br.close();
			} catch (Exception e2) {
				e2.printStackTrace();
			}

		}
	}

	/**
	 * Ejecuta la lectura de un fichero haciendo uso de MappedByteBuffer
	 * 
	 * @param nombreFichero
	 *            : el nombre del fichero
	 * @throws IOException
	 */
	public void lecturaMB(String nombreFichero) {
		try {
			long length = new File(nombreFichero).length();
			FileInputStream fileInputStream = new FileInputStream(nombreFichero);
			MappedByteBuffer in = fileInputStream.getChannel().map(
					FileChannel.MapMode.READ_ONLY, 0, length);
			int i = 0;
			while (i < length)
				in.get(i++);

			fileInputStream.close();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}
package es.jcarmonaloeches;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/**
 * Clase orientada a comparar diferentes algoritmos utilizados para crear
 * ficheros de texto
 * 
 * @author jaimecl
 *
 */
public class EscribeFichero {
	static String LS = System.getProperty("line.separator");

	/**
	 * Escribe un fichero utilizando diferentes algoritmos
	 * 
	 * @param lineas
	 *            : el nº de líneas
	 * @param linea1
	 *            : la primera parte de la línea a escribir.
	 * @param linea2
	 *            : la segunda parte de la línea a escribir (entre medias de
	 *            ambas se imprime el nº de línea)
	 * @return el nombre del fichero
	 */
	public String metodoPrincipal(int lineas, StringBuffer linea1,
			StringBuffer linea2) {
		System.out.println("********** ESCRIBIENDO EL FICHERO **********");
		int bytes = linea1.length() + linea2.length() + 3;

		System.out.print(lineas + " lineas, de aprox, " + bytes + " bytes. ");

		long time_start = 0, time_end;
		time_start = System.currentTimeMillis();

		String nombreFichero = "fic_" + lineas + "L_" + bytes + "B_.txt";

		escrituraPW(nombreFichero, lineas, linea1, linea2);

		time_end = System.currentTimeMillis();

		System.out.print(" PrintWriter->" + (time_end - time_start) + " ms");

		long time_start2, time_end2 = 0;
		time_start2 = System.currentTimeMillis();

		escrituraMB(nombreFichero, lineas, linea1, linea2);

		time_end2 = System.currentTimeMillis();

		System.out.println(" MappedByteBuffer->" + (time_end2 - time_start2)
				+ " ms");
		
		System.out.println("****************** ESCRITO *****************");
		return nombreFichero;
	}

	/**
	 * Escribe haciendo uso de PrintWriter
	 * 
	 * @param nombreFichero
	 *            : el nombre del fichero a escribir
	 * @param numLineas
	 *            : el nº de líneas.
	 * @param linea1
	 *            : la primera parte de la línea.
	 * @param linea2
	 *            : la segunda parte de la línea (NOTA: entre medias, se
	 *            incluirá el número de línea)
	 */
	private void escrituraPW(String nombreFichero, long numLineas,
			StringBuffer linea1, StringBuffer linea2) {

		File file = new File(nombreFichero);
		if (file.exists()) {
			file.delete();
		}

		FileWriter fichero = null;
		PrintWriter pw = null;
		try {

			fichero = new FileWriter(nombreFichero);
			pw = new PrintWriter(fichero);

			for (long i = 0; i < numLineas; i++) {
				String linea = linea1 + "-" + i + "-" + linea2;

				pw.println(linea);

			}

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (null != fichero)
					fichero.close();
			} catch (Exception e2) {
				e2.printStackTrace();
			}

		}
	}

	/**
	 * Escribe haciendo uso de MappedByteBuffer
	 * 
	 * @param nombreFichero
	 *            : el nombre del fichero a escribir
	 * @param numLineas
	 *            : el nº de líneas.
	 * @param linea1
	 *            : la primera parte de la línea.
	 * @param linea2
	 *            : la segunda parte de la línea (NOTA: entre medias, se
	 *            incluirá el número de línea)
	 */
	private static void escrituraMB(String nombreFichero, long numLineas,
			StringBuffer linea1, StringBuffer linea2) {
		try {

			File file = new File(nombreFichero);
			if (file.exists()) {
				file.delete();
			}

			// create a random access file stream (read-write)
			RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
			FileChannel channel = randomAccessFile.getChannel();

			long reservaDeMemoriaDelBuffer = calculaDeReservaDeMemoria(
					numLineas, linea1, linea2);

			MappedByteBuffer buf = channel.map(FileChannel.MapMode.READ_WRITE,
					0, reservaDeMemoriaDelBuffer);

			for (int i = 0; i < numLineas; i++) {
				String linea = linea1 + "-" + i + "-" + linea2 + LS;
				buf.put(linea.getBytes());
			}

			buf.clear();
			channel.close();
			randomAccessFile.close();

		} catch (IOException e) {
			System.out.println("I/O Error: " + e.getMessage());
		}

	}

	/**
	 * Calcula el nº de bytes necesarios, teniendo en cuenta el número de
	 * líneas, la línea 1, la línea 2, y los separadores de ambas.
	 * 
	 * @param numLineas
	 *            : el número de líneas.
	 * @param linea1
	 *            : la línea 1.
	 * @param linea2
	 *            : la línea 2. (NOTA: entre medias de ambas, irá la cadena "-"
	 *            + nº de línea + "-"
	 * @return: el total de bytes
	 */
	private static long calculaDeReservaDeMemoria(long numLineas,
			StringBuffer linea1, StringBuffer linea2) {
		int numLongitud = 0;
		if (numLineas < 100) {
			numLongitud = 2;
		} else if (numLineas >= 100 && numLineas < 1000) {
			numLongitud = 3;
		} else if (numLineas >= 1000 && numLineas < 10000) {
			numLongitud = 4;
		} else if (numLineas >= 10000 && numLineas < 100000) {
			numLongitud = 5;
		} else if (numLineas >= 100000 && numLineas < 1000000) {
			numLongitud = 6;
		} else if (numLineas >= 1000000 && numLineas < 10000000) {
			numLongitud = 7;
		} else if (numLineas >= 10000000 && numLineas < 100000000) {
			numLongitud = 8;
		} else if (numLineas >= 100000000 && numLineas < 1000000000) {
			numLongitud = 9;
		} else if (numLineas >= 100000000) {
			numLongitud = 10;
		}

		long reservaDeMemoriaDelBuffer = numLineas
				* (linea1.length() + 1 + numLongitud + 1 + linea2.length() + 2);
		return reservaDeMemoriaDelBuffer;
	}
}

La salida de la ejecución del método principal es:

********** ESCRIBIENDO EL FICHERO **********
5000 lineas, de aprox, 490 bytes. PrintWriter->110 ms MappedByteBuffer->100 ms
****************** ESCRITO *****************

********** LEYENDO EL FICHERO **********
BufferedReader ->53 ms MappedByteBuffer ->12 ms
***************** LEIDO *****************

Vemos que la lectura es 4 veces más rápida en MappedBytBuffer (que trae el contenido a memoria), pero tenemos que tener en cuenta que BufferedReader está leyendo líneas, y en MappedByteBuffer estamos leyendo bytes, por lo que es necesario determinar qué acción lógica queremos realizar contra ese contenido para realizar el ejercicio de comparación completo.
Por otro lado, comentar que es necesario determinar si es dispone de la memoria necesaria en la JVM para realizar el procesado en memoria, si no se obtendrá una excepción de memoria.
Un saludo,
Article originally appeared on javaHispano (http://www.javahispano.org/).
See website for complete article licensing information.