Cómo acelerar la base 64 de lectura y escritura codificado gzipped archivos de gran tamaño en Java

rayimpr:

La tarea consiste en comprimir / descomprimir muy grandes de datos > 2G , que no pueden ser asimiento por una sola cadena o ByteArray. Mi solución es escribir / fragmento de datos descomprimidos por el trozo comprimido en un archivo. Funciona, pero no lo suficientemente rápido.

Comprimir : texto sin formato de archivos -> gzip -> base64 codificar -> archivo comprimido
Descomprimir : comprimido de archivos -> decodificación base 64 -> gunzip -> archivo de texto plano

Resultado de la prueba en la computadora portátil, con la memoria de 16G.

Created compressed file, takes 571346 millis
Created decompressed file, takes 378441 millis

bloque de código

public static void compress(final InputStream inputStream, final Path outputFile) throws IOException {
    try (final OutputStream outputStream = new FileOutputStream(outputFile.toString());
        final OutputStream base64Output = Base64.getEncoder().wrap(outputStream);
        final GzipCompressorOutputStream gzipOutput = new GzipCompressorOutputStream(base64Output);
        final BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {

      reader.lines().forEach(line -> {
        try {
          gzipOutput.write(line.getBytes());
          gzipOutput.write(System.getProperty("line.separator").getBytes());
        } catch (final IOException e) {
          e.printStackTrace();
        }
      });
    }
  }

public static void decompress(final InputStream inputStream, final Path outputFile) throws IOException {
  try (final OutputStream outputStream = new FileOutputStream(outputFile.toString());
      final GzipCompressorInputStream gzipStream = new GzipCompressorInputStream(Base64.getDecoder().wrap(inputStream));
      final BufferedReader reader = new BufferedReader(new InputStreamReader(gzipStream))) {

    reader.lines().forEach(line -> {
      try {
        outputStream.write(line.getBytes());
        outputStream.write(System.getProperty("line.separator").getBytes());
      } catch (final IOException e) {
        e.printStackTrace();
      }
    });
  }
}

Por otra parte, he tratado de hacer lotes de escritura cuando se envían datos a un archivo, no vio una gran mejora.

# batch write
public static void compress(final InputStream inputStream, final Path outputFile) throws IOException {
  try (final OutputStream outputStream = new FileOutputStream(outputFile.toString());
      final OutputStream base64Output = Base64.getEncoder().wrap(outputStream);
      final GzipCompressorOutputStream gzipOutput = new GzipCompressorOutputStream(base64Output);
      final BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {

    StringBuilder stringBuilder = new StringBuilder();
    final int chunkSize = Integer.MAX_VALUE / 1000;

    String line;
    int counter = 0;
    while((line = reader.readLine()) != null) {
      counter++;
      stringBuilder.append(line).append(System.getProperty("line.separator"));
      if(counter >= chunkSize) {
        gzipOutput.write(stringBuilder.toString().getBytes());
        counter = 0;
        stringBuilder = new StringBuilder();
      }
    }

    if (counter > 0) {
      gzipOutput.write(stringBuilder.toString().getBytes());
    }
  }
}

Pregunta

  1. Buscando sugerencia sobre la forma de acelerar el proceso global
  2. ¿Cuáles serán los cuellos de botella?

10/02/2019 actualización

Hice algunas pruebas más, los resultados muestran que la codificación base64 es el cuello de botella.

public static void compress(final InputStream inputStream, final Path outputFile) throws IOException {
  try (final OutputStream outputStream = new FileOutputStream(outputFile.toString());
       final OutputStream base64Output = Base64.getEncoder().wrap(outputStream);
       final GzipCompressorOutputStream gzipOutput = new GzipCompressorOutputStream(base64Output)) {

    final byte[] buffer = new byte[4096];
    int n = 0;
    while (-1 != (n = inputStream.read(buffer))) {
      gzipOutput.write(buffer, 0, n);
    }
  }
}
  • archivo de prueba 2.2G, con 21,5 millones de líneas
  • única copia de archivos: ~ 2 segundos
  • único archivo gzip: ~ 12 segundos
  • Gzip + base64: ~ 500 segundos
Joop Eggen:

Primero: Nunca el juego de caracteres por defecto, ya que no es portátil.

String s = ...;
byte[] b = ...;
b = s.getBytes(StandardCharsets.UTF_8);
s = new String(b, StandardCharsets.UTF_8);

Para la compresión de texto que no entrañan un lector, como que convierte los bytes dan algunos charset en un (Unicode holding) de cuerda, y de nuevo una parte posterior conversión. También Char de una cadena requiere 2 bytes (UTF-16) en lugar de 1 byte para los símbolos ASCII básicos.

Base64 convierte binario a un alfabeto de 64 símbolos ASCII, lo que requiere 4/3 el espacio. No hagas eso otra cuando los datos deben ser transmitidos envasados ​​en XML o tales.

Los archivos grandes pueden ser (de) comprimido.

final int BUFFER_SIZE = 1024 * 64;
Path textFile = Paths.get(".... .txt");
Path gzFile = textFile.resolveSibling(textFile.getFileName().toString() + ".gz");

try (OutputStream out = new GzipOutputStream(Files.newOutputStream(gzFile), BUFFER_SIZE))) {
    Files.copy(textFile, out);
}

try (InputStream in = new GzipInputStream(Files.newInputStream(gzFile), BUFFER_SIZE))) {
    Files.copy(in, textFile);
}

A menudo, el BUFFER_SIZE parámetro opcional se pasa por alto, lo que podría disminuir el rendimiento.

copy puede tener parámetros adicionales para el manejo de conflictos de archivos.

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=316262&siteId=1
Recomendado
Clasificación