Skip to content

Implement fast and slow value object hash paths#24356

Open
AditiS11 wants to merge 1 commit into
eclipse-openj9:masterfrom
AditiS11:value2
Open

Implement fast and slow value object hash paths#24356
AditiS11 wants to merge 1 commit into
eclipse-openj9:masterfrom
AditiS11:value2

Conversation

@AditiS11

Copy link
Copy Markdown
  • Use the recursive fast path for value object hash computation.
  • Use the fast path while the recursion depth is below J9_VALUE_TYPE_HASH_DEPTH_LIMIT.
  • Switch to the iterative path when the recursion depth limit is exceeded.
  • Use a DFS-based queue to traverse value object fields in the iterative path.

Fixes: #24087

@AditiS11 AditiS11 force-pushed the value2 branch 3 times, most recently from 8fac93c to a8c82ae Compare July 14, 2026 11:32
@AditiS11

Copy link
Copy Markdown
Author

@theresa-m theresa-m added comp:vm project:valhalla Used to track Project Valhalla related work labels Jul 14, 2026
bool depthExceeded = false;
I_32 hashValue = recursiveValueObjectHash(vm, currentThread, objectPointer, clazz, startOffset, 0, &depthExceeded, oomOccurred);
if (!depthExceeded || *oomOccurred) {
return hashValue;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Shouldn't the method return here regardless of the value of oomOccurred?

if (NULL == entry->currentField->field) {
U_32 finalHash = finalizeMurmur3Hash(entry->hashValue, entry->numBytesHashed);
if (0 == finalHash) {
finalHash = 1;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can you add back the comment that was here for clarity?
/* Hash code for value types should not be zero. */

Comment thread runtime/oti/ObjectHash.hpp Outdated
default:
/* Invalid field signature. Should assert but we are in util. */
break;
if (*depthExceeded || *oomOccurred) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Would oomOccurred ever be true here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

No Theresa, oomOccurred would never be true in the recursive path. Will remove this.

@hangshao0

hangshao0 commented Jul 14, 2026

Copy link
Copy Markdown
Contributor

I suggest adding a debugging -XX option (sth like -XX:VTHashRecDepth=n) to overwrite the default J9_VALUE_TYPE_HASH_DEPTH_LIMIT. This could help us diagnose issues with fastpath vs slowpath implementations.

Comment thread runtime/vm/jvminit.c Outdated
UDATA depth = 0;
char *optname = VMOPT_HASHMAXRECDEPTH_EQUALS;
GET_INTEGER_VALUE(argIndex, optname, depth);
if (0 != depth) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think 0 should be allowed in case we want to test the slow path only.

Comment thread runtime/oti/ObjectHash.hpp Outdated
frame->hashValue = getSalt(vm, (UDATA)frame->objectPointer, true);
frame->hashValue = mix(frame->hashValue, (U_32)(UDATA)frame->clazz);
frame->numBytesHashed = sizeof(UDATA);
frame->currentField = vm->internalVMFunctions->fieldOffsetsStartDo(vm, frame->clazz->romClass, VM_VMHelpers::getSuperclass(frame->clazz),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Please put each of the arguments on a separate line.

Comment thread runtime/oti/ObjectHash.hpp Outdated

/**
* Fast-path recursive hash computation for value objects.
* Used if recursive depth is less than J9_VALUE_TYPE_HASH_DEPTH_LIMIT.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

J9_VALUE_TYPE_HASH_DEPTH_LIMIT doesn't exist anymore. Also in line 495.

- Use the recursive fast path for value object hash
  computation.
- Use the fast path while the recursion depth is
  below J9_VALUE_TYPE_HASH_DEPTH_LIMIT.
- Switch to the iterative path when the recursion
  depth limit is exceeded.
- Use a DFS-based queue to traverse value object
  fields in the iterative path.
- Add -XX:hashMaxRecDepth debug option to override
  default recursion depth.
Fixes: eclipse-openj9#24087
Signed-off-by: Aditi Srinivas M <Aditi.Srini@ibm.com>
IDATA parentIndex;
};

struct ValueTypeHashQueue {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This becomes more of a stack than a queue ?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Yes Hang its a stack. I just kept the naming the same as the original code to minimize the diff.
I will change the naming.

{
if (full) {
UDATA newTop = (UDATA)(top + 1);
if (newTop >= capacity) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why not check if (newTop == capacity) only ?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I just added >= as a defensive check. Will change it to newTop == capacity

UDATA hashSlotOffset = fieldClazz->backfillOffset;
storedHash = objectAccessBarrier.inlineMixedObjectReadI32(currentThread, fieldObject, hashSlotOffset);
}
if (0 != storedHash) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think for non-VT field, its hash can be 0. So if the storedHash is 0 in this case, it can still be used.
The same comment apply to the slowpath.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Based on the analysis of one of the benchmark logs, we didn't observe any cases where identity types had a hash value of 0. Hence I assumed this scenario wouldn't occur.

@dmitripivkine

Copy link
Copy Markdown
Contributor

@hangshao0 How we guarantee that object pointers stored during calculation (slow path particularly) are not becoming stale? Do we assume hash code calculation executes under VM Access always to prevent GC?
If so,

  • Should assertion VM Access is taken be added?
  • Should here be a concern for delaying GC for too long potentially?

@hangshao0

Copy link
Copy Markdown
Contributor

The code here in this PR assumes the object pointers are non-stale.

Should assertion VM Access is taken be added?

We should have an assertion for this. However, there were review comments on adding assertions in ObjectHash,hpp in Aditi's previous hashcode implementation PR. but it seems assertions cannot be added into this file (don't recalled the details back then). If that is the case, I think assertion should be added to the callers in other files for VM access (it should be taken if not already taken).

Should here be a concern for delaying GC for too long potentially?

I expect VT objects are mostly leaves of the data tree that does not delay GC too long for the hash calculation.

@dmitripivkine

Copy link
Copy Markdown
Contributor

yes, assertion can be tricky here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp:vm project:valhalla Used to track Project Valhalla related work

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Valhalla valhalla/valuetypes/ValueObjectMethodsTest.java

4 participants