Skip to content
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

8300236: Use VarHandle access in Data(Input | Output)Stream classes #12076

Closed
wants to merge 19 commits into from

Conversation

minborg
Copy link
Contributor

@minborg minborg commented Jan 18, 2023

This PR proposes using a performance optimization using a new supported API for operations similar to those found in java.io.Bits


Progress

  • Change must be properly reviewed (1 review required, with at least 1 Reviewer)
  • Change must not contain extraneous whitespace
  • Commit message must refer to an issue

Issue

  • JDK-8300236: Use VarHandle access in Data(Input | Output)Stream classes

Reviewers

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/jdk pull/12076/head:pull/12076
$ git checkout pull/12076

Update a local copy of the PR:
$ git checkout pull/12076
$ git pull https://git.openjdk.org/jdk pull/12076/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 12076

View PR using the GUI difftool:
$ git pr show -t 12076

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/jdk/pull/12076.diff

@bridgekeeper
Copy link

bridgekeeper bot commented Jan 18, 2023

👋 Welcome back pminborg! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

@openjdk
Copy link

openjdk bot commented Jan 18, 2023

@minborg The following label will be automatically applied to this pull request:

  • core-libs

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.

@openjdk openjdk bot added the core-libs core-libs-dev@openjdk.org label Jan 18, 2023
Copy link
Contributor

@RogerRiggs RogerRiggs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments:

}

// Alternative with an internal buffer fixed att offset zero.
public static final class BigEndianAtZeroBuffer {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd keep it simple an leave the buffer management to the caller. (Omitting this class).


}

public static final class BigEndianAtZero {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd merge these methods (with zero offset) in with the previous class; the overloads with offsets are sufficient to distinguish them and it would make discovery more natural. (omitting this class).

private Bits() {
}

public static final class BigEndian {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there other names that improve the readability of the uses?
For example ByteArray.getShort() and ByteArray.getDouble.

There's been a move to drop get and put prefixes where they are unnecessary.
Get and put prefixes do make them similar to the existing methods, but...

Is it just a readable to say:
float degrees = ByteArray.float(buf); // for get
ByteArray.int(buf, degrees); // for put

Maybe, maybe not....

@minborg
Copy link
Contributor Author

minborg commented Jan 19, 2023

Performance looks promising for serialization (values in us/operation):

  Java 20 Java 21 Improvement
SerializeBenchmark.serializeData 7.283 6.793 6.7%
SerializeBenchmark.serializeRecord 7.275 6.733 7.5%

image

@minborg minborg changed the title Add a supported API similar to java.io.Bits 8300236: Use VarHandle access in Data(Input | Output)Stream classes Jan 19, 2023
@minborg minborg marked this pull request as ready for review January 19, 2023 19:54
@openjdk openjdk bot added the rfr Pull request is ready for review label Jan 19, 2023
@mlbridge
Copy link

mlbridge bot commented Jan 19, 2023

);
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new InternalError("Can't lookup Bits.getXXX", e);
throw new InternalError("Can't lookup BigEndian.getXXX", e);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BigEndian -> ByteArrayAccess

* questions.
*/

package jdk.internal.util.access;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pretty deep; I'd drop the final "access". The package name jdk.internal.util is fine.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to rename to ByteArray if we keep the package. The reason for proposing a separate package is that if we later decide to export the class, we are able to export to only this package and not all the other classes in `jdk.internal.util'. This could reduce coupling.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"access" in the package/class name does look a bit strange, and could easily get mixed up with the package and classes that are used for shared secrets. I don't think jdk.internal.util.ByteArrays would look out of place. Hopefully it won't need to be exported to many other modules as we need to keep the qualified exports to a minimum.

Comment on lines 59 to 60
* Methods for unpacking primitive values from byte arrays starting at
* a given offsets.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: "a given offsets".

*/

/**
* Gets a {@code boolean} from the provided {@code array} at the given {@code offset}.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could use the concise javadoc tag that provides both the first line and @return value. For example,
{@return Returns a {@code boolean} from the provided {@code array} at the given {@code offset}}

* @param array to read a value from.
* @param offset where extraction in the array should begin
* @return a {@code boolean} from the array
* @throws NullPointerException if the provided {@code array} is {@code null}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the class level statement about throwing NullPointer, the @throws is not necessary in each method.

Comment on lines 612 to 614
/*
* Methods for packing primitive values into byte arrays starting at offset zero.
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the only advantage to the zero offset versions in the API, shorter invocations with fewer parameters?
Is there any performance difference?

Copy link
Contributor Author

@minborg minborg Jan 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very good question. I will take a look at it. A fixed value is inserted in the original var handle so potentially it might improve performance that way too.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No significant performance increase:

2-parameters
Benchmark                                             Mode  Cnt  Score   Error  Units
PrimitiveFieldSerializationBenchmark.serializeData    avgt    8  6.761 ± 0.126  ns/op
PrimitiveFieldSerializationBenchmark.serializeRecord  avgt    8  6.890 ± 0.093  ns/op

3-parameters
Benchmark                                             Mode  Cnt  Score   Error  Units
PrimitiveFieldSerializationBenchmark.serializeData    avgt    8  6.850 ± 0.074  ns/op
PrimitiveFieldSerializationBenchmark.serializeRecord  avgt    8  6.855 ± 0.057  ns/op

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will remove the extra methods with lesser parameters.

@@ -54,6 +56,8 @@ public DataInputStream(InputStream in) {
super(in);
}

private final byte[] readBuffer = new byte[8];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No objection to the readBuffer but it does make it wonder if we should re-visit bytearr and chararr as they are only need for reading modified UTF-8 strings and shouldn't need to be eagerly created.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

* using {@linkplain ByteOrder#BIG_ENDIAN big endian order} (aka. "network order").
* <p>
* All methods in this class will throw an {@linkplain NullPointerException} if {@code null} is
* passed in as a method parameter for a byte array.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The NPE is described in both the class description and some of the methods, it doesn't need to be both.

@openjdk
Copy link

openjdk bot commented Jan 24, 2023

@minborg this pull request can not be integrated into master due to one or more merge conflicts. To resolve these merge conflicts and update this pull request you can run the following commands in the local repository for your personal fork:

git checkout bits-api
git fetch https://git.openjdk.org/jdk master
git merge FETCH_HEAD
# resolve conflicts and follow the instructions given by git merge
git commit -m "Merge master"
git push

@openjdk openjdk bot added the merge-conflict Pull request has merge conflict with target branch label Jan 24, 2023
@openjdk openjdk bot removed the merge-conflict Pull request has merge conflict with target branch label Jan 24, 2023
Copy link
Contributor

@AlanBateman AlanBateman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, I don't have any more comments, @RogerRiggs?

@openjdk
Copy link

openjdk bot commented Jan 24, 2023

@minborg 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:

8300236: Use VarHandle access in Data(Input | Output)Stream classes

Reviewed-by: rriggs, alanb

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 2 new commits pushed to the master branch:

  • 3be5758: 8300769: Remove G1CollectionSet::_inc_bytes_used_before
  • 859ca75: 8300862: Remove some G1 collection set remembered set debugging code

Please see this link for an up-to-date comparison between the source branch of this pull request and the master branch.
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 master branch, type /integrate in a new comment.

@openjdk openjdk bot added the ready Pull request is ready to be integrated label Jan 24, 2023
Copy link
Contributor

@RogerRiggs RogerRiggs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Look good, thanks

}

// At-zero methods

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These AtZero test methods don't add much value? It is already testing an offset of 1 in the original tests.

@minborg
Copy link
Contributor Author

minborg commented Jan 25, 2023

/integrate

@openjdk
Copy link

openjdk bot commented Jan 25, 2023

Going to push as commit 74e1a8b.
Since your change was applied there have been 16 commits pushed to the master branch:

  • a5d8e12: 8300244: Replace NULL with nullptr in share/interpreter/
  • 71107f4: 8300651: Replace NULL with nullptr in share/runtime/
  • 3c61d5a: 8300659: Refactor TestMemoryAwareness to use WhiteBox api for host values
  • b2071f7: 8300657: Remove null filtering in CLD oop handle area
  • 95fafd0: 8300644: Remove gc/shenandoah/jni/TestStringCriticalWithDedup.java
  • 5a478ef: 8297730: C2: Arraycopy intrinsic throws incorrect exception
  • 7465de4: 6603771: Nimbus L&F: Ctrl+F7 keybinding for Jinternal Frame throws a NPE.
  • fbe5ab0: 8300830: Remove redundant assertion in src/hotspot/share/runtime/javaCalls.cpp
  • 1339461: 8300981: Build failure on 32-bit platforms after JDK-8281213
  • 81d523d: Merge
  • ... and 6 more: https://git.openjdk.org/jdk/compare/048705c04967d106dedc09a4cf2325a3b46ef4e7...master

Your commit was automatically rebased without conflicts.

@openjdk openjdk bot added the integrated Pull request has been integrated label Jan 25, 2023
@openjdk openjdk bot closed this Jan 25, 2023
@openjdk openjdk bot removed ready Pull request is ready to be integrated rfr Pull request is ready for review labels Jan 25, 2023
@openjdk
Copy link

openjdk bot commented Jan 25, 2023

@minborg Pushed as commit 74e1a8b.

💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored.

@altrisi
Copy link
Contributor

altrisi commented Jan 25, 2023

Not fully related to this PR, but is there a reason that functionality was exposed as a VarHandle factory instead of just methods that do that, taking an array and an index?

These byte array handles don't seem to have much state in them other than endianness, that could just be passed in (improving 8300235 further), removing the need here for a class holding them and the indirection.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
core-libs core-libs-dev@openjdk.org integrated Pull request has been integrated
4 participants