You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@juneau.apache.org by ja...@apache.org on 2021/03/08 18:53:42 UTC

[juneau] branch master updated: Add byte buffer reuse to IOUtils.

This is an automated email from the ASF dual-hosted git repository.

jamesbognar pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/juneau.git


The following commit(s) were added to refs/heads/master by this push:
     new 182dcdb  Add byte buffer reuse to IOUtils.
182dcdb is described below

commit 182dcdbc0799797c9a66697b4708832e18c80730
Author: JamesBognar <ja...@salesforce.com>
AuthorDate: Mon Mar 8 13:53:29 2021 -0500

    Add byte buffer reuse to IOUtils.
---
 .../apache/juneau/internal/FileWriterBuilder.java  | 17 ++++-
 .../java/org/apache/juneau/internal/IOUtils.java   | 88 ++++++++++++----------
 2 files changed, 64 insertions(+), 41 deletions(-)

diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/FileWriterBuilder.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/FileWriterBuilder.java
index 1e63f8f..0c3e5af 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/FileWriterBuilder.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/FileWriterBuilder.java
@@ -22,7 +22,7 @@ public final class FileWriterBuilder {
 
 	private File file;
 	private Charset cs = Charset.defaultCharset();
-	private boolean append;
+	private boolean append, buffered;
 
 	/**
 	 * Creates a new builder.
@@ -112,12 +112,25 @@ public final class FileWriterBuilder {
 	}
 
 	/**
+	 * Sets the buffer mode on the writer to <jk>true</jk>.
+	 *
+	 * @return This object (for method chaining).
+	 */
+	public FileWriterBuilder buffered() {
+		this.buffered = true;
+		return this;
+	}
+
+	/**
 	 * Creates a new File writer.
 	 *
 	 * @return A new File writer.
 	 * @throws FileNotFoundException If file could not be found.
 	 */
 	public Writer build() throws FileNotFoundException {
-		return new OutputStreamWriter(new FileOutputStream(file, append), cs);
+		OutputStream os = new FileOutputStream(file, append);
+		if (buffered)
+			os = new BufferedOutputStream(os);
+		return new OutputStreamWriter(os, cs);
 	}
 }
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/IOUtils.java b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/IOUtils.java
index 11760d0..c95ac5f 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/IOUtils.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/IOUtils.java
@@ -15,6 +15,7 @@ package org.apache.juneau.internal;
 import java.io.*;
 import java.nio.charset.*;
 import java.util.*;
+import java.util.concurrent.atomic.*;
 
 /**
  * Various I/O related utility methods.
@@ -32,6 +33,20 @@ public final class IOUtils {
 		}
 	};
 
+	private static final ThreadLocal<byte[]> BYTE_BUFFER_CACHE = (Boolean.getBoolean("juneau.disableIoBufferReuse") ? null : new ThreadLocal<>());
+	private static final ThreadLocal<char[]> CHAR_BUFFER_CACHE = (Boolean.getBoolean("juneau.disableIoBufferReuse") ? null : new ThreadLocal<>());
+
+	static final AtomicInteger cacheHits = new AtomicInteger();
+
+	static {
+		Runtime.getRuntime().addShutdownHook(new Thread() {
+			@Override
+			public void run() {
+				System.out.println("I/O buffer cache hits: " + cacheHits.get());
+			}
+		});
+	}
+
 	/** Reusable empty reader. */
 	public static final Reader EMPTY_READER = new Reader() {
 		@Override
@@ -69,7 +84,7 @@ public final class IOUtils {
 	public static long pipe(Reader in, File out) throws IOException {
 		if (out == null || in == null)
 			return 0;
-		try (Writer w = FileWriterBuilder.create(out).build()) {
+		try (Writer w = FileWriterBuilder.create(out).buffered().build()) {
 			return pipe(in, w);
 		}
 	}
@@ -94,7 +109,7 @@ public final class IOUtils {
 			return 0;
 		long total = 0;
 		try (Reader in2 = in) {
-			char[] buffer = new char[buffSize(-1)];
+			char[] buffer = charBuffer(-1);
 			int readLen;
 			while ((readLen = in.read(buffer)) != -1) {
 				out.write(buffer, 0, readLen);
@@ -209,7 +224,7 @@ public final class IOUtils {
 	public static long pipe(InputStream in, OutputStream out, long maxBytes) throws IOException {
 		if (in == null || out == null)
 			return 0;
-		byte[] buffer = new byte[buffSize(maxBytes)];
+		byte[] buffer = byteBuffer((int)maxBytes);
 		int readLen;
 		long total = 0;
 		if (maxBytes < 0) {
@@ -253,7 +268,7 @@ public final class IOUtils {
 		try (Reader in2 = in) {
 			OutputStreamWriter osw = new OutputStreamWriter(out, UTF8);
 			int i;
-			char[] b = new char[BUFF_SIZE];
+			char[] b = charBuffer(-1);
 			while ((i = in.read(b)) > 0) {
 				total += i;
 				osw.write(b, 0, i);
@@ -373,7 +388,7 @@ public final class IOUtils {
 			return null;
 		try (Reader in2 = in) {
 			StringBuilder sb = new StringBuilder(buffSize(expectedLength)); // Assume they're ASCII characters.
-			char[] buf = new char[buffSize(expectedLength)];
+			char[] buf = charBuffer((int)expectedLength);
 			int i = 0;
 			while ((i = in2.read(buf)) != -1)
 				sb.append(buf, 0, i);
@@ -421,39 +436,6 @@ public final class IOUtils {
 	}
 
 	/**
-	 * Shortcut for calling <c>readBytes(in, 1024);</c>
-	 *
-	 * @param in
-	 * 	The object to read into a byte array.
-	 * 	<br>Can be any of the following types:
-	 * 	<ul>
-	 * 		<li><code><jk>byte</jk>[]</code>
-	 * 		<li>{@link InputStream}
-	 * 		<li>{@link Reader}
-	 * 		<li>{@link CharSequence}
-	 * 		<li>{@link File}
-	 * 	</ul>
-	 * @return The contents of the stream as a byte array.
-	 * @throws IOException Thrown by underlying stream or if object is not a supported type.
-	 */
-	@Deprecated
-	public static byte[] readBytes(Object in) throws IOException {
-		if (in == null)
-			return new byte[0];
-		if (in instanceof byte[])
-			return (byte[])in;
-		if (in instanceof CharSequence)
-			return in.toString().getBytes(UTF8);
-		if (in instanceof InputStream)
-			return readBytes((InputStream)in);
-		if (in instanceof Reader)
-			return read((Reader)in, 0).getBytes(UTF8);
-		if (in instanceof File)
-			return readBytes((File)in);
-		throw new IOException("Cannot convert object of type '"+in.getClass().getName()+"' to a byte array.");
-	}
-
-	/**
 	 * Reads the specified input stream into the specified byte array.
 	 *
 	 * @param in
@@ -486,7 +468,7 @@ public final class IOUtils {
 			return new byte[0];
 		ByteArrayOutputStream buff = new ByteArrayOutputStream(buffSize(maxBytes));
 		int nRead;
-		byte[] b = new byte[buffSize(maxBytes)];
+		byte[] b = byteBuffer(maxBytes);
 		while ((nRead = in.read(b, 0, b.length)) != -1)
 			buff.write(b, 0, nRead);
 		buff.flush();
@@ -765,6 +747,34 @@ public final class IOUtils {
 		return null;
 	}
 
+	private static final byte[] byteBuffer(int maxBytes) {
+		if (BYTE_BUFFER_CACHE != null) {
+			byte[] x = BYTE_BUFFER_CACHE.get();
+			if (x == null) {
+				 x = new byte[BUFF_SIZE];
+				 BYTE_BUFFER_CACHE.set(x);
+			} else {
+				cacheHits.incrementAndGet();
+			}
+			return x;
+		}
+		return new byte[buffSize(maxBytes)];
+	}
+
+	private static final char[] charBuffer(int maxChars) {
+		if (CHAR_BUFFER_CACHE != null) {
+			char[] x = CHAR_BUFFER_CACHE.get();
+			if (x == null) {
+				 x = new char[BUFF_SIZE];
+				 CHAR_BUFFER_CACHE.set(x);
+			} else {
+				cacheHits.incrementAndGet();
+			}
+			return x;
+		}
+		return new char[buffSize(maxChars)];
+	}
+
 	private static final int buffSize(long max) {
 		return (max > 0 && max < BUFF_SIZE) ? (int)max : BUFF_SIZE;
 	}