On Thu, 31 Dec 2020 10:11:58 GMT, Philippe Marschall 
<github.com+471021+marsch...@openjdk.org> wrote:

>> Implement three optimiztations for Reader.read(CharBuffer)
>> 
>> * Add a code path for heap buffers in Reader#read to use the backing array 
>> instead of allocating a new one.
>> * Change the code path for direct buffers in Reader#read to limit the 
>> intermediate allocation to `TRANSFER_BUFFER_SIZE`.
>> * Implement `InputStreamReader#read(CharBuffer)` and delegate to 
>> `StreamDecoder`.
>> * Implement `StreamDecoder#read(CharBuffer)` and avoid buffer allocation.
>
> A couple of implementation notes:
> 
> Reader#read(CharBuffer)
> 
> on-heap case
> 
> I introduced a dedicated path for the on-heap case and directly read into the 
> backing array. This completely avoids the intermediate allocation and copy. 
> Compared to the initial proposal the buffer position is updated. This has to 
> be done because we bypass the buffer and directly read into the array. This 
> also handles the case that #read returns -1.
> 
> I am using a c-style array declaration because the rest of the class uses it.
> 
> off-heap case
> 
> I limit the intermadiate allocation to TRANSFER_BUFFER_SIZE. In order to 
> reduce the total intermediate allocation we now call #read multiple times 
> until the buffer is full or EOF is reached. This changes the behavior 
> slightly as now possibly more data is read. However this should contribute to 
> reduce the overall intermediate allocations.
> 
> A lock is acquired to keep the the read atomic. This is consistent with #skip 
> which also holds a lock over multiple #read calls. This is inconsistent with 
> #transferTo which does not acquire a lock over multiple #read calls. 
> 
> The implementation took inspiration from java.io.InputStream#readNBytes(int).
> 
> InputStreamReader#read(CharBuffer)
> 
> Since StreamDecoder is a Reader as well we can simply delegate.
> 
> StreamDecoder#read(CharBuffer)
> 
> Interestingly this was not implemented even though StreamDecoder internally 
> works on NIO buffers.
> 
> on-heap case
> 
> We see a performance improvement and the elimination of all intermediate 
> allocation.
> 
> StreamDecoder#read(char[], int, int) and InputStreamReader#read(char[], int, 
> int) may get a small improvement as we no longer #slice the buffer.
> 
> off-heap case
> 
> We see the elimination of all intermediate allocation but a performance 
> penalty because we hit the slow path in #decodeLoop. There is a trade-off 
> here, we could improve the performance to previous levels by introducing 
> intermediate allocation again. I don't know how much the users of off-heap 
> buffers care about introducing intermediate allocation vs maximum throughput.
> 
> I was struggling to come up with microbenchmarks because the built in 
> InputStream and Reader implementations are finite but JMH calls the benchmark 
> methods repeatably. I ended up implementing custom infinite InputStream and 
> Reader implementations. The code can be found there:
> 
> https://github.com/marschall/reader-benchmarks/tree/master/src/main/java/com/github/marschall/readerbenchmarks
> 
> An Excel with charts can be found here:
> 
> https://github.com/marschall/reader-benchmarks/raw/master/src/main/resources/4926314.xlsx
> 
> I looked for java.io.Reader#read(CharBuffer) users in the JDK and only found 
> java.util.Scanner#readInput(). I wrote a microbenchmark for this but only 
> found minuscule improvements due to regex dominating the runtime.
> 
> There seem to be no direct Reader tests in the tier1 suite, the initial code 
> was wrong because I forgot to update the buffer code position but I only 
> found out because some Scanner tests failed.

I changed the JBS issue summary to match the title of this PR so that 
integration blocker is removed.

How does the performance of `InputStreamReader.read(CharBuffer)` compare for 
the case where only the change to `Reader` is made versus if all your proposed 
changes are applied?

Some kind of specific test should likely be included.

Note that the more recent copyright year is now 2021.

-------------

PR: https://git.openjdk.java.net/jdk/pull/1915

Reply via email to