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

Improved execution field merging #2019

Merged
merged 4 commits into from
Nov 25, 2023

Conversation

kyri-petrou
Copy link
Collaborator

@kyri-petrou kyri-petrou commented Nov 23, 2023

In many cases, there's would be only a single condition on fields (either None or Set(Something)). In these cases, the field merging has already been handled within Field.apply, which means we don't need to do it again.

For these cases, we up to 15% improvement in execution time depending on the number of fields in the object

f._condition.isEmpty || f._condition.get.contains(typeName)

def mergeFields(fields: List[Field]) = {
val map = new java.util.LinkedHashMap[String, Field]((fields.size / 0.75d).toInt + 1)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this a known formula? Or just an estimate?

Copy link
Owner

Choose a reason for hiding this comment

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

Would be good to explain what this is in a comment

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The exact formula would be Math.ceil(size / 0.75) but this is close enough I believe. I can use that instead if you prefer.

The Map resizes when the number of items in it exceed the capacity * loading factor (defaults to 0.75). Using this formula guarantees that we have adequate capacity so that the Map won't need to resize. In JDK17+ there is a method that allows you to create the Maps taking the expected number of items as a parameter (instead of using the initial size) and it used this formula.

I'll add a comment for it.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I forget is scala list's size O(n) or is it stored on concat?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's O(n), although given that this List only contains the queried fields and that this method is only run for objects with multiple fragments, I think the overall impact of counting the number of elements in this list is extremely small

val condition = head._condition
var remaining = tail
while (!remaining.isEmpty) {
if (remaining.head._condition != condition) return false
Copy link
Collaborator

Choose a reason for hiding this comment

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

I was gonna suggest tail recursion but this will probably iterate tighter

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Hmm I think the compiler will rewrite the tailrec method likely to this implementation. I'll check, if it's the same I'll use tailrec

Copy link
Collaborator Author

@kyri-petrou kyri-petrou Nov 24, 2023

Choose a reason for hiding this comment

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

Looking at the generated code, it looks like @tailrec uses Java labels in an infinite while loop with return statements, whereas this one uses the !remaining.isEmpty method as a condition to terminate the while loop. Not sure which one would be more performant in theory

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah we're probably getting into a grey area of compiler optimization. I think the conditionless while might be able to skip a branch if equal instruction, though for modern chips branches aren't really performance killers anymore

*
* NOTE: This method is the same as java.util.HashMap.calculateHashMapCapacity on JDK19+
*/
private def calculateMapCapacity(nMappings: Int): Int =
Copy link
Owner

Choose a reason for hiding this comment

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

After merging this you can use it in the other PR

@kyri-petrou kyri-petrou merged commit 3388667 into series/2.x Nov 25, 2023
10 checks passed
@kyri-petrou kyri-petrou deleted the improved-execution-field-merging branch November 25, 2023 08:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants