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

AVRO-4060: Use JDK to Hash Byte Array in UTF8 #3175

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

belugabehr
Copy link
Contributor

What is the purpose of the change

  • This pull request improves file read performance by using the JDK hashcode implementation, fixing AVRO-4060.*

Verifying this change

This change is a trivial rework / code cleanup without any test coverage.

Documentation

  • Does this pull request introduce a new feature? no

@github-actions github-actions bot added the Java Pull Requests for Java binding label Sep 25, 2024
@KalleOlaviNiemitalo
Copy link
Contributor

I feel there should be a test to verify that the result of Utf8.hashCode does not depend on whether the array has unused bytes in it. TestUtf8.hashCodeReused seems to largely cover that by hardcoding specific hash codes but it does not compute hash codes for equal sequences of bytes in two ways. I'm thinking about test code like

Utf8 u1 = new Utf8("abcdefghi"); // length=9, bytes.length=9
u1.setByteLength(8); // length=8, bytes.length=9
Utf8 u2 = new Utf8("abcdefgh"); // length=8, bytes.length=8
assertEquals(u1.hashCode(), u2.getHashCode());

or with a hardcoded hash code in the test.

@belugabehr
Copy link
Contributor Author

Hello,

Thank you for the feedback. I agree a unit test is appropriate here.

Ideally, the hashcode implementation should return the same value for the same contents regardless on the size of the array. However, that is not currently the case because the two implementations are different. I believe AVRO-4061 will fix the discrepancy between the two implementations and then this unit test becomes possible.

I also need to look at the setLength method. It's very confusing to me. If the buffer is made smaller, then the underlying string is truncated, if the buffer is made larger, than the underlying string is padded with zero (it's not just that the buffer is expanded). I don't really understand the use case of this method just yet. I feel like it shouldn't bother copying anything into the newly sized array since the end result is somewhat confusing.

@belugabehr
Copy link
Contributor Author

I have a unit test that confirms the behavior, but it does require AVRO-4061 to be merged first.

@belugabehr
Copy link
Contributor Author

Unit test will fail on this PR until AVRO-401 is addressed.

@belugabehr belugabehr force-pushed the belugabehr/AVRO-4060 branch 3 times, most recently from 408af58 to 64765e9 Compare September 28, 2024 02:21
@belugabehr
Copy link
Contributor Author

AVRO-4061 is now fixed.

Added unit test for coverage.

@belugabehr belugabehr requested a review from martin-g October 2, 2024 03:05
@belugabehr belugabehr force-pushed the belugabehr/AVRO-4060 branch 3 times, most recently from 19f72ff to 8d95711 Compare December 29, 2024 05:12
@belugabehr
Copy link
Contributor Author

@KalleOlaviNiemitalo and @martin-g - Another pass on this one please?

Copy link
Contributor

@KalleOlaviNiemitalo KalleOlaviNiemitalo left a comment

Choose a reason for hiding this comment

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

Some nits on the javadoc but otherwise it looks OK. I'm not a professional Java programmer, though.

Comment on lines 103 to 107
* There are two different code paths that hashcode() can call depending on the
* state of the internal buffer. If the buffer is full (string length eq. buffer
* length) then the JDK hashcode function can be used. This function can is
* vectorized JDK 21+ and therefore should be preferable. However, if the buffer
* is not full (string length le. buffer length), then the JDK does not support
Copy link
Contributor

Choose a reason for hiding this comment

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

"hashcodehashCode" (twice)

"function can isbe vectorized in JDK 21+"

The "eq." and "le." abbreviations look confusing. Does "le." mean "less than or equal to"? But the equality case was already described.

/**
* There are two different code paths that hashcode() can call depending on the
* state of the internal buffer. If the buffer is full (string length eq. buffer
* length) then the JDK hashcode function can be used. This function can is
Copy link
Member

Choose a reason for hiding this comment

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

This function can is vectorized ... sounds incorrect

// If the array is filled, use the underlying JDK hash functionality.
// Starting with JDK 21, the underlying implementation is vectorized.
if (length > 7 && bytes.length == length) {
h = Arrays.hashCode(bytes);
Copy link
Member

Choose a reason for hiding this comment

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

How does Arrays.hashCode(bytes) behave when the length is smaller ?
Doesn't it fall back to serial execution internally for anything that is not vectorizable ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It does fall back, but in my micro benchmarks, I found that it is better to implement this skip logic before bothering to jump into the method call itself especially since the length needs to be interrogated anyway.

@belugabehr belugabehr force-pushed the belugabehr/AVRO-4060 branch from 8d95711 to a67e68a Compare January 4, 2025 02:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Java Pull Requests for Java binding
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants