I have played with this quite a bit and have come up with a slightly modified change which does not regress for the smallest of the sample objects and shows a nice improvement for the 2 larger files.
Here is baseline benchmark on 1.8: jdk 11 64 bit 1.8 BASELINE Benchmark (file) Mode Cnt Score Error Units XZDecompressionBenchmark.decompress ihe_ovly_pr.dcm avgt 3 0.751 ± 0.145 ms/op XZDecompressionBenchmark.decompress image1.dcm avgt 3 427.542 ± 7.724 ms/op XZDecompressionBenchmark.decompress large.xml avgt 3 286.265 ± 2.841 ms/op And here are the results after modifications: Benchmark (file) Mode Cnt Score Error Units XZDecompressionBenchmark.decompress ihe_ovly_pr.dcm avgt 3 0.751 ± 0.022 ms/op XZDecompressionBenchmark.decompress image1.dcm avgt 3 396.706 ± 13.298 ms/op XZDecompressionBenchmark.decompress large.xml avgt 3 245.282 ± 3.709 ms/op diff --git a/src/org/tukaani/xz/lz/LZDecoder.java b/src/org/tukaani/xz/lz/LZDecoder.java index 85b2ca1..da0ec98 100644 --- a/src/org/tukaani/xz/lz/LZDecoder.java +++ b/src/org/tukaani/xz/lz/LZDecoder.java @@ -12,6 +12,7 @@ package org.tukaani.xz.lz; import java.io.DataInputStream; import java.io.IOException; + import org.tukaani.xz.ArrayCache; import org.tukaani.xz.CorruptedInputException; @@ -94,13 +95,29 @@ public final class LZDecoder { int back = pos - dist - 1; if (dist >= pos) back += bufSize; - - do { - buf[pos++] = buf[back++]; + //many times there is only exactly 1 byte to copy + //optimize for that use case + buf[pos++] = buf[back++]; + --left; + //then handle in bulk if there is work remaining + while (left > 0) { if (back == bufSize) back = 0; - } while (--left > 0); - + //it is possible for the "repeat" to include content which is going + //to be generated here so we have to limit ourselves to how much + //data is already in the buffer (i.e. pos - back). It is also + //possible that "back" can actually be forward in the buffer from + //position, in which case that comparison does not matter + int toCopy = Math.min(left, bufSize - back); + int tmp = pos - back; + if (tmp < toCopy && tmp > 0) { + toCopy = tmp; + } + System.arraycopy(buf, back, buf, pos, toCopy); + pos += toCopy; + back += toCopy; + left -= toCopy; + } if (full < pos) full = pos; }