-
Notifications
You must be signed in to change notification settings - Fork 5.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
8254078: DataOutputStream is very slow post-disabling of Biased Locking #542
Conversation
👋 Welcome back aph! A progress list of the required criteria for merging this PR into |
@theRealAph The following label will be automatically applied to this pull request:
When this pull request is ready to be reviewed, an "RFR" email will be sent to the corresponding mailing list. If you would like to change these labels, use the /label pull request command. |
|
Webrevs
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is a pre-existing race condition on use of the writeBuffer.
It is allocated per DataOutputStream but is unsynchronized.
Only the write(byte) and write(byte[], off, len) methods are synchronized.
Will that be/become a problem?
Hard to say. There isn't a guarantee in the specification. After my patch, writeInt is
whereas writeLong() is
I know there's a history of Java programmers writing to the implementation, not the specification, but ISTM that if writeLong() can be written this way so can writeInt(). |
DataOutputStream is a JDK 1.0 era class that doesn't specify whether it is safe for use by multiple threads or not. Some methods are synchronized, some are not. The writeBuffer used by writeLong means it is not thread safe. The writeShort and writeInt methods aren't thread safe so changing them to use the writeBuffer doesn't make things any worse. Other examples are the "bytearr" used by writeUTF, the writeChars and writeBytes methods, and the protected "written" field that sub-classes can use to monitor the number of bytes written. It might be helpful to add a statement to the DataOutputStream class description to say that it not safe for by concurrent threads and it needs appropriate synchronization to coordinate multiple writers. This issue or another issue doesn't matter. Can the benchmark be turned into a microbenchmark for test/micro? |
I see.
Now there's an interesting question. I can tweak the benchmark (which at present contains a modified version of DataOutputSteam, so can compare and contrast) so that it measures only the performance of the core library's implementation. Does that make sense? If so, I can add it to this PR. |
The benchmark can be run with different JDKs, so the modified DOS can be omitted. |
It turns out that DataInputStream already has a comment about thread safety being the user's problem, so I copied that comment to DataOutputStream. |
/test |
@theRealAph you need to get approval to run the tests in tier1 for commits up until 8379b4f |
/csr |
@AlanBateman has indicated that a compatibility and specification (CSR) request is needed for this pull request. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have comments about the benchmark code style :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have comments about the benchmark code style :)
Wow, thanks! I didn't know that JMH could do this, I thought you had to jump through the BenchmarkState hoops. This way is much better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Benchmark code looks good, except a few remaining nits.
|
||
@Param({"4096"}) int size; | ||
final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(size); | ||
final File f = new File("DataOutputStreamTest.tmp"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be File.createTempFile("DataOutputStreamTest", "out")
? This way you don't leave waste in current dir.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can probably move all these initializations to setup()
. It does not look like the fields final
-ity matter for this benchmark.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can probably move all these initializations to
setup()
. It does not look like the fieldsfinal
-ity matter for this benchmark.
This conflicts with other desiderata, i.e. that fields should be final and if a field can be initialized it should be. And it makes the benchmark longer. I prefer to pass on this one.
* Thread safety is optional and is the responsibility of users of | ||
* methods in this class. | ||
* A DataInputStream is not safe for use by multiple concurrent | ||
* threads. If a DataOutputStream is to be used by more than one |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi Andrew, just noticed a typo here: DataOutputStream
best regards,
-- daniel
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that was a comment on the previous iteration too. Also s/output/input/.
* methods in this class. | ||
* A DataInputStream is not safe for use by multiple concurrent | ||
* threads. If a DataInputStream is to be used by more than one | ||
* thread then access to the data output stream should be controlled |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"data output stream" should be "data input stream".
@theRealAph This change now passes all automated pre-integration checks. ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details. After integration, the commit message for the final commit will be:
You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed. At the time when this comment was updated there had been 310 new commits pushed to the
As there are no conflicts, your changes will automatically be rebased on top of these commits when integrating. If you prefer to avoid this automatic rebasing, please check the documentation for the /integrate command for further details. ➡️ To integrate this PR with the above commit message to the |
/integrate |
@theRealAph Since your change was applied there have been 554 commits pushed to the
Your commit was automatically rebased without conflicts. Pushed as commit 17f04fc. 💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored. |
DataOutputStream is very slow post-disabling of Biased Locking. This
was discovered when benchmarking a transaction library, which showed
significant performance loss when moving to JDK 15. WIth some small
changes to DataOutputStream we can get the performance back. There's a
JMH benchmark at
http://cr.openjdk.java.net/~aph/JDK-8254078/jmh-tests.tar
Some Stream classes use very fine-grained locking.
In particular, writeInt is defined like this:
Unfortunately, ByteArrayOutputStream.write(byte) is defined like this:
so we acquire and release a lock for every byte that is output.
For example, writing 4kb of ints goes from 17.3 us/op to 53.9 us/op when biased locking is disabled:
+UseBiasedLocking DataOutputStreamTest.dataOutputStreamOverByteArray avgt 6 53.895 ± 5.126 us/op
-UseBiasedLocking DataOutputStreamTest.dataOutputStreamOverByteArray avgt 6 17.291 ± 4.430 us/op
There are refactorings of DataOutputStream we can do to mitigate this.
Progress
Issue
Reviewers
Download
$ git fetch https://git.openjdk.java.net/jdk pull/542/head:pull/542
$ git checkout pull/542