You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by gg...@apache.org on 2021/01/25 22:06:29 UTC
[commons-io] branch master updated: Improve performance of
IOUtils.contentEquals(Reader, Reader).
This is an automated email from the ASF dual-hosted git repository.
ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-io.git
The following commit(s) were added to refs/heads/master by this push:
new aa04779 Improve performance of IOUtils.contentEquals(Reader, Reader).
aa04779 is described below
commit aa04779ea19c87400780fdeb75cb6af182e6b28a
Author: Gary Gregory <ga...@gmail.com>
AuthorDate: Mon Jan 25 17:06:24 2021 -0500
Improve performance of IOUtils.contentEquals(Reader, Reader).
This is based on the PR https://github.com/apache/commons-io/pull/118 by
XenoAmess but only for this one method.
---
src/changes/changes.xml | 5 +-
src/main/java/org/apache/commons/io/IOUtils.java | 66 ++++++++++++++--------
.../jmh/IOUtilsContentEqualsReadersBenchmark.java | 23 ++++----
3 files changed, 59 insertions(+), 35 deletions(-)
diff --git a/src/changes/changes.xml b/src/changes/changes.xml
index 022bb3f..782791b 100644
--- a/src/changes/changes.xml
+++ b/src/changes/changes.xml
@@ -195,7 +195,10 @@ The <action> type attribute can be add,update,fix,remove.
Bump jimfs from 1.1 to 1.2 #183.
</action>
<action dev="ggregory" type="update" due-to="XenoAmess, Gary Gregory">
- Improved performance of IOUtils.contentEquals(InputStream, InputStream).
+ Improve performance of IOUtils.contentEquals(InputStream, InputStream).
+ </action>
+ <action dev="ggregory" type="update" due-to="XenoAmess, Gary Gregory">
+ Improve performance of IOUtils.contentEquals(Reader, Reader).
</action>
</release>
<!-- The release date is the date RC is cut -->
diff --git a/src/main/java/org/apache/commons/io/IOUtils.java b/src/main/java/org/apache/commons/io/IOUtils.java
index 2e68824..313b2e9 100644
--- a/src/main/java/org/apache/commons/io/IOUtils.java
+++ b/src/main/java/org/apache/commons/io/IOUtils.java
@@ -16,6 +16,9 @@
*/
package org.apache.commons.io;
+import static org.apache.commons.io.IOUtils.DEFAULT_BUFFER_SIZE;
+import static org.apache.commons.io.IOUtils.EOF;
+
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
@@ -790,43 +793,60 @@ public class IOUtils {
}
/**
- * Compares the contents of two Readers to determine if they are equal or
- * not.
+ * Compares the contents of two Readers to determine if they are equal or not.
* <p>
- * This method buffers the input internally using
- * {@code BufferedReader} if they are not already buffered.
+ * This method buffers the input internally using {@code BufferedReader} if they are not already buffered.
* </p>
*
- * @param reader1 the first reader
- * @param reader2 the second reader
- * @return true if the content of the readers are equal or they both don't
- * exist, false otherwise
+ * @param input1 the first reader
+ * @param input2 the second reader
+ * @return true if the content of the readers are equal or they both don't exist, false otherwise
* @throws NullPointerException if either input is null
- * @throws IOException if an I/O error occurs
+ * @throws IOException if an I/O error occurs
* @since 1.1
*/
@SuppressWarnings("resource")
- public static boolean contentEquals(final Reader reader1, final Reader reader2)
- throws IOException {
- if (reader1 == reader2) {
+ public static boolean contentEquals(final Reader input1, final Reader input2) throws IOException {
+ if (input1 == input2) {
return true;
}
- if (reader1 == null ^ reader2 == null) {
+ if (input1 == null || input2 == null) {
return false;
}
- final BufferedReader bufferedInput1 = toBufferedReader(reader1);
- final BufferedReader bufferedInput2 = toBufferedReader(reader2);
- int ch = bufferedInput1.read();
- while (EOF != ch) {
- final int ch2 = bufferedInput2.read();
- if (ch != ch2) {
- return false;
+ final char[] array1 = new char[DEFAULT_BUFFER_SIZE];
+ final char[] array2 = new char[DEFAULT_BUFFER_SIZE];
+ int pos1;
+ int pos2;
+ int count1;
+ int count2;
+ while (true) {
+ pos1 = 0;
+ pos2 = 0;
+ for (int index = 0; index < DEFAULT_BUFFER_SIZE; index++) {
+ if (pos1 == index) {
+ do {
+ count1 = input1.read(array1, pos1, DEFAULT_BUFFER_SIZE - pos1);
+ } while (count1 == 0);
+ if (count1 == EOF) {
+ return pos2 == index && input2.read() == EOF;
+ }
+ pos1 += count1;
+ }
+ if (pos2 == index) {
+ do {
+ count2 = input2.read(array2, pos2, DEFAULT_BUFFER_SIZE - pos2);
+ } while (count2 == 0);
+ if (count2 == EOF) {
+ return pos1 == index && input1.read() == EOF;
+ }
+ pos2 += count2;
+ }
+ if (array1[index] != array2[index]) {
+ return false;
+ }
}
- ch = bufferedInput1.read();
}
-
- return bufferedInput2.read() == EOF;
}
/**
diff --git a/src/test/java/org/apache/commons/io/jmh/IOUtilsContentEqualsReadersBenchmark.java b/src/test/java/org/apache/commons/io/jmh/IOUtilsContentEqualsReadersBenchmark.java
index 763eab7..1d36550 100644
--- a/src/test/java/org/apache/commons/io/jmh/IOUtilsContentEqualsReadersBenchmark.java
+++ b/src/test/java/org/apache/commons/io/jmh/IOUtilsContentEqualsReadersBenchmark.java
@@ -45,12 +45,12 @@ import org.openjdk.jmh.infra.Blackhole;
* Test different implementations of {@link IOUtils#contentEquals(Reader, Reader)}.
*
* <pre>
- * IOUtilsContentEqualsReadersBenchmark.testFileCurrent avgt 5 1984542.440 ▒ 741983.929 ns/op
- * IOUtilsContentEqualsReadersBenchmark.testFilePr118 avgt 5 1903047.996 ▒ 1126067.279 ns/op
- * IOUtilsContentEqualsReadersBenchmark.testFileRelease_2_8_0 avgt 5 2000614.270 ▒ 577200.820 ns/op
- * IOUtilsContentEqualsReadersBenchmark.testStringCurrent avgt 5 4833065053.333 ▒ 313253734.966 ns/op
- * IOUtilsContentEqualsReadersBenchmark.testStringPr118 avgt 5 1032292548.000 ▒ 32968762.278 ns/op
- * IOUtilsContentEqualsReadersBenchmark.testStringRelease_2_8_0 avgt 5 4810962660.000 ▒ 221405909.807 ns/op
+ * IOUtilsContentEqualsReadersBenchmark.testFileCurrent avgt 5 1670968.050 ▒ 67526.308 ns/op
+ * IOUtilsContentEqualsReadersBenchmark.testFilePr118 avgt 5 1660143.543 ▒ 733178.893 ns/op
+ * IOUtilsContentEqualsReadersBenchmark.testFileRelease_2_8_0 avgt 5 1785283.975 ▒ 214177.764 ns/op
+ * IOUtilsContentEqualsReadersBenchmark.testStringCurrent avgt 5 1144495273.333 ▒ 50706166.907 ns/op
+ * IOUtilsContentEqualsReadersBenchmark.testStringPr118 avgt 5 1075059231.455 ▒ 275364676.487 ns/op
+ * IOUtilsContentEqualsReadersBenchmark.testStringRelease_2_8_0 avgt 5 4767157193.333 ▒ 139567775.251 ns/op
* </pre>
*/
@BenchmarkMode(Mode.AverageTime)
@@ -61,6 +61,7 @@ import org.openjdk.jmh.infra.Blackhole;
@Fork(value = 1, jvmArgs = {"-server"})
public class IOUtilsContentEqualsReadersBenchmark {
+ private static final int STRING_LEN = 1 << 24;
private static final String TEST_PATH_A = "/org/apache/commons/io/testfileBOM.xml";
private static final String TEST_PATH_16K_A = "/org/apache/commons/io/abitmorethan16k.txt";
private static final String TEST_PATH_16K_A_COPY = "/org/apache/commons/io/abitmorethan16kcopy.txt";
@@ -69,15 +70,15 @@ public class IOUtilsContentEqualsReadersBenchmark {
static String[] STRINGS = new String[5];
static {
- STRINGS[0] = StringUtils.repeat("ab", 1 << 24);
+ STRINGS[0] = StringUtils.repeat("ab", STRING_LEN);
STRINGS[1] = STRINGS[0] + 'c';
STRINGS[2] = STRINGS[0] + 'd';
- STRINGS[3] = StringUtils.repeat("ab\rab\n", 1 << 24);
- STRINGS[4] = StringUtils.repeat("ab\r\nab\r", 1 << 24);
+ STRINGS[3] = StringUtils.repeat("ab\rab\n", STRING_LEN);
+ STRINGS[4] = StringUtils.repeat("ab\r\nab\r", STRING_LEN);
}
- static String SPECIAL_CASE_STRING_0 = StringUtils.repeat(StringUtils.repeat("ab", 1 << 24) + '\n', 2);
- static String SPECIAL_CASE_STRING_1 = StringUtils.repeat(StringUtils.repeat("cd", 1 << 24) + '\n', 2);
+ static String SPECIAL_CASE_STRING_0 = StringUtils.repeat(StringUtils.repeat("ab", STRING_LEN) + '\n', 2);
+ static String SPECIAL_CASE_STRING_1 = StringUtils.repeat(StringUtils.repeat("cd", STRING_LEN) + '\n', 2);
@SuppressWarnings("resource")
public static boolean contentEquals_release_2_8_0(final Reader input1, final Reader input2) throws IOException {