- Boosting Server Performance

As more machines get connected to, it's important that the server be able to handle all of the traffic as efficiently as possible. Over the weekend, I have been looking into various optimizations I would make. Mind you, the server is built in C# and so, there is plenty of things that can be done. First, I looked at changing the way I handle network traffic. There is a new way to handle sockets in Microsoft .NET Framework 3.5, instead of using: BeginConnect, BeginReceive, BeingSend... you use ConnectAsync, ReceiveAsync, SendAsync... This is very interesting and had lots of potential until I realized that this feature did not work with secured connections (SSLStream). Really very sad.

Then I looked into another optimization opportunity. I use a lot of temporary buffers in the server, that is I create a lot of "MemoryStream" objects. For example, when wanted to encode a data command, here is the code I would use:
[c-sharp]using (MemoryStream mem = new MemoryStream())
mem.Write(BitConverter.GetBytes((short)1), 0, 2);
mem.Write(BitConverter.GetBytes((short)m_swarmserverid), 0, 2);
mem.Write(BitConverter.GetBytes((short)ev), 0, 2);
mem.Write(data, 0, data.Length);
Well, it's all good, but this code would create a bunch of new objects each time it runs. The MemoryStream and the results of each BitConverter plus the ToArray(). Starting tonight, the server uses a different system based on a pool of "BinaryWriter" objects. First, I created a pool that would create a new BinaryWriter when none are available, and allows code to put BinaryWriters back into the pool when they are done. Lets see what it looks like:
[c-sharp]private static Stack<BinaryWriter> BinaryWriteRecycleList = new Stack<BinaryWriter>();
public static BinaryWriter GetBinaryWriter() { lock (BinaryWriteRecycleList) { return (BinaryWriteRecycleList.Count == 0) ? new BinaryWriter(new MemoryStream()) : BinaryWriteRecycleList.Pop(); } }
public static void RecycleBinaryWriter(BinaryWriter obj) { lock (BinaryWriteRecycleList) { ((MemoryStream)obj.BaseStream).SetLength(0); BinaryWriteRecycleList.Push(obj); } }

BinaryWriter bw = MeshUtils.GetBinaryWriter();
bw.Write(data, 0, data.Length);
First, i have the 3 lines of code that implement the object pool. It's just a simple stack I can push and pop. Note the SetSize(0) call, this resets the BinaryWriter and gets is ready for the next use. Then, the data encoding code, but using the object pool, BinaryWriter (instead of BitConverter) and the final call to put the BinaryWriter back into the pool. it took me all day to change all the data encoding everywhere in the server to the new technique. After it was done, I ran the server a few minutes with counters on the pool methods... I asked from the pool and recycled a BinaryWriter object 3000 times and only created 3 objects in total. So the same 3 objects are just re-used over and over again.

So far, I don't expect to see any visible performance difference, the huge 16 thread server is plenty capable of handling all current traffic even if unoptimized. But as we scale up, I think this change is a good idea.

Update: I fixed the "Recycle" method to add the missing lock.



Per informazioni più dettagliate sulle ottimizzazioni basate su compilatore, vedere il nostro Avviso sull'ottimizzazione.