Powering and depowering rails are 3 to 4 times laggier since 1.13+ than 1.12.2-
"The primary bottleneck lies in shape comparison which is performed to determine whether the block beneath the rail is solid on top and can support it." - JellySquid
1.12.2- used a simple boolean check while 1.13+ recalculates the hitbox of the block below the rail every time
Below are some testing results
I have a void world with 10,000 rails running on a 2gt observer clock
5000 rails update every gametick (half turning on, half turning off)
16 chunk render distance
4gb of ram for the jvm
The world ran at less than half an mspt when idle which was consistent across all versions
Each test was done a few times to remove outliers
Version | Variant | TPS | MSPT | Thumbnail |
---|---|---|---|---|
1.11.2 | Vanilla | 16.96 | 58.9 | [media] |
1.12.2 | Vanilla | 16.26 | 62 | [media] |
1.13.0 | Vanilla | 5.39 | 185 | [media] |
1.13.1 | Vanilla | 13.19 | 76 | [media] |
1.13.2 | Vanilla | 12.63 | 79 | [media] |
1.14.4 | Vanilla | 4.61 | 216 | [media] |
1.15.2 | Vanilla | 4.59 | 213 | [media] |
20w10a | Vanilla | 5 | 200 | [media] |
20w11a | Vanilla | 4.19 | 228 | [media] |
1.15.2 | Lithium | 16.7 | 60 | [media] |
20w10a | Lithium | 20 | 50 | [media] |
20w11a | Lithium | 19.3 | 52 | [media] |
20w18a | Vanilla | 5 | 197 | [media] |
20w19a | Vanilla | 20 | 50 | [media] |
Lithium heavily optimizes the shape comparison check (It does not revert the code back to 1.12) and adds block ticking optimizations
World Download:
Linked issues
relates to 1
Attachments
Comments 4
Thank you both for the excellent and highly valuable report and suggestions.
I'll take a closer look at the world + improvements and get back to you asap.
Can confirm that 20w19a performance is back up to 1.12 levels (maybe even slightly above?)
Vanilla 20w18a 5tps 197ms:
[media]Vanilla 20w19a 20tps 50ms:
[media]Relates to MC-182868
Hey there. I'm the author of Lithium, which was mentioned in the ticket. The specific benchmark used here is a bit of a unrealistic synthetic workload, but it does demonstrate very clearly how recent versions have impacted performance.
The reason for the extreme slowness here is because the game will always perform an expensive shape comparison beneath a rail block in order to determine if the rail can stay at a given location (even if the block shape beneath it is a simple full-cube) and never cache the result to avoid subsequent computations. This issue is not specific to rails and also causes significant slowdown for Redstone gate blocks, which are otherwise very simple in comparison.
There are two solutions which could be employed here with varying levels of complexity, though both are not super invasive.
The shape information cache for each block state could be extended to include other properties, such as whether or not it can support a Redstone component or light source on its given faces. This data could be encoded into 6 bits (one for each face) and fit into a single byte for each property, making it essentially free when the information is cached, whereas a simple boolean array for each would require something like 24-28 bytes.
Or, the code responsible for checking if a block can support another component could simply check if the block's shape is a simple full-cube with an equality check and early-exit with a truthy value, since full-cube shapes can always support another component. This would perform the same as without it in cases where other non-simple shapes are involved, and very slightly slower than caching the information in the BlockState due to virtual call overhead in retrieving the shape from a block implementation.
I use both of these techniques in Lithium. The block state's shape information cache is checked first, and if it can't be used because the shape is non-static, a simple full-cube test is done before falling back to the slow shape equality test.