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 {