[SPIGOT-3754] Performance of BlockState.hashCode may be improved Created: 10/Jan/18  Updated: 11/Jan/18  Resolved: 10/Jan/18

Status: Resolved
Project: Spigot
Component/s: None
Affects Version/s: None
Fix Version/s: None

Type: New Feature Priority: Minor
Reporter: Donald Duck Assignee: Unassigned
Resolution: Fixed Votes: 1
Labels: None

Attachments: PNG File Capture.PNG     PNG File Capture.PNG     PNG File Capture_alsoCachingBlockStateEnum_hashCode.PNG     PNG File Capture_hashCodeCalls_new.PNG     PNG File Capture_hashCodeCalls_old.PNG    

 Description   

When I look at a profiler's output then the BlockState.hashCode() method looks suspicious. If the profile can be trusted, it seems to consume a significant share of the server's CPU. A BlockState object has two final fields: a Class and a String object. Therefore, the hashCode() method in BlockState will always return the same value when called on the same object multiple times. Maybe caching helps?



 Comments   
Comment by Donald Duck [ 11/Jan/18 ]

Cool stuff. I re-built with SPIGOT-3754 included, and with the same world CPU is down from ~80% to ~70%. I like it!

Comment by Donald Duck [ 10/Jan/18 ]

I'll go even further. After analyzing BlockStateEnum I think we can prove that once the hash code of any BlockStateEnum has been computed it won't change. The BlockStateEnum objects are constructed with a Collection of enum literals, either taken straight from the class (which doesn't change during the life time of the containing class loader) or upon explicit request as in BlockPortal.AXIS where a constant array of enum literals is provided. The ImmutableSet is initialized at construction time with Enum literals (which don't change at runtime), and the b HashMap is built up only in the constructor, keys being immutable String objects, values being immutable Enum literals.

This is why I think it's safe to also cache a BlockStateEnum's hashCode. See the attached profile which goes to show that hashCode calculations has gone way down the stack, no longer being the bottleneck.

Comment by Donald Duck [ 10/Jan/18 ]

Regarding Black Hole's comment: subclasses may at best call super.hashCode(), and the hash code of BlockState does not depend on any polymorphic methods that may be overridden in subclasses. Therefore I think this change is safe.

Regarding md_5's question: yes, I do have before/after comparisons. I've used a world that we have here on our little private server. It's characterized by quite some redstone mechanics and a few farms that are continuously operating. With the patch installed, Object.hashCode() appears a lot further down the list.

The difference also becomes evident when looking at the call hierarchies leading into hashCode. Before the patch it looks like this:

It clearly shows that redsone block mechanics are driving CPU effort here. After applying the patch things look like this:

So here, redstone doesn't play any role anymore, and the remaining hashCode effort is caused largely by BlockStateEnum.hashCode. For the latter I'm not sure to what degree its "b" Map can change, so I'd rather leave it alone.

But for all I see, caching BlockState.hashCode()'s result doesn't have any adverse side effects and according to the profiler used improves performance significantly, at least when some redstone mechanics are involved.

Comment by md_5 [ 10/Jan/18 ]

Do you have any before and after for this?
It doesn't necessarily prove that its the same state which has hashcodes repeated.

Comment by Black Hole [ 10/Jan/18 ]

Since NMS BlockState objects are immutable this would be a great addition. But there are some subclasses: BlockStateEnum, BlockStateBoolean and BlockStateInteger.

Comment by Donald Duck [ 10/Jan/18 ]

SeeĀ https://hub.spigotmc.org/stash/projects/SPIGOT/repos/craftbukkit/pull-requests/421/overview.

Generated at Sat Dec 13 15:28:23 UTC 2025 using Jira 10.3.13#10030013-sha1:56dd970ae30ebfeda3a697d25be1f6388b68a422.