Live data from Hacker News

Testing a 1,000 player Minecraft server with Folia

cubxity.dev

71–80 of 166 posts

Re: Testing a 1,000 player Minecraft server with Folia

#71
post #68

I know this isn't the point of Folia's test, but: > During the period when 1,000 players were online, we reached a maximum of ~7.9GB/s heap allocation and our GC was hovering around 2-3GB/s when averaged over a minute. As someone who dealt with soft-realtime telephony stuff, this makes me want to scream in horror. It seems like the platform really hurts the performance here. In an average second with that many player…

This is not an issue with the runtime. If anything, the JVM is handling this really well. The problem is code like: while(game_running) { position = new Box(x,y,z) } instead of reusing objects: while(game_running) { position.set(x,y,z) } plus, Minecraft is not static! There are many thousands of other things moving and changing state in the world.

An age old question... Is the code wrong for not being optimal, or is the compiler/JIT wrong for not optimizing it? I've seen optimizing compilers like clang, gcc, rust optimize constructs similar to this to the same machine code. Ideally you want your high-level stuff that makes code easier to understand to be completely transparent and not have a performance impact when the code actually runs.

Re: Testing a 1,000 player Minecraft server with Folia

#72
post #2

Always fun to continue to see innovation within the Minecraft Java space. At Hypixel we ran (and I believe they still do run) a custom fork of Spigot from 2014-ish, with features from each sequential update to Minecraft being added to our fork via our Spigot fork. This let us diverge greatly and customize the Minecraft protocol to our own needs, saving hugely on internal bandwidth and letting us optimize crap out of…

> Always fun to continue to see innovation within the Minecraft Java space.

Just don't expect that innovation to come out of Mojang/Minecraft

Re: Testing a 1,000 player Minecraft server with Folia

#73
post #2

Always fun to continue to see innovation within the Minecraft Java space. At Hypixel we ran (and I believe they still do run) a custom fork of Spigot from 2014-ish, with features from each sequential update to Minecraft being added to our fork via our Spigot fork. This let us diverge greatly and customize the Minecraft protocol to our own needs, saving hugely on internal bandwidth and letting us optimize crap out of…

Oh god that's a throwback. I remember hanging out in the bukket IRC before all the legal debacles. I worked on MCSG and Wynncraft. It honestly was a blast slamming together patches on spigot(craftbukkit(nms))) onion; I remember writing a patch to run entities off main-thread among other things. I hope you're doing well and I hope Hytale is doing alright! I feel like everyone I intereacted with back then ended up doing well in tech in some form.

Re: Testing a 1,000 player Minecraft server with Folia

#74
post #64

Earlier quoted context omitted.

I'm excited for Project Valhalla to affect Minecraft performance; a lot of the temporary allocations are things like BlockPos objects (a Vec3i, basically) and VoxelShapes (a tree of axis aligned bounding boxes; so, like, an array of a struct that's six f64s) which seem ripe for becoming value objects that are inlined by HotSpot instead of living on the heap

Why not pool these objects? I'm primarily an Android developer and it's a well-known easy optimization on Android to avoid short-lived objects as much as possible, but especially during drawing and other actions that run every frame. You just don't use the word "new" in onDraw and other related methods. Android Studio would even warn you if you do. But then the new APIs in JDK itself are designed such that you have t…

Allocation is typically really cheap, maintaining pools for objects would likely have more overhead. And while collecting garbage takes resources too, it's heavily concurrently, especially in GCs like Shenandoah and ZGC. So instead of more overhead due to pooling on the thread that uses the objects, you have more overhead on a different thread during garbage collection.

So while it makes sense to avoid unnecessary allocations by using different APIs (e.g. not creating Streams in hot paths), pooling brings far more new problems with it. It might make sense for large objects, but generally requires in-depth analysis to make sure it actually helps.

Also, when plugins come into the equation, you need to make sure that those can't modify objects they aren't meant to modify, which involves copying of objects. Additionally, some objects have different representations in the API (what's used by plugins) vs the implementation (what's used by vanilla minecraft), so converting between those representations is another source of allocations.

Re: Testing a 1,000 player Minecraft server with Folia

#75
post #68

Earlier quoted context omitted.

This is not an issue with the runtime. If anything, the JVM is handling this really well. The problem is code like: while(game_running) { position = new Box(x,y,z) } instead of reusing objects: while(game_running) { position.set(x,y,z) } plus, Minecraft is not static! There are many thousands of other things moving and changing state in the world.

An age old question... Is the code wrong for not being optimal, or is the compiler/JIT wrong for not optimizing it? I've seen optimizing compilers like clang, gcc, rust optimize constructs similar to this to the same machine code. Ideally you want your high-level stuff that makes code easier to understand to be completely transparent and not have a performance impact when the code actually runs.

For my simple case, it would. Generally, the code around the allocation is complicated so maybe it's not getting optimized for some reason. Or maybe the object is passed through some complicated path... or maybe it's added to a list and then cleared at the end of the frame...

Re: Testing a 1,000 player Minecraft server with Folia

#76
post #53

I know this isn't the point of Folia's test, but: > During the period when 1,000 players were online, we reached a maximum of ~7.9GB/s heap allocation and our GC was hovering around 2-3GB/s when averaged over a minute. As someone who dealt with soft-realtime telephony stuff, this makes me want to scream in horror. It seems like the platform really hurts the performance here. In an average second with that many player…

> In an average second with that many players, most (all?) of them will not do any action apart from changing their position The world is not static though. Each player loads in a lot of living entities (monsters/animals/...) and block entities (furnaces, redstone, ...) that all need to update. There's some overlap of course, though in a game like Minecraft where there's a near infinite world to explore that overlap…

Well yeah but that's a process that can run independently from player movement and (I guess?) can be a highly asynchronous and paralellised process.

(note: armchair remark, I honestly haven't a clue)

Re: Testing a 1,000 player Minecraft server with Folia

#77
post #64

Earlier quoted context omitted.

Why not pool these objects? I'm primarily an Android developer and it's a well-known easy optimization on Android to avoid short-lived objects as much as possible, but especially during drawing and other actions that run every frame. You just don't use the word "new" in onDraw and other related methods. Android Studio would even warn you if you do. But then the new APIs in JDK itself are designed such that you have t…

Allocation is typically really cheap, maintaining pools for objects would likely have more overhead. And while collecting garbage takes resources too, it's heavily concurrently, especially in GCs like Shenandoah and ZGC. So instead of more overhead due to pooling on the thread that uses the objects, you have more overhead on a different thread during garbage collection. So while it makes sense to avoid unnecessary al…

Pooling would have been more overhead than allocation + deallocation? Do you have any relevant readings?

No idea how to do it in java but a few pointers ought to be enough. You can also omit clearing the memory area between allocations for things that aren't security sensitive, if that is done in java, which I would assume.

Re: Testing a 1,000 player Minecraft server with Folia

#78
post #3

Very excited to see how far minecraft servers can be pushed! If anyone is curious why you’d want to have 1000 players in minecraft, this video documents a pretty amazing example scenario: https://youtu.be/zv-TS_mEHE4

According to the making of video of this, they are using Multipaper: https://github.com/MultiPaper/MultiPaper

Re: Testing a 1,000 player Minecraft server with Folia

#79
post #53

Earlier quoted context omitted.

> In an average second with that many players, most (all?) of them will not do any action apart from changing their position The world is not static though. Each player loads in a lot of living entities (monsters/animals/...) and block entities (furnaces, redstone, ...) that all need to update. There's some overlap of course, though in a game like Minecraft where there's a near infinite world to explore that overlap…

Well yeah but that's a process that can run independently from player movement and (I guess?) can be a highly asynchronous and paralellised process. (note: armchair remark, I honestly haven't a clue)

Not really.

Block entities (those with complex data, like inventories) can be read/interacted with by redstone (a regular-ish set of blocks in terms of data), which can then trigger a piston or dispenser (which changes the world), and said piston or dispenser can then interact with the players movement, pushing them, or hitting them with an arrow, or dispensing water that changes their movement.

Same again for non player entities, but also factor in their AI having to adjust pathing based on the world changing.

Re: Testing a 1,000 player Minecraft server with Folia

#80
post #68

Earlier quoted context omitted.

This is not an issue with the runtime. If anything, the JVM is handling this really well. The problem is code like: while(game_running) { position = new Box(x,y,z) } instead of reusing objects: while(game_running) { position.set(x,y,z) } plus, Minecraft is not static! There are many thousands of other things moving and changing state in the world.

An age old question... Is the code wrong for not being optimal, or is the compiler/JIT wrong for not optimizing it? I've seen optimizing compilers like clang, gcc, rust optimize constructs similar to this to the same machine code. Ideally you want your high-level stuff that makes code easier to understand to be completely transparent and not have a performance impact when the code actually runs.

Neither is wrong, and there's very likely a lot of low hanging JVM performance fruit here still to claim.

Right now they're in a bit of a transitional period. Since a few weeks ago the most obvious thing to try is using the latest Oracle GraalVM with ZGC. ZGC is a pauseless GC like Shenandoah and the Graal compiler is a lot better at escape analysis and removing allocations than the stock C2 compiler especially now Oracle made the enterprise compiler free to use.

The main problem is going to be that ZGC Generational is not quite launched yet. Without generational GC it may not be able to keep up with those very high allocation rates, even with a better compiler reducing them down again. Generational ZGC should be in Java 21 but then you'd have to wait for GraalVM to catch up. So, probably it's worth trying that combination in the next six months or so.

Whilst I don't know about ZGC vs Shenandoah, the performance improvements from using Graal EE over C2 can be large even for Java (the wins are much bigger still for other higher level/more dynamic languages). But the Minecraft community isn't really known for adopting the latest JVM tech. They're pretty conservative.

Edit: someone downthread linked to https://github.com/brucethemoose/Minecraft-Performance-Flags... which talks about all of that so I guess they're getting more experimental!

Post reply on HN