So I was looking at serialization yesterday. Here’s what I have found out.
What Is Serialization
Saving your classes to some format that you can rebuild them from. So, say for example in Rust you have a box filled with stuff and you want to save your game.. you need some way to save that information to disk. Or if you have an item and its health has changed, you need some way to send that data from the server to the client.
Because we’re talking about a game, we’re not talking XML or JSON. We’re talking binary.
The Lowest Level, Simplest Way
I don’t know that you can get any simpler than this in C# – I’d be surprised if you can.
I think it’s safe to assume that you can’t really get any faster than this.
Reflection is slow. We all know this.. right? So yep. Iterating fields, checking for attributes, and using GetValue( obj ) is about 4-5x slower than the above method. Which isn’t crazy slow (because above is quite fast).
There’s ways to make this faster. By caching the fields and only calling GetValue( obj ) I got it to about 2x slower.
I found some code that created a function on the fly to GetValue.. and then it was only about 1.5x slower. Which was acceptable.
At this point I decided that even though this worked, and the speed was acceptable.. the amount of code wasn’t. The fact that it didn’t handle compatibility properly, all that had to be custom coded. That was a turn off.
Google Proto Buffers
So then there’s Protocol Buffers. We use this c# flavour. Protocol buffers solve a lot of problems we’re likely going to come up against. They are future compatible, so we can add new fields to them and old saves will still be compatible.
So now the process is to create a proto file, which looks like this..
Which, when we run our batch file.. generates a bunch of c# classes.. which allow us to make our new save/load to this:
And.. they’re fast. Obviously not as fast as the low level mode but not far off – certainly faster than anything I could make happen with reflection.
I experimented with a few things that did make them faster. For example, using the ProtoBuffer itself to store the variables, rather than creating a builder every time. I couldn’t justify the code mess it made, and speed increases by doing this were minimal.
It’s not made obvious in the code, but the item returned by Save can be used to create a byte array, or a json string, or an xml string. This is another great reason to use Protobuffers.. because at any time you can call Save() and save that item to JSON.. to find out what’s going on.
Also – the same proto files can be used to generate code in c++ and PHP. So you can share the data between pretty much anything and it becomes loadable and usable instantly.
Use Proto buffers if you’re saving/loading/sending network messages.