Software Development and LEGOs
I recently went to my cousin's birthday party (he turned 6), and since he's apparently become a huge Star Wars fan (read: played LEGO Star Wars and loves shooting off C-3PO's leg), my parents decided to get him a couple small Star Wars LEGO toys that were designed for that age group. They're small - maybe 30 pieces or so - but they're still pretty neat, and I can't help but be amazed at how the pieces still come together to form the whole. I was a LEGO and K'nex fanatic when I was younger, but I don't think I really played around with them since I was 14 or so - ever since I left Illinois. So when I was asked to help my cousin put them together, my first reaction was, "Hey, I don't do that anymore." But as I got started, I was drawn in - even on the little 30-piece landspeeder.
And so, I recently made one of the biggest entirely-vanity buys since I've moved into my new place:
The "Ultimate Collector's Edition" Imperial Star Destroyer. I couldn't bring myself to invest the full $500 in the Millenium Falcon, but I thought that the price wasn't too insane, a paltry 3104 pieces wasn't too daunting, and that I would have enough fun putting together the monstrous 37" set. The instruction book is about the thickness of my high school year books, and I'm disappointed but not entirely surprised that they didn't include the hyperdrive units as part of the set. I guess I'm on my own for making it actually fly.
I took the pieces out and set them on my dining room table, and as I analyze the rather daunting task ahead, I realized that, really, it's like my every day job. And the first page of the book seemed to confirm it:
Everything is done in components. We have a frame, then a wing, then another wing. The bridge. All of these go together with little regard for how the rest of the ship is built. Just like we build software. We try to solve a problem, but we try to make sure that our components can be used or adapted to solve another problem as well.
So maybe all of my formative years building with those LEGO sets weren't for nought. Maybe they were setting the stage for software development even then, tuning my problem-solving skills, making me think of things as tasks to be decomposed until the smallest task can be approached. Who's to say?
Sadly, my software development experience doesn't have a way for me to estimate time to build this bad boy. But at least my motivation's there!
Update: Check out the building marathon!
Speedy C#, Part 2: Optimizing Memory Allocations – Pooling and Reusing Objects
In C#, Visual Basic .NET, C++/CLI, J# - the list goes on - we're freed from having to worry about our memory management. Objects take care of themselves, and when the CLR garbage collector detects that an object is no longer in use, it frees the associated memory. That doesn't mean that we should run around allocating and deallocating objects all willy-nilly; in fact, since we have less control over memory, we arguably have the opportunity to be more careful with the way we use high-frequency objects.
Memory Regions in .NET
In .NET, and generally in most programming, we can think of two places in which we can access memory: the stack and the heap. We can think of Stack memory as temporary workspace, or scratch space; when we leave a function, all of our stack goes away. Way, way down in the machine architecture, the stack also stores the return addresses of functions. The stack also stores function parameters. It's generally very orderly, inexpensive, but its volatile nature makes it a poor candidate for long-term storage. In .NET, all types that derive from the ValueType class are stored on the stack unless they are boxed into an object reference; this includes types defined with the struct and enum keywords, as well as all of the primitive types except string (including int, double, and bool).
Heap memory is another matter. The heap is a region of memory reserved for the use of the program and is intended to store objects that aren't quite so transient. This might be something like a database connection, a file or buffer, or a window.
The Enemy: Fragmentation
Over time, objects are allocated and eventually released, and because there's not really any rhyme or reason, the heap becomes chaotic. Allocations grab the first free block that's large enough (sometimes larger than necessary) and hold onto it until they go into the abyss. This leads to fragmentation - all the free memory must be tracked somehow, and here's the real killer: contiguous blocks of free memory may not always be recognized as such. Check this out: let's say we have a heap allocated at memory location 0x4000 that is 32 bytes wide:
(Yes, my awesome artwork was done with none other than Excel!)
Suppose we allocate an 8-byte object and another 8-byte object, then a 16-byte object. The first is in red, the second in orange, and the third in gray:
Now I'll free the first and the third objects; we'll have 24 bytes of total free memory:
Either we need to keep track of every little piece of memory, which might be the fastest algorithm for releasing but slow for allocating (not to mention potentially VERY wasteful), or try to come up with another solution. This type of memory fragmentation is referred to as external fragmentation.
The Garbage Collector and Compaction
The garbage collector has two components: a reference counter and a compaction engine. The reference counter is responsible for determining when objects no longer have references to them; this frees programmers from having to explicitly destroy objects (as is the practice in C++ with the delete operator, or in C with the free function). A lazy thread is then able to release and compact memory as needed, avoiding much of the overhead of external fragmentation and also allowing unused memory to be reclaimed. The garbage collector in .NET is generational; it checks the newest objects first (what are called "gen-0"), and if the newest objects are still in use, they get moved to gen-1. If the memory pressure requires, gen-1 objects are evaluated, and if they are still in use, they get moved to gen-2. Gen-2 objects are considered long-lasting, and are only checked when memory pressure is severe.
Let's go back to our heap example; supposing I had an 8-byte, another 8-byte, and a 12-byte allocation, here's my heap graph:
Object 1 (in red) has gone out of scope, but objects 2 and three are sticking around. Using normal memory freeing rules, the largest object that could be allocated would still only be 8 bytes, because that would be the largest contiguous free space. However, using .NET's compacting garbage collector, we could expect something along these lines:
Here we can see we've dealt with the problem of external fragmentation by compacting the heap. This convenience doesn't come without a cost, though; while the garbage collector performed the compaction, all of your application threads were suspended. The GC can't guarantee object integrity if memory is getting abused during a garbage collection!
Preventing Compaction: Stop Killing off Objects!
Object pooling is a pattern to use that allows objects to be reused rather than allocated and deallocated, which helps to prevent heap fragmentation as well as costly GC compactions. A pool can be thought of as an object factory; in essence, the most rudimentary pool could look like this:
1: public class Pool<T> where T : new()
2: {
3: private Stack<T> _items = new Stack<T>();
4: private object _sync = new object();
5:
6: public T Get()
7: {
8: lock (_sync)
9: {
10: if (_items.Count == 0)
11: {
12: return new T();
13: }
14: else
15: {
16: return _items.Pop();
17: }
18: }
19: }
20:
21: public void Free(T item)
22: {
23: lock (_sync)
24: {
25: _items.Push(item);
26: }
27: }
28: }
Here, objects are created entirely on-demand and, when freed, are stored in a stack. The reason we want to use a Stack is the performance characteristic of adding and removing objects; operations are always performed at the end of the list, which makes it highly efficient to add or remove items. If possible, it may be prudent to pre-create a number of objects for use throughout the lifetime of your application.
Here's an example: the project I've been discussing lately uses a pool of byte arrays to handle incoming network messages received and sent via a Socket. When pooling is enabled, over the course of the application's lifetime, there were 17 Gen-0 collections, 5 Gen-1 collections, and 3 Gen-2 collections; a total of 270 byte[] instances were allocated, of which 44 were eligible for pooling and were pooled. When pooling is disabled, there were 22 Gen-0 collections, 5 Gen-1 collections, and 3 Gen-2 collections; a total of 11,660 byte[] instances were allocated, of which approximately 10,900 were eligible for pooling. That's a lot of memory!
Summary - When and Why
Object pooling is a powerful optimization technique, and if you're already using factory patterns it shouldn't be terribly foreign to you. The .NET Framework includes the ThreadPool class as part of System.Threading. Other objects you might consider pooling are database connections, any expensive links to unmanaged code, or anything that needs to be allocated frequently and can then be thrown away. In my example, byte arrays are exceptionally good for this because they can be overwritten easily.
Further Reading
- Wikipedia - Object pool
- Garbage Collection: Automatic Memory Management in the Microsoft .NET Framework (MSDN)
- Marissa's Guide to the .NET Garbage Collector
The "Speedy C#" Series:
- Part 1: Optimizing Long if-else or switch Branches
- Part 2: Optimizing Memory Allocations - Pooling and Reusing Objects
- Part 3: Understanding Memory References, Pinned Objects, and Pointers
- Part 4: Using - and Understanding - CLR Profiler
- Part 5: Using Threads with Waits, or "Don't Kill Your CPU"
Speedy C#, Part 1: Optimizing Long if-else or switch Branches
Lately I've been doing some interesting work that I've alluded to elsewhere dealing with the binary communications protocol hosted Blizzard Entertainment's Battle.net game service. It's kind of what brought me into C# development in the first place; I walked away from it for a few years, and now I've been digging into it again. And I've learned a few things between then and now; I've been particularly interested in looking at the under-the-hood workings of the CLR, and so I'm starting a new series on "Speedy C#". Let me be the first to point out that optimizations have a unique way of obfuscating code; particularly in this example, if you don't explain why you're doing what you're doing, and exactly what result you expect, you could run into trouble, or worse, your colleagues may run into trouble. So while going through this series,
A little background: the binary protocol used for Battle.net has about 80 or so message IDs, which generally have a different structure for each. The messages don't necessarily come as a result of sending a message first, and so the general pattern is that a receive loop is in place that receives the data, parses it, and then sends events back to the client. In fact, there are no synchronous requests defined by the protocol.
When I first started programming, I had handlers for every message ID in a switch/case branching construct:
1: switch (packetID)
2: {
3: case BncsPacketId.Null:
4: break;
5: case BncsPacketId.EnterChat:
6: string ecUniqueName = pck.ReadNTString();
7: string ecStatstring = pck.ReadNTString();
8: string ecAcctName = pck.ReadNTString();
9: EnteredChatEventArgs ecArgs = new EnteredChatEventArgs(ecUniqueName, ecStatstring, ecAcctName);
10: OnEnteredChat(ecArgs);
11: break;
12: // ... ad nauseum
13: }
When I looked at this in ildasm, I noticed that it declared a max stack size of something ridiculously large (sorry I don't have a specific number - it was about 6 years ago). I also noticed that there were a LOT of branches, but not necessarily in the order in which I had written them. The compiler had intrinsically optimized my code to perform a binary search. Fairly interesting, optimal speed at O(log N), and something that most of us wouldn't have thought of naturally!
When I last revisited this type of development, I broke all of my handlers out of the branching conditional, calling a separate method to handle each message. This had a nice effect of making me not have to worry about variable name collisions like I had to in the above example, and it made the code slightly more maintainable. It's difficult to gauge on paper whether that would have been better or worse performance; there was certainly far less stack allocation, but there was an additional (potentially virtual) method call.
The latest code incorporated into my library takes a different approach: I declare a Dictionary<BncsPacketId, ParseCallback>, populate it with default handlers, and allow existing handlers to be replaced and new ones to be added provided certain conditions are met. This has had several benefits:
- According to MSDN, Dictionary<TKey, TValue> approaches O(1), which is (obviously) the fastest lookup we could hope for.
- Adding support for new or changed messages does not require change to the code, only that a handler be updated via a method call.
- Handlers can be switched at runtime.
In this code, a ParseCallback is a delegate that accepts information provided by the message header and the message contents themselves. This has modified the entire parsing thread to be:
1: private void Parse()
2: {
3: try
4: {
5: while (IsConnected)
6: {
7: m_parseWait.Reset();
8:
9: while (m_packetQueue.Count == 0)
10: {
11: m_parseWait.WaitOne();
12: }
13:
14: ParseData data = m_packetQueue.Dequeue();
15: if (m_packetToParserMap.ContainsKey(data.PacketID))
16: {
17: m_packetToParserMap[data.PacketID](data);
18: }
19: else
20: {
21: switch (data.PacketID)
22: {
23: #region SID_NULL
24: case BncsPacketId.Null:
25: break;
26: #endregion
27: default:
28: Trace.WriteLine(data.PacketID, "Unhandled packet");
29: if (!BattleNetClientResources.IncomingBufferPool.FreeBuffer(data.Data))
30: {
31: Debug.WriteLine(data.PacketID, "Incoming buffer was not freed for packet");
32: }
33: break;
34: }
35: }
36: }
37: }
38: catch (ThreadAbortException)
39: {
40: // exit the thread gracefully.
41: }
42: }
Now, obviously, this is a very domain-specific optimization that I wouldn't make unless it makes sense in the problem domain. For mine, it does; I am writing the library so that others are able to integrate functionality without having to worry about modifying code that they maybe are not familiar with or are worried about breaking. If you absolutely need to use this method, be sure to document why.
The "Speedy C#" Series:
- Part 1: Optimizing Long if-else or switch Branches
- Part 2: Optimizing Memory Allocations - Pooling and Reusing Objects
- Part 3: Understanding Memory References, Pinned Objects, and Pointers
- Part 4: Using - and Understanding - CLR Profiler
- Part 5: Using Threads with Waits, or Don't Kill your CPU
My C# 4.0 Wishlist Part 6: Automatic Properties for Enum Variables
OK, so I lied; I'm not stopping at 5 parts.
I've been working with enumerations frequently lately; the Battle.net chat protocol is binary and therefore the values that come over the wire have different contextual meanings based on the values that might have preceded them. For example, a chat message event actually can have about a dozen meanings; it can be a server-broadcasted message, a message from another user, or just an announcement that a user joined the channel. In addition to the standard values identifying things like message type, messages typically have one form or another of flags; if the event is based on a user, the flags contain information about the user's status on the server (whether the user is an administrator or has operator privileges in the channel). Others, such as channel information updates, contain information about the chat channel itself, such as whether it is public, silent, or otherwise normal.
The Problem
Having had to deal with enumerations frequently has made me hate code like this:
1: if (((UserFlags)e.Flags & UserFlags.ChannelOperator) == UserFlags.ChannelOperator)
Especially when working with bitwise values (enumerations decorated with the [Flags] attribute), because of the specific operator precedence constraints that C# places on the developer, this becomes annoying quickly. So much so, that classes where I have to do that frequently end up with several TestFlag() methods, but even these are limited. Consider code like this:
1: bool TestFlag(UserFlags test, UserFlags reference) { ... }
2: bool TestFlag(ChannelFlags test, ChannelFlags reference { ... }
Or this:
1: bool TestFlag<T>(T test, T reference) {
2: // hard to implement since no meaningful type constraint can be placed on T
3: }
Or this:
1: bool TestFlag(int test, int reference) { ... }
In proposition 1 we have to implement n methods, either repeatedly or in a globally-defined, internal utility class; that stinks. Proposition 2 is difficult to implement; we can't place a type constraint because C# doesn't allow enum type constraints, and since enums have a type constraint themselves of always being an integral value, this would be ideal; but type constraints in this case are limited to struct, which doesn't guarantee operator | or operator &. In proposition 3, every time we want to test, we need to cast to int (or long) and lose type information. I guess that works, but then you worry that you end up with code like this:
1: if (TestFlag((int)e.User.Flags, (int)UserFlags.ServerAdministrator))
2: {
3: // ...
4: }
5: else if (TestFlag((int)e.User.Flags, (int)UserFlags.ChannelOperator))
6: {
7: // ...
8: } // ...
No, there's a cleaner solution, and, like the compiler features added to C# 3.0, it doesn't require a new CLR: automatic properties on enumerations.
The Solution
Internally, enumerations are treated as their base numeric type by the CLR; the variable itself carries around type information, but it's not strong and can be changed by direct casting. But the compiler always knows the type of a local variable and can apply it directly. So, consider applying a property to an enumeration variable called IsEnumField. Consider this [Flags] enumeration, and look at the code that uses it when using this style of coding:
1: if (e.User.Flags.IsNone)
2: { }
3: else if (e.User.Flags.IsBlizzardRepresentative || e.User.Flags.IsBattleNetAdministrator)
4: { }
5: else if (e.User.Flags.IsChannelOperator
6: { }
7: else if (e.User.Flags.IsNoUDP)
8: { }
We can easily identify the pattern that the compiler supports; prefix "Is" to the field name and perform the underlying logic.
The great part about this solution is that the emitted code is exactly the same as what you or I would produce right now. So the compiler can know by its clever compiler tricks to do this:
1: if (e.User.Flags == UserFlags.None) { }
2: else if ((e.User.Flags & UserFlags.BlizzardRepresentative) == UserFlags.BlizzardRepresentative
3: || (e.User.Flags & UserFlags.BattleNetAdministrator) == UserFlags.BattleNetAdministrator) { }
4: else if ((e.User.Flags & UserFlags.ChannelOperator) == UserFlags.ChannelOperator) { }
5: else if ((e.User.Flags & UserFlags.NoUDP) == UserFlags.NoUDP) { }
In that example, I qualified the type name UserFlags nine times. Can you say "carpal tunnel"?
Future-Proofing
There are some considerations to make about this. First, there are already going to be some enumerations in the wild with field names that begin with "Is," and it could very easily raise confusion if someone sees code such as user.Flags.IsIsOnline. Fortunately, the solution is equally simple: create a decorator attribute, just like we did for extension methods:
1: namespace System
2: {
3: [AttributeUsage(AttributeTargets.Enum)]
4: public sealed class EnumPropertiesAttribute : Attribute { }
5: }
Then, when you create an enumeration that you'd like to expose these style of properties, simply decorate the enumeration with this attribute. IntelliSense knows to show the properties, the compiler knows to translate the properties, and we're in the free and clear.
Wouldn't it be great?
