**Afterburner** – a device placed in the tail pipe of a turbojet engine that injects additional fuel into the exhaust stream in order to burn it with the remaining oxygen. This creates additional heat producing further gas expansion thereby increasing the thrust of the engine.
Viade AfterburnerViade Afterburner is a freeware Microsoft Visual Studio extension/add-In designed to add some “missing” functionality to Microsoft .NET Framework’s feature set:
There are numerous development tools, utilities, libraries and code snippets out there vying for a programmer’s attention and offering to simplify software development process. Why would you want to look at yet another one? Unlike many, Afterburner requires minimal investment of your time and effort in order to benefit from it. There is no need to learn a new API or make any changes to your existing code. The only requirements are to install Afterburner and to check off what features are to be applied to the selected projects in your solution no matter what .NET language is used. The chosen features are being automatically injected into the selected assemblies during the post-compilation Afterburner step “thereby increasing the thrust” of your application. PredicamentProgrammers have been hitching a cheap ride on Moore’s law for the longest time. Hardware guys were picking up most of the tab by coming up with more and more ingenious solutions designed to push CPU clocks ever higher to give the existing applications a “free” performance boost. However, now that Moore’s law is being pitched against the quantum laws of physics, hardware engineers are being forced to introduce more cores and CPUs rather than making the CPUs faster. The ball is in the developers’ court. To keep up with the users’ demand for increased performance, more and more programmers are forced to take advantage of these extra cores. This predicament naturally brings us to the multi-threaded realm and all the glory and the perils that come with it. Offered solutionsOne of the many difficult tasks facing a developer that just started working on a mature multithreaded application is to understand its design from the point of view of its threads’ interactions. What threads are created or are entering the system? What classes are the key players controlling the synchronization between these threads? What synchronization primitives are shared by which threads? Changing threading related code without good understanding of these matters is asking for trouble. Afterburner’s Threads Map feature, based on information collected during application execution, generates an interactive UML-like diagram that attempts to answer some of these questions. The diagrams show threads accessing the system, key classes visited by these threads and synchronization primitives shared by them. With the help of the created diagram a developer can within minutes zero in on the most relevant players and relations instead of spending countless hours debugging and sifting through the source code to achieve the same level of understanding of the application’s threading architecture. Multiple threads must share data structures in a mutually exclusive fashion in order to avoid corrupting them. While a given thread manipulates a shared data item, it owns the exclusive rights to access it. All other threads seeking an access to the same item must wait for their turn. If a cycle emerges out of threads owning exclusive access to some data items while waiting for some other data items that are already held by other threads in the cycle the application deadlocks. Normally, after establishing that a “hung” application is actually deadlocked (not a simple feat in itself), the developer is faced with a daunting task of examining multiple call stacks in the system in order to determine what threads and data items are involved in the deadlock. Moreover, such an examination is hardly possible if a deadlock is encountered on a computer without all the necessary tools installed on it in advance. Afterburner’s Deadlock Detection feature alerts the running application when a deadlock is detected by throwing an exception describing the complete cycle of threads and data items participating in the deadlock with all the relevant call stacks. This information should be sufficient to understand the nature of the encountered deadlock in order to eliminate its possibility. Deadlocks usually are quite hard to reproduce since only a particular timing of events forces a buggy application to actually experience them. There exist coding strategies that can guarantee the absence of the deadlocks all together. A commonly used one, called lock leveling, is to come up with a designated ordering of all shared data items. Each thread that needs to exclusively access more than one of the shared data items at once must acquire exclusive access rights to these data items in the designated order. Well… easier said than done. In complex multi-layer multi-component applications it is easy to lose track of the acquisition order and to violate the strategy. This is where Afterburner’s Deadlock Prediction feature comes handy. It’s a dynamic analysis tool that tracks exclusive access acquisitions made by the threads in the running application and generates a report of the acquisitions performed in mismatching orders. More likely than not such a report is an indication of a potential deadlock condition and warrants a careful examination. Even though the issue of object lifetime management is not a problem specifically related to multi-threaded development, involvement of more than one thread certainly does not simplify things. Framework’s garbage collector (GC) alleviates many of the memory related issues that plague unmanaged code, but it does not solve all of the related problems. Sometimes your managed objects need to explicitly control creation and clean up of certain resources (database connections, operating system handles, etc.) that are scarce and need to be cleaned up as soon as they are no longer necessary. Relying on GC to clean things up is not an option. Such objects should implement IDisposable interface to allow their consumers to clean things up deterministically. IDisposable only provides the mechanism – it is still up to the consumer to actually call the Dispose() method at appropriate times. If Dispose() is not called the code still appears to execute correctly, albeit not as efficiently as possible. Afterburner’s Dispose Monitoring feature generates a report listing objects that implement IDisposable interface but whose Dispose() method was not called before GC got to work on them. |