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
8263482: Make access to the ICC color profiles data multithread-friendly #2957
Conversation
👋 Welcome back serb! A progress list of the required criteria for merging this PR into |
return LCMS.getTagNative(getNativePtr(), key); | ||
}); | ||
} finally { | ||
lock.unlockRead(stamp); | ||
} |
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.
The comments about the change in this class and the changes in the "getTag()" method.
- I have removed the TagData and TagCache classes and move the tag cache to the profile class directly.
- I have moved synchronization logic from the LCMS.java to this class, so now we can use more "granular" synchronizations.
The logic behind this change:
- The StampedLock guards the access to the native lcms code, so when we change the tags via "LCMS.setTagDataNative" nobody will call "LCMS.getTagNative" and "LCMS.getProfileDataNative". I tried the "ReentrantReadWriteLock", but it is a little bit slower.
- The cache itself is maintained by the ConcurrentHashMap, and the usage of "byte[] t = tags.get(sig);" w/o synchronization is a key for the performance.
Note that it is possible to use "tags.computeIfAbsent" instead of "tags.get(sig)" and take care of "native access" synchronization in the "LCMS.getTagNative", but unfortuntly for some workload the "get()" is x10 times faster than computeIfAbsent() if the key is already exists in the map(common situation for the cache).
activate(); | ||
return getData(cmmProfile, tagSignature); | ||
byte[] t = getData(cmmProfile(), tagSignature); | ||
return t != null ? t.clone() : null; |
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 moved the clone operation to the public "ICC_Profile.getData(int tagSignature)" method, so we do not clone it again again and again when we use this data internally.
|
||
@Override | ||
public synchronized void setTagData(Profile p, int tagSignature, byte[] data) { | ||
getLcmsProfile(p).setTag(tagSignature, data); |
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.
Note that this method is synchronized, I think it is not needed to be, because a long time ago(before the LCMSProfile class was implemented ) all method in this class was synchronized to guard information about tags. But later we changed all such synchronization methods to the per-profile locks. And only this method remains synchronized.
But I leave it as is for now, since it might affect transforms which I am not touching in this fix. WIll remove that later.
@mrserb 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 97 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 |
@mrserb Since your change was applied there have been 121 commits pushed to the
Your commit was automatically rebased without conflicts. Pushed as commit 1a21f77. 💡 You may see a message that your pull request was closed with unmerged commits. This can be safely ignored. |
FYI: probably is better/simpler to review it via webrev.
After migration to the lcms from the kcms the performance of some operations was regressed. One possible workaround was to split the operation into multiple threads. But unfortunately, we have too many bottlenecks which prevent using multithreading. This is the request to remove/minimize such bottlenecks(at least some of them), but it does not affect the single-threaded performance it should be handled separately.
The main code pattern optimized here is this:
Notes about the code above:
Before the change "activate()" method checked that the "cmmProfile" field was not null. After that we usually used the "cmmProfile" as the parameter to some other method. This included two volatile reads, and also required to check when we need to call the "activate()" method before usage of the "cmmProfile" field.
Solution: "activate()" renamed to the "cmmProfile()" which became an accessor for the field, so we will get one volatile read and can easily monitor the usage of the field itself(it is used directly only in this method).
The synchronized static method "CMSManager.getModule()" reimplemented to have only one volatile read.
The usage of locking in the "getTagData()" is changed. Instead of the synchronized instance methods, we now use the mix of "ConcurrentHashMap" and StampedLock.
See some comments inline.
Some numbers(small numbers are better):
Performance of ICC_Profile.getInstance(ColorSpace.CS_sRGB); and ColorSpace.getInstance(ColorSpace.CS_sRGB);
note "ThreadsMAX" is 32 threads.
Progress
Issue
Reviewers
Download
To checkout this PR locally:
$ git fetch https://git.openjdk.java.net/jdk pull/2957/head:pull/2957
$ git checkout pull/2957
To update a local copy of the PR:
$ git checkout pull/2957
$ git pull https://git.openjdk.java.net/jdk pull/2957/head