If I am bored I just pick up a little problem that has bothered me and I just fix it. Like this other day. We have this product right where a mobile device is interacting with a Java server using Hessian. I wrote most of the Java server and the Mobile application so I should know how it works. There was something with the speed of the synchronization of the data that just didn't feel right so I sat down and started writing some tests for it. The mobile application is written in C# using Visual Studio. Unfortunately there are no freeware/opensource performance monitoring tools that I know of as of now that covers that platform so I had to resort to some of my own tricks. (I might write something myself as soon as the Compact Framework team is adding dynamic proxy support to the framework.)
So after some time I discovered that during the synchronization of the data some methods were called with their arguments evaluated dynamically. These dynamic arguments are quite expensive and should not be performed during sync. So we basically have this type of situation:
// running on the client in the syncService..
while(until no more dataChanges or exception)
{
serviceMethod(extractArguments(dataChange))
...
}
public void addToChangeLog(Date date, Operation op, ...)
{
if(IsSyncing)
return;
// add to changelog...
}
...
// in the proper service method, called during sync or normal operation..
public void serviceMethod(Type arg1, Type arg2, ...)
{
// 1) do work... save to DB and so on...
...
// 2) save changes to dataChangeLog to be picked up during next sync
// but should not be done if we are syncing already
syncService.addToChangeLog(currentDate, operation,
extractNewDataAsStringVeryExpensive(sourceObject));
}
In the call to
syncService.addToChangeLog
we have one argument extractNewDataAsStringVeryExpensive
that is evaluated before the addToChangeLog
is called. This method is very expensive in terms of execution time. But this is not directly obvious when running this piece of code just a few times. Lets say the argument takes 200ms to complete and this method is called 100 times then the total execution time would be 200ms x 100 = 20 seconds. That is 20 seconds wasted.So by changing the code to something like...
public void serviceMethod(Type arg1, Type arg2, ...)
{
// 1) do work... save to DB and so on...
...
// 2) save changes to dataChangeLog to be picked up during next sync
// but should not be done if we are syncing already
if(!syncService.IsSyncing)
syncService.addToChangeLog(currentDate, operation,
extractNewDataAsStringVeryExpensive(sourceObject));
}
...we will fix the problem but have introduced a special case check in otherwise generic code. The check whether we are syncing already has been moved one level up to the calling code. Once would think that this problem could be handled with an around advice in an aspect where we have full control of the invocation of the target method, but no. The arguments would still be evaluated before the target method was called. If would be nice somehow to be able to have an advice feature whereby you would be able to decide on what arguments to resolve before the method is called, similar to the around advice.
Take away points:
- the extractNewDataAsStringVeryExpensive method was not expensive initially. It evolved!!
- it pays of having a CONTINUOUS PERFORMANCE task in place that would run as part of the continuous integration build so that performance increasing code changes can be detected properly. This is not a trivial exercise but fun never the less
If I come up with a better solution I will write about it here... Ciao