Since JDK 18 some implementations of InputStream.transferTo, namely FileInputStream and ChannelInputStream, offload work to lower layers using NIO channels. This provides shorter execution time and reduced resource consumption. Unfortunately, this effect is prevented once the input stream itself is wrapped by a PushbackInputStream. As it is a common pattern told by many Java tutorials, lots of existing applications do wrap FileInputStream using PushbackInputStream by default. Hence, many existing applications will not experience the offloading-improvements made by JDK 18.
To provide enhanced performance to existing applications, it makes sense to address this impediment: PushbackInputStream.transferTo should be implemented in a way that first writes the complete backpushed content to the target output stream, then command the wrapped input stream to directly transfertTo(out) on its own.
JMH benchmarking with an initial prototype has proven this to be simple yet effective:
Java 19-internal with patch => Push has only slight impact on performance (within variance)
PushbackInputStreamPerformanceTest.unwrapped_transferTo thrpt 25 1,091 ┬▒ 0,145 ops/s
PushbackInputStreamPerformanceTest.wrapped_transferTo thrpt 25 1,133 ┬▒ 0,197 ops/s
Java 19-internal without patch => Pushback drastically reduces performance from 1,179 to 0,622 (far beyond variance)
PushbackInputStreamPerformanceTest.unwrapped_transferTo thrpt 25 1,179 ┬▒ 0,188 ops/s
PushbackInputStreamPerformanceTest.wrapped_transferTo thrpt 25 0,622 ┬▒ 0,028
As these numbers demonstrate, the original (unpatched) code of JDK 19-internal shows a clear difference between using and not using PushbackInputStream, where the pushback buffer seems to eat up lots of execution time, while the modified (patched) code shows rather same performance between using and not using a PushbackInputStream. This proofs that the patch is effective and frees lots of otherwise squandered ops/s.
To provide enhanced performance to existing applications, it makes sense to address this impediment: PushbackInputStream.transferTo should be implemented in a way that first writes the complete backpushed content to the target output stream, then command the wrapped input stream to directly transfertTo(out) on its own.
JMH benchmarking with an initial prototype has proven this to be simple yet effective:
Java 19-internal with patch => Push has only slight impact on performance (within variance)
PushbackInputStreamPerformanceTest.unwrapped_transferTo thrpt 25 1,091 ┬▒ 0,145 ops/s
PushbackInputStreamPerformanceTest.wrapped_transferTo thrpt 25 1,133 ┬▒ 0,197 ops/s
Java 19-internal without patch => Pushback drastically reduces performance from 1,179 to 0,622 (far beyond variance)
PushbackInputStreamPerformanceTest.unwrapped_transferTo thrpt 25 1,179 ┬▒ 0,188 ops/s
PushbackInputStreamPerformanceTest.wrapped_transferTo thrpt 25 0,622 ┬▒ 0,028
As these numbers demonstrate, the original (unpatched) code of JDK 19-internal shows a clear difference between using and not using PushbackInputStream, where the pushback buffer seems to eat up lots of execution time, while the modified (patched) code shows rather same performance between using and not using a PushbackInputStream. This proofs that the patch is effective and frees lots of otherwise squandered ops/s.