Friday, July 27, 2012

Ordinals vs Column Names - Part Deux

Since the first post turned into a discussion of readability versus performance, maintainability versus speed and other very worthwhile topics, I decided to put this entire process to a road test.  I created unit tests that called a DAL services basically retrieving an entire table in each of the three presented methodologies.  To get the output, I enabled our handy-dandy unit test timer to trap the performance of the DAL list functions. In order for this to be good science, I restarted the database 3 times over the course of the testing and ran the tests in different orders each time. For retrieving just under 17000 rows consisting of 38 columns, here are the results and averages for the test run (values in Milliseconds).


Run  String  Dictionary Ordinal String Order Dictionary Order Ordinal Order Difference
1 2705 2469 2505 1 3 2 236
2 2648 2575 2506 2 3 1 142
3 2669 2520 2501 2 1 3 168
4 2570 2648 2445 3 1 2 203
5 2577 2566 2397 3 1 2 180
6 2450 2646 2575 3 1 2 196
7 2555 2606 2479 2 1 3 127
8 2843 2621 2446 1 2 3 397
9 2628 2485 2388 1 2 3 240
10 2580 2525 2400 3 1 2 180
Total 2622.50 2566.10 2464.20 0.21

So, at the end of the day, here is what I learned.  It really doesn't matter enough to worry about it.  Sure, the ordinals are consistently faster than the dictionary and the dictionary is faster than string, but the variance is hardly worth worrying about.  When retrieving 17000 rows, the average difference from the best performance to the worst performance was a matter of 2/10 of one second.  I think I got wrapped up in a common distraction know as micro-optimization, where we spend a ton of mental energy and rewrite code to gain a millisecond on a routine. Yes, you should care about performance, and yes, faster code should always be your goal, but there comes a point in time where it stops being worth the development time.  Yes, we should avoid the blatantly poor performing code mistakes that everyone knows about. But after that, we should be equally worried about the scalability, portability, maintainability and readability of our code. We should ask if saving 2/10 second while retrieving 17000 rows is the matter to discuss, or should we be discussing why we would ever be retrieving 17000 often enough in our application to have 2/10 second be an issue.  Yes, I started the discussion, thinking it was a creative way to gain performance.  Honestly, I enjoy trying to work through things like this too, which fed the distraction.  I believe now that these mental exercises should always be framed with realistic improvement potential weighed against the time and effort of the pursuit. At that point, only pursue until it starts becoming a net loss of productivity.  On this particular subject, after doing this research, count me firmly in the "It really doesn't matter, aim at code readability and object design" camp.

Tuesday, July 24, 2012

Synchronization Context and Callbacks

SynchronizationContext 
The SynchronizationContext behavior is basically a configuration that allows the asynchronous and synchronization operations of the CLR to act appropriately while being used within various synchronization models. It also allows for a simple configuration of applications to work correctly under the different synchronization environments. This gives a service a quick way of associating itself with a particular synchronization context and then allowing WCF to detect that context and automatically marshal the call from the worker thread to the service synchronization context. The default value of UseSynchronizationContext is true. Affinity between the service, host and synchronization context is set when the host is opened. If the thread opening the host has a synchronization context and UseSynchronizationContext is true, WCF will establish an affinity between that synchronization context and all instances of the service hosted by that host. WCF will automatically marshal all incoming calls to the synchronization context. If UseSynchronizationContext is false, regardless of any synchronization context the opening thread might have, the service will have no affinity to any synchronization context. Interestingly enough, if UseSynchronizationContext is true but the opening thread has no synchronization context, the service will still not have one. By default, when executing the code below the client thread will be blocked while the return value is received from the service.

serviceProxy = new SomeService(new InstanceContext(this));
serviceProxy.Open();
MyObject = serviceProxy.CreateMyObject(new MyObject(1));

This is all fine on the surface. But, what would happen if the CreateMyObject function sends a callback? The client thread would be blocked. We can handle this with the callback behavior aspect of WCF. CallbackBehaviorAttribute.

UseSynchronizationContext
As a refresher, the CallbackContract property of a ServiceContract specifies the interface to define callback operations. This will create a dependent relationship between the interfaces. Once a CallbackContract is specified, the client will have to implement the callback functions in order to interact with the service at all. The CallbackBehavior setting for UseSynchronizationContext basically governs the affinity between the service and the client. You can easily override the automatic association of synchronization contexts with a simple decoration. By setting the UseSynchronizationContext property of the CallbackBehavior attribute to false, WCF will no longer guarantee a particular thread to be responsible for processing service requests. Instead, the operations will be automatically delegated to worker threads.

[CallbackBehavior(UseSynchronizationContext = false)]

When not using synchronization context on callback behavior, you may run into issues trying to directly update the UI, since those callbacks will no longer be on the UI thread.  One way around that would be to use a SendOrPostCallback delegate.