Refactoring SpiderMonkey

I was thinking of something similar as well except where the message object is added to a queue and serialized on the network thread. Would a system like the following be good?:

  1. One ServerSocket thread on server
  2. Accepts connections and then creates new Kernel object to read, write, and process messages
  3. Each Kernel gets it’s own thread
  4. Read kernel just reads from a DataInputStream similar to my previous example (on it’s own thread, so blocking isn’t an issue)
  5. Read kernel passes data to a message processing queue where the messages are deserialized and listeners called
  6. Write kernel keeps an arraylist of queued messages and sends + serializes them as needed using interrupts
  7. The same read+write kernels are used on the client and the server. The difference is how they are constructed.

In this case, although Sockets are being used, there isn’t an issue since each socket is a private field of a Kernel object so I don’t think that this violates the rule of keeping those kinds of objects private.

I don’t know the NIO library nor do I have the time to learn it so I can’t implement anything using that right now.

Another possibility (albeit perhaps not a good one) is to have the serializer write directly to the DataOutputStream of the socket so that messages can start being sent as they are serialized. This is what I used in addition to the thread described above when I implemented a non-JME game that needed a networking framework.

Why not just add a setBufferSize() method? If an NIO’s buffer size can’t be changed on the fly it should be easy enough to construct a new one. That way, each user can choose what they need.

The other limitation is that the message size is sent as two bytes… so you are limited to 65535 max anyway.

No matter what… ever single thing about your networked game will be happier is you break you up your huge data into some management chunks. Whether that’s 32k or 64k or 128k it doesn’t matter. There could be any number of things that would like to get a message through on that channel that you’ve blocked with the 2 meg image your are sending as one giant stream. Everything will be happier if there is some max size and there is always going to be a case where you want to send something bigger.

So why not implement a streaming service to handle that case?

Fair enough. I was trying to think of a temporary solution until something better can be implemented. Also, dosen’t SpiderMonkey support multiple channels each with their own connection (I never saw anything about this on the wiki but I’ve seen people talk about this on the forums)? If so, then it is easy enough to have a separate buffer size per channel.

Yeah, but there still could be reasons to limit the size of messages. Right now is spider monkey drops a connection then you are done. Someday I’d like to implement a reconnect under the covers. That’s going to be pretty impossible if channels are tied up sending megabytes of data.

The service described is not hard to write. Someone just needs to do it. Volunteers welcome. It’s easier to do than anything proposed so far by a longshot.

If there are two channels, the main one and a bulk one and the main one disconnects, what prevents the main one from reconnecting while the bulk one sends data? Web browsers do this all the time (continuing to browse while a large file is downloading).

Are you referring to the service I described or the idea of using bitstreams (I’m not even sure what those are – Google gives nothing) to re-implement the serializer?

When one drops, they all drop. 99 times out of 100. (Probably 9999 times out 10000.)

Mythruna uses four channels. I’ve never ever seen one go down without the others. The hiccup that kills one kills them all.

I’m referring to the streaming service I described… built on top of SpiderMonkey using the new service model. Open an OutputStream on one end and an InputStream on the other and let the service chunk up the data how it likes using regular SM messages under 32k.

Our apologies for the double correspondence: we first decided to make contact through mail, but then decided to use the forum for more open communication. We also did not mean to imply SpiderMonkey was not functioning properly at the moment in any way.

Thanks a lot for the discussion. Our current plan is as follows: We will write tests for the current Serialization behavior in order to capture the current functionality in an automatic and reproducible fashion. We will then create another version of this behavior using Java’s built-in Serialization functionality, and assess its performance in relation to the current behavior. Our plan is also to report these findings (positive or not) to you, and maybe even discuss a pull request if the findings are positive. We will try to contribute the tests we wrote anyway.

We think it could be useful, as it could shorten the SpiderMonkey library, and less code generally means less potential defects. It seems like it could also shorten client code by a small bit, eliminating the need to register classes for serialization. We realize there are potential pitfalls, as the Java built-in serialization seems to bring large overhead (as mentioned here and here).

In our research so far two questions sprung up that we have been unable to find an answer for so far, do you mind if we use this topic for our findings? If so, please tell us what a better location would be.

  1. Is it intended behavior that you are unable to add more (TCP) channels when not specifying a UDP channel? You can create a Server with only a TCP channel. This means the channels List will have only one channel in it. Trying to add a channel using addChannel will result in an IllegalStateException (line 149 in DefaultServer.java). If it’s not, should we create an issue?

  2. Why are the Maps in the FieldSerializer static? It seems that private static fields are shared among multiple instances of the same class, but we didn’t think different FieldSerializers should share their saved fields and constructors. We stumbled across this when trying to execute new FieldSerializer() in a test: it gave a NullPointerException for savedCtors on line 62 in FieldSerializer.java, which we found to be very odd. Removing the static modifying removed the error.

As I understand it, one of the main reasons for not using Java’s built-in serialization is the size of the serialized messages. If you use it, this is something you will need to solve.

That’s it right there. We’re not stupid so of course we considered Java serialization… but for the size of messages we send, the overhead of Java serialization is often greater than the size of the message payload. Just serializing the class name itself is larger than some message payloads.

You are welcome to perform your exercise but I can already tell you the results will not be acceptable.

1 Like

Thanks for your reactions, we’ll definitely take the ‘typical’ jME message in account: is MonkeyZone still representative for a simple game using networking? Other examples we could use?

Should we post the questions elsewhere to have a higher chance to be answered?

Here is fine for questions.

As promised we will share our results:
We replaced the existing functionality with Java built-in serialization and two alternative libraries: FST and Kryo.

Doing this we were able to remove about 1250 lines of code (with more possible) which means less maintaining. All three support streams, making refactoring from buffers easy. FST and Kryo support optional registration, meaning code does not break when you forget to register, but the gain in efficiency is possible. Small note: we were unable to test Kryo with registration.

In terms of serialization size, jME performs best (props for that), but FST and Kryo follow closely. For a message containing a few Vector3f and Matrix3f, jME fit it in 137 bytes, FST in 149. Java built-in serialization is exceptionally bad with 552 bytes.

We tested performance using a throughput test due to the lack of ‘real world’ example (MonkeyZone doesn’t work in the newest jME). Again, jME performed best closely followed by FST and Kryo. We are aware of the fact the throughput in a throughput test is limited by many other factors and we believe the difference in performance is unnoticeable in real games.

We think considering replacing the hand-made serialization system with either FST and Kryo is a good idea. It introduces a external dependency, but saves a lot of potential maintenance in the removal of over 1000 lines of code. We also believe the libraries are less buggy and the optional registration saves users and you lots of headaches. Performance is very similar and the differences are most likely unnoticeable.

If you want, we can help setting up a pull request. We also have written some unit tests that would be relatively easy to add. Our code can be found in this branch: https://github.com/rbottema/jmonkeyengine/tree/benchmarking

2 Likes

So, JME’s serialization performs best. It’s not buggy… it’s used by many games without issue.

When we replace it, we will replace it with bit streams and it will be even more efficient.

JME used to do this, too… but then you have increased size because you have to send more information and keep track of per-connection registration. For JME, all connections have the same registrations, thus a message could be serialized once and broadcast to all clients instead of serialized X number of times. At any rate, optional registration would have to work at either end and it gets more complicated with a messaging protocol involved.

One open source project is not necessarily any less or more buggy than another.

I am sorry but at this point I have to put my two cents into this “conversation”. I have been working with high performance real-time systems for 30 years, been a part of Java even before it was Java … Yes I was involved in the SUN interactive TV tests that actually created java and also part of the blackdown project that ported java to linux … those green threads back then where a killer … But I digress … needless to say, when it comes to java I am as much of an expert as you can be. Anyhow, back to this conversation. I have read every post in this thread and nowhere have I seen ANY mention of ANY problems with spidermonkey. If you want to re-write it so you can teach yourself about how a gaming network layer works then go right ahead and understand that it’s just an academic exercise for you so that YOU can learn about things that @pspeed has obviously learned and implemented. As for the message buffer “limit” you obviously don’t know how network layers actually work. Do you actually think that the most efficient way to transmit a 1 meg message across the internet is to stream it as a huge chunk? If so, I would suggest you learn more about how the network protocols REALLY work first before even THINKING about this project. Can certain out of the box features of spidermonkey be improved? Sure, but the beauty of spidermonkey is that it’s flexible enough that … sory for screaming this but … YOU CAN JUST WRITE PLUGINS TO IMPROVE THEM! If you think that messaging bloatware is a better solution then you obviously have never deployed a high throughput networking system before. The rule of thumb in a game networking layer … ESPECIALLY in a game networking layer … is that the best and fastest solution will ALWAYS be one that is custom tailored to YOUR game mechanics. This is what makes spidermonkey so usefull. It is NOT a swiss army knife catering to every game out there. It’s a starting framework that deals with the basic transport of data and it’s connection states and allows you to customize EVERYTHING after that point for YOUR game needs. Anything more than that and it would make the package WORST not better.

My advise to you is to use spidermonkey for what it’s good at. Based on your comments and suggestions you obviously do not understand enough to even understand the simplest concept of why it’s CRITICAL to ONLY send small payloads let alone work on the transport layers that are the foundations of spider monkey. We all started out as noobs in software engineering and we all thought we knew it all when we started so I do understand you will dismiss everything I wrote in here but … You will re-read this post after you hit your head up against a wall for a few years and you will understand then.

I wish you all the luck in your little project. You will need it.

3 Likes

https://cdn.meme.am/instances/400x/63088506.jpg

Also, i really like how they’ve already assumed that pspeed code is bugged beyond salvation

We also believe the libraries are less buggy

:see_no_evil:

Thank you for your feedback. To clarify: we never intended to come over as if we know it better, we just wanted to see if our efforts could be made useful for jME instead of just being an academic exercise. All we did was offer help and time, which was definitely not required or part of the course. Open source project generally welcome contributions, but I guess there are exceptions.

Size is indeed better with registration. FST and Kryo are both pretty efficient without registration, about a 20% increase in size. The increased flexibility of this might be useful, it might not be.

We did find some bugs in SpiderMonkey, for example not being able to serialize some built-in Java classes (as they do not have an empty constructor) or the ones mentioned in an earlier post. We acknowledge that it has been working well in production for quite some time now, but this is no guarantee for the absence of bugs.

Best of luck with jMonkeyEngine.

Hey, I was kinda wondering when you expect to be able to work on that :o Am hesitating right now to get into spider monkey or something more like Netty or kryo. So do you expect to do to massive refactorisation sooner or later? Cause I would like my next network architecture to be the last, so I can just thicker stuff later on.

Netty seems pretty solid, but the 4. x documentation and examples is not really good. But am also pretty sure you once told me that even if SpiderMonkey is great, it’s probably not at a sufficient level for hardcore networking.

Not that am good enouf for hardcore networking anyway xD But still, it would be nice to start working on a definitive tech.

I forgot to ask, but since you don’t require registrations how do you avoid this issue: What Do WebLogic, WebSphere, JBoss, Jenkins, OpenNMS, and Your Application Have in Common? This Vulnerability. ?

It’s a bug… though nothing to do with serialization.

Because they are keyed off of Class objects which are as ‘singleton’ as the static reference. In fact, the whole point is to keep from constantly re-searching the classes if they’ve already been done. You shouldn’t get NPEs when constructing a FieldSerializer. I do it all the time (so does the framework). So I don’t know what NPE you got.

You can register your own custom serializer for these cases. Obviously we can’t support every class in Java by default.