What do the phrases, "The grass is greener!", "How long did that take?", and, "iiisshhh onnny matter of timmee duuudde, eeat teh red pil!", all have in common? They represent the general context of conversation over drinks at social gatherings or local bars where I share stories of a coding project with a seasoned C# programmer. Though sometimes unintelligible, consistency makes the message clear.
As the newest member on the MSCOM OPS Debug Team, my first task was to familiarize myself with some of the technologies implemented in our production environment. This includes but is not limited to ASP.NET, VB.NET, IIS, SQL, XML and ultimately, C#. Managed destiny was upon me. Having coded in C++ for a while, I've come to know and love the "raw to the metal" performance possible with the language. "inline" and "__asm", sweet cycles, I thee save! I've stayed away from managed languages due to fear of performance losses that could occur as a result of all the 'abstraction' and 'magic' that happens to make things easier on the programmer. The time had come to take a peek over the fence.
I sketched some ideas and came up with a project that would allow me to learn the language while getting some exposure to the technologies I support in our production environment. The goal was to touch as many technologies as possible in the shortest amount of time while doing things that I expected to create performance bottlenecks so I could ultimately prove my original assumption correct. I went heads down and two weeks later it was done. Enter the "Distributed Network Load-Balanced Real-Time Lissajous Geometry Streaming/Rendering System":
The purpose was to generate a 3D Lissajous waveform and project the resulting geometry into 'view space' manually before streaming it to an arbitrary number of clients for rendering. The entire setup required 5 virtual machines running Windows Server 2003, a Virtual Server R2 host running on a Window Server 2003 64bit workstation which also hosted the end clients. 3 of the virtual machines were setup in a network load balanced cluster running IIS 6.0 and each hosted an ASP.NET web application that interfaced with a background Windows service. The two remaining virtual machines were configured with ISA Server 2004 to act as a firewall and SQL Server 2005 to host a database.
Here is the general order of operations that occur from initial client connection to end geometry rendering:
· The client is launched and instructed to connect to the public facing IP address of the firewall
· The client connects to the ASP.NET web application running on one of the three network load balanced machines through the ISA firewall and passes information about itself
· The ASP.NET web application logs the client connection attempt and information in the SQL database
· The ASP.NET web application queries the background service for its TCP/IP listening port and returns the port to the client
· The client makes a TCP/IP connection to the port provided using the public facing IP address of the firewall
· The background service receives the connection request and spins off a worker thread to work with the new client
· The worker thread generates a three oscillator Lissajous waveform in 3D space
· The worker thread translates, rotates, scales and projects the points that compose the waveform into view space
· The worker thread streams the resulting geometry to the client via the TCP/IP connection
· The client renders the geometry on three Direct3D surfaces via managed hardware accelerated DirectX
· The client pauses for 25 milliseconds and requests the next set of geometry via the active TCP/IP session
After squashing the final bug, I connected the client in and in came the geometry stream. Oh man was it slow! I was expecting roughly 30-40 frames per second with the 25 millisecond geometry request delay but it was at about 2 to 3 frames per second. The client's CPU utilization was low- something must have been wrong on the server side. Was I asking for too much? Having recently done some reading on garbage collection, I attached Performance Monitor to the background service to check on the garbage collections- Woah! 14,000 Generation 0 collections in less than 10 minutes! I reviewed the code and learned first that I'm still very new at this language and second, I probably shouldn't be instantiating all off these objects each time through all of the loops. After a few iterations of testing and tweaking performance jumped significantly. Not only will the client now render at full frame rate- but 32 instances of the client running on the host all render at full frame rate simultaneously. The reality check slowly started to creep in.
Here is the general order in which things began to occur to me:
- I enjoyed working on this project
- I've touched more technologies with this project then any other coding project I've worked on
- Managed code is much faster than I expected
- Managed coding is much easier then native coding
- Managed debugging is much easier then native debugging
- Thrown exceptions told me what went wrong
- IntelliSense typed a good portion of the code for me and greatly reduced the 'F7 compile and pray' keystrokes
- I don't hate strings anymore
Well that was a learning experience! A screenshot of the project in motion is posted below. Working with managed code and.NET allowed me to get more done with less code and debugging was notably easier. This combined with the general lushness of Visual Studio 2005 and the managed debugger extensions, .NET may have just delivered a fatal blow to my long standing perspective on native code. I'm looking forward to working with my new team using these new technologies; it will continue to be a tremendous learning experience! One thing for sure- they can be confident that when the going gets tough, I'll be hittin' the !sos.