Re: RFR: 8320971: Use BufferedInputStream.buf directly when param of implTransferTo() is trusted [v17]

2023-12-28 Thread Vladimir Sitnikov
On Sat, 23 Dec 2023 13:01:09 GMT, Markus KARG  wrote:

>Typically it was sufficient to document the JMH results before and after a PR 
>just once (not dynamically in the form of a test).

The problem with "results before and after a PR just once" is those benchmarks 
will not automatically fail the build if somebody else breaks the optimization 
unintentionally. In other words, benchmarks do not serve as a safety net.

At the same time, if the test code were to ensure "less than 20 bytes 
allocated" in a common case of transferring data from `BufferedInputStream` to 
`ByteArrayOutputStream`, then it would be a nice safety net so the optimization 
will not be broken unintentionally.

>The latter should be rather easy to check: Invoke transferTo(out) two times in 
>a row and compare the identity of the two byte arrays passed to out.write(). 
>If they stay the same, then apparently no temporary copy was created. Two 
>achieve this, the BIS must be wrapper around an extendable input stream (like 
>FileInputStream) so between calls the stream could get extended (e. g. by 
>writing into the file)

Markus, I am afraid you missed that anything that "extends FileOutputStream" (I 
assume you mean `FileOutputStream`, not `FileInputStream`) would **not** 
qualify as a trusted stream, and `transferTo` will make a copy. The current 
check is `clazz == FileOutputStream.class`

-

PR Review Comment: https://git.openjdk.org/jdk/pull/16879#discussion_r1435625914


Re: RFR: 8320971: Use BufferedInputStream.buf directly when param of implTransferTo() is trusted [v17]

2023-12-28 Thread Markus KARG
On Sat, 23 Dec 2023 15:02:28 GMT, Vladimir Sitnikov  
wrote:

>> Why do you want to put *that* in a test case at all? So far I did not come 
>> across performance-based *tests* (only *benchmarks*), i. e. nobody ever 
>> requested that from me in any of my NIO optimizations. Typically it was 
>> sufficient to document the JMH results before and after a PR *just once* 
>> (not dynamically in the form of a test).
>> 
>> For *this* PR it is sufficient to proof what the PR's title says: Proof the 
>> *direct* call (i. e. the *absence of a copy*) and we're good to merge. I 
>> already explained how to proof *that*.
>
>>Typically it was sufficient to document the JMH results before and after a PR 
>>just once (not dynamically in the form of a test).
> 
> The problem with "results before and after a PR just once" is those 
> benchmarks will not automatically fail the build if somebody else breaks the 
> optimization unintentionally. In other words, benchmarks do not serve as a 
> safety net.
> 
> At the same time, if the test code were to ensure "less than 20 bytes 
> allocated" in a common case of transferring data from `BufferedInputStream` 
> to `ByteArrayOutputStream`, then it would be a nice safety net so the 
> optimization will not be broken unintentionally.
> 
>>The latter should be rather easy to check: Invoke transferTo(out) two times 
>>in a row and compare the identity of the two byte arrays passed to 
>>out.write(). If they stay the same, then apparently no temporary copy was 
>>created. Two achieve this, the BIS must be wrapper around an extendable input 
>>stream (like FileInputStream) so between calls the stream could get extended 
>>(e. g. by writing into the file)
> 
> Markus, I am afraid you missed that anything that "extends FileOutputStream" 
> (I assume you mean `FileOutputStream`, not `FileInputStream`) would **not** 
> qualify as a trusted stream, and `transferTo` will make a copy. The current 
> check is `clazz == FileOutputStream.class`

I think we should not overcomplicate things. Nobody really asked for actually 
failing the test when either the number of bytes allocated or the time to 
allocate it execeeds an expected limit. The requirement for the test solely is 
that it proofs the claim outlined by the topic: Proof that 
BufferedInputStream.bug is used directly and fail if not. Nothing else.  
Everthing beyond that is nice to have as we did not have such a test for the 
similar change at BAIS.

As long as it is proven that we use *the same* buffer always, it is implicitly 
proven that the original claim of the published benchmarks still holds true. 
There is no need to re-evaluate the actual benchmark again and again, it will 
not provide any additional benefit but would make the test much more complex, 
less reliable, and increase the risk of false-fail in automated test farms 
using alternating hardware.

I did not mean `FileOutputStream` but really `FileInputStream`: You have a file 
as an input, so you can extend the file between two calls to `transferTo(out)` 
to be actually able to execute `transferTo` two times in a row. Nevertheless 
you are right, the problem with my proposed solution is that the mock receiver 
needed in that scenario (the code that records the identity of the buffer) 
would not be in the set of trusted classes. Actually I do not see how we can 
solve this.

@bplb In https://github.com/openjdk/jdk/pull/16893 we actually did not check if 
the PR is actually effective, but solely if the buffer is effectively 
unchanged. The same could be done here. Is it OK for you if we skip checking 
the actual efficiency of the *actual target* of this PR itself (i. e. still not 
checking if it is *the same* buffer)?

-

PR Review Comment: https://git.openjdk.org/jdk/pull/16879#discussion_r1435679827


Re: RFR: 8320971: Use BufferedInputStream.buf directly when param of implTransferTo() is trusted [v17]

2023-12-28 Thread Vladimir Sitnikov
On Fri, 22 Dec 2023 15:08:33 GMT, Markus KARG  wrote:

> IMHO the trigger for this PR was sparing time

That is fair. Do you know how one could create a reliable test for performance 
that would fail without the fix and succeed with the fix?

I think the allocation is a reasonable proxy for the duration in this case, and 
the allocation is more-or-less easy to measure and test if the JVM supports 
`getCurrentThreadAllocatedBytes`.

-

PR Review Comment: https://git.openjdk.org/jdk/pull/16879#discussion_r1435200386


Re: RFR: 8320971: Use BufferedInputStream.buf directly when param of implTransferTo() is trusted [v17]

2023-12-28 Thread Markus KARG
On Fri, 22 Dec 2023 16:25:29 GMT, Vladimir Sitnikov  
wrote:

>> IMHO the trigger for this PR was sparing *time*, not necessarily sparing 
>> *bytes* (the default buffer size is just 8K); the latter certainly is a nice 
>> and beneficial side effect. But I may be wrong here, then the original 
>> contributor should chime in now and clarify.
>
>> IMHO the trigger for this PR was sparing time
> 
> That is fair. Do you know how one could create a reliable test for 
> performance that would fail without the fix and succeed with the fix?
> 
> I think the allocation is a reasonable proxy for the duration in this case, 
> and the allocation is more-or-less easy to measure and test if the JVM 
> supports `getCurrentThreadAllocatedBytes`.

Why do you want to put *that* in a test case at all? So far I did not come 
across performance-based *tests* (only *benchmarks*), i. e. nobody ever 
requested that from me in any of my NIO optimizations. Typically it was 
sufficient to document the JMH results before and after a PR *just once* (not 
dynamically in the form of a test).

For *this* PR it is sufficient to proof what the PR's title says: Proof the 
*direct* call (i. e. the *absence of a copy*) and we're good to merge. I 
already explained how to proof *that*.

-

PR Review Comment: https://git.openjdk.org/jdk/pull/16879#discussion_r1435596696


Re: RFR: 8320971: Use BufferedInputStream.buf directly when param of implTransferTo() is trusted [v17]

2023-12-22 Thread Markus KARG
On Fri, 22 Dec 2023 14:37:26 GMT, Vladimir Sitnikov  
wrote:

>> I think there is a misundertanding. This PR is not intendend to reduce the 
>> *amount* of allocated heap, it is about sparing time by not creating 
>> *temporary  copies*. The latter should be rather easy to check: Invoke 
>> `transferTo(out)` two times in a row and compare the *identity* of the two 
>> byte arrays passed to `out.write()`. If they stay the same, then apparently 
>> no *temporary copy* was created. Two achieve this, the BIS must be wrapper 
>> around an extendable input stream (like `FileInputStream`) so between calls 
>> the stream could get extended (e. g. by writing into the file).
>
> Markus, could you please double-check? A temporary allocation *is* an 
> allocation, and it is the point of the change to avoid allocation and copying 
> the temp data when the output stream is known to behave well. I am sure the 
> allocation "before the change" would be non-trivial (more than several KiB), 
> and after the change the allocation within .transferTo would be minimal (few 
> bytes) assuming the output stream has already allocated its buffer

IMHO the trigger for this PR was sparing *time*, not necessarily sparing 
*bytes* (the default buffer size is just 8K); the latter certainly is a nice 
and beneficial side effect. But I may be wrong here, then the original 
contributor should chime in now and clarify.

-

PR Review Comment: https://git.openjdk.org/jdk/pull/16879#discussion_r1435146173


Re: RFR: 8320971: Use BufferedInputStream.buf directly when param of implTransferTo() is trusted [v17]

2023-12-22 Thread Vladimir Sitnikov
On Fri, 22 Dec 2023 14:27:16 GMT, Markus KARG  wrote:

>> Can anybody give a hint how one can create assertions in OpenJDK test code 
>> that would check the amount of allocated heap for the tested method?
>> 
>> Since the change here is "removal of an allocation", the assert in the code 
>> should probably allow only a very small allocation in `.transferTo`.
>> 
>> Does `com.sun.management.ThreadMXBean#getCurrentThreadAllocatedBytes()` 
>> sound right for the test?
>
> I think there is a misundertanding. This PR is not intendend to reduce the 
> *amount* of allocated heap, it is about sparing time by not creating 
> *temporary  copies*. The latter should be rather easy to check: Invoke 
> `transferTo(out)` two times in a row and compare the *identity* of the two 
> byte arrays passed to `out.write()`. If they stay the same, then apparently 
> no *temporary copy* was created. Two achieve this, the BIS must be wrapper 
> around an extendable input stream (like `FileInputStream`) so between calls 
> the stream could get extended (e. g. by writing into the file).

Markus, could you please double-check? A temporary allocation *is* an 
allocation, and it is the point of the change to avoid allocation and copying 
the temp data when the output stream is known to behave well. I am sure the 
allocation "before the change" would be non-trivial (more than several KiB), 
and after the change the allocation within .transferTo would be minimal (few 
bytes) assuming the output stream has already allocated its buffer

-

PR Review Comment: https://git.openjdk.org/jdk/pull/16879#discussion_r1435120325


Re: RFR: 8320971: Use BufferedInputStream.buf directly when param of implTransferTo() is trusted [v17]

2023-12-22 Thread Markus KARG
On Fri, 22 Dec 2023 13:53:42 GMT, Vladimir Sitnikov  
wrote:

>> test/jdk/java/io/BufferedInputStream/TransferToTrusted.java line 68:
>> 
>>> 66: 
>>> 67: var bis = new BufferedInputStream(new 
>>> ByteArrayInputStream(dup));
>>> 68: bis.mark(dup.length);
>> 
>> If you take a closer look into `BIS::transferTo()` then you will notice that 
>> in case you call `bis.mark()` then your optimization will effectively *not 
>> getting called at all*, due to this line: 
>> https://github.com/openjdk/jdk/pull/16879/files#diff-e19c508d1bb6ee78697ecca66947c395adda0d9c49a85bf696e677ecbd977af1L643.
>>  So Brian's comment still applies and you should fix your test before this 
>> PR can be accepted for a merge (hence: you MUST get rid of `mark()`).
>
> Can anybody give a hint how one can create assertions in OpenJDK test code 
> that would check the amount of allocated heap for the tested method?
> 
> Since the change here is "removal of an allocation", the assert in the code 
> should probably allow only a very small allocation in `.transferTo`.
> 
> Does `com.sun.management.ThreadMXBean#getCurrentThreadAllocatedBytes()` sound 
> right for the test?

I think there is a misundertanding. This PR is not intendend to reduce the 
*amount* of allocated heap, it is about sparing time by not creating *temporary 
 copies*. The latter should be rather easy to check: Invoke `transferTo(out)` 
two times in a row and compare the *identity* of the two byte arrays passed to 
`out.write()`. If they stay the same, then apparently no *temporary copy* was 
created. Two achieve this, the BIS must be wrapper around an extendable input 
stream (like `FileInputStream`) so between calls the stream could get extended 
(e. g. by writing into the file).

-

PR Review Comment: https://git.openjdk.org/jdk/pull/16879#discussion_r1435108416


Re: RFR: 8320971: Use BufferedInputStream.buf directly when param of implTransferTo() is trusted [v17]

2023-12-22 Thread Vladimir Sitnikov
On Fri, 22 Dec 2023 13:19:46 GMT, Markus KARG  wrote:

>> Sergey Tsypanov has updated the pull request with a new target base due to a 
>> merge or a rebase. The incremental webrev excludes the unrelated changes 
>> brought in by the merge/rebase. The pull request contains 22 additional 
>> commits since the last revision:
>> 
>>  - Merge branch 'master' into 8320971
>>  - 8320971: Inline tests
>>  - 8322292: Remove TransferToTrusted.checkedOutputStream()
>>  - 8320971: Adjust JavaDoc
>>  - Merge branch 'master' into 8320971
>>  - 8320971: Revert irrelevant changes
>>  - 8320971: Add more tests
>>  - 8320971: Fix JavaDoc
>>  - 8320971: Whitespaces
>>  - 8320971: Fix build
>>  - ... and 12 more: https://git.openjdk.org/jdk/compare/d38d95d8...ee035998
>
> test/jdk/java/io/BufferedInputStream/TransferToTrusted.java line 68:
> 
>> 66: 
>> 67: var bis = new BufferedInputStream(new ByteArrayInputStream(dup));
>> 68: bis.mark(dup.length);
> 
> If you take a closer look into `BIS::transferTo()` then you will notice that 
> in case you call `bis.mark()` then your optimization will effectively *not 
> getting called at all*, due to this line: 
> https://github.com/openjdk/jdk/pull/16879/files#diff-e19c508d1bb6ee78697ecca66947c395adda0d9c49a85bf696e677ecbd977af1L643.
>  So Brian's comment still applies and you should fix your test before this PR 
> can be accepted for a merge (hence: you MUST get rid of `mark()`).

Can anybody give a hint how one can create assertions in OpenJDK test code that 
would check the amount of allocated heap for the tested method?

Since the change here is "removal of an allocation", the assert in the code 
should probably allow only a very small allocation in `.transferTo`.

Does `com.sun.management.ThreadMXBean#getCurrentThreadAllocatedBytes()` sound 
right for the test?

-

PR Review Comment: https://git.openjdk.org/jdk/pull/16879#discussion_r1435074076


Re: RFR: 8320971: Use BufferedInputStream.buf directly when param of implTransferTo() is trusted [v17]

2023-12-22 Thread Markus KARG
On Fri, 22 Dec 2023 09:55:15 GMT, Sergey Tsypanov  wrote:

>> It looks like we can skip copying of `byte[]` in 
>> `BufferedInputStream.implTransferTo()` for `OutputStreams` residing in 
>> `java.io`.
>> 
>> See comment by @vlsi in 
>> https://github.com/openjdk/jdk/pull/10525/files#diff-e19c508d1bb6ee78697ecca66947c395adda0d9c49a85bf696e677ecbd977af1R612
>
> Sergey Tsypanov has updated the pull request with a new target base due to a 
> merge or a rebase. The incremental webrev excludes the unrelated changes 
> brought in by the merge/rebase. The pull request contains 22 additional 
> commits since the last revision:
> 
>  - Merge branch 'master' into 8320971
>  - 8320971: Inline tests
>  - 8322292: Remove TransferToTrusted.checkedOutputStream()
>  - 8320971: Adjust JavaDoc
>  - Merge branch 'master' into 8320971
>  - 8320971: Revert irrelevant changes
>  - 8320971: Add more tests
>  - 8320971: Fix JavaDoc
>  - 8320971: Whitespaces
>  - 8320971: Fix build
>  - ... and 12 more: https://git.openjdk.org/jdk/compare/17ab55e8...ee035998

test/jdk/java/io/BufferedInputStream/TransferToTrusted.java line 68:

> 66: 
> 67: var bis = new BufferedInputStream(new ByteArrayInputStream(dup));
> 68: bis.mark(dup.length);

If you take a closer look into `BIS::transferTo()` then you will notice that in 
case you call `bis.mark()` then your optimization will effectively *not getting 
called at all*, due to this line: 
https://github.com/openjdk/jdk/pull/16879/files#diff-e19c508d1bb6ee78697ecca66947c395adda0d9c49a85bf696e677ecbd977af1L643.
 So Brian's comment still applies and you should fix your test before this PR 
can be accepted for a merge (hence: you MUST get rid of `mark()`).

-

PR Review Comment: https://git.openjdk.org/jdk/pull/16879#discussion_r1435050559


Re: RFR: 8320971: Use BufferedInputStream.buf directly when param of implTransferTo() is trusted [v17]

2023-12-22 Thread Sergey Tsypanov
> It looks like we can skip copying of `byte[]` in 
> `BufferedInputStream.implTransferTo()` for `OutputStreams` residing in 
> `java.io`.
> 
> See comment by @vlsi in 
> https://github.com/openjdk/jdk/pull/10525/files#diff-e19c508d1bb6ee78697ecca66947c395adda0d9c49a85bf696e677ecbd977af1R612

Sergey Tsypanov has updated the pull request with a new target base due to a 
merge or a rebase. The incremental webrev excludes the unrelated changes 
brought in by the merge/rebase. The pull request contains 22 additional commits 
since the last revision:

 - Merge branch 'master' into 8320971
 - 8320971: Inline tests
 - 8322292: Remove TransferToTrusted.checkedOutputStream()
 - 8320971: Adjust JavaDoc
 - Merge branch 'master' into 8320971
 - 8320971: Revert irrelevant changes
 - 8320971: Add more tests
 - 8320971: Fix JavaDoc
 - 8320971: Whitespaces
 - 8320971: Fix build
 - ... and 12 more: https://git.openjdk.org/jdk/compare/15c2671f...ee035998

-

Changes:
  - all: https://git.openjdk.org/jdk/pull/16879/files
  - new: https://git.openjdk.org/jdk/pull/16879/files/84686bc6..ee035998

Webrevs:
 - full: https://webrevs.openjdk.org/?repo=jdk=16879=16
 - incr: https://webrevs.openjdk.org/?repo=jdk=16879=15-16

  Stats: 1284 lines in 66 files changed: 844 ins; 243 del; 197 mod
  Patch: https://git.openjdk.org/jdk/pull/16879.diff
  Fetch: git fetch https://git.openjdk.org/jdk.git pull/16879/head:pull/16879

PR: https://git.openjdk.org/jdk/pull/16879