Using AOP for Measuring Elapsed Time

December 27th, 2007 by James Carr

Recently I’ve been knee deep in some legacy code doing a fair bit of refactoring, and one of the patterns I keep coming across is processes that need their elapsed time measured. You know what I’m talking about… a procedural mess shock full of primitive obsession all over, something like the following (except imagine that the for loop is some time intensive process):

public class TimeWaster {

	public SomeResponse makeLongRequest(){
		long start = System.currentTimeMillis();
		for(long i = 0L; i < 200000000L;i++);
		SomeResponse resp = new SomeResponse("FOO");
		long end = System.currentTimeMillis();
		resp.setElapsedTime(end - start);
		return resp;
	}
}

Of course, the knee jerk response will be to hide the primitive long values manipulated all over behind some kind of abstraction, such as:

	private static final StopWatch TIMER = new StopWatch();

	public SomeResponse makeLongRequest(){
		TIMER.start();
		for(long i = 0L; i < 200000000L;i++);
		SomeResponse resp = new SomeResponse("FOO");
		TIMER.stop();
		resp.setElapsedTime(TIMER.getTime());
		return resp;
	}

A little better… but there’s still something fishy here… why does my class (despite the fact I selected the random name of timewaster) need to know about a timer? It has two roles now… generating the SomeResponse (again, remember the for loop is an assumed time intensive process of generating SomeResponse) and timing the time it takes to generate that response.

To solve this problem, we can move the timing process to an aspect, measuring the execution of the makeLongRequest using an around advice, which allows us to invoke the target method as well as modify any input and output values before returning (this is also ideal for contraint checking). So to begin, and especially since aspects can be a little tricky, we construct a very simple aspect… containing only a pointcut and a sysout in an after advice (which is called after the method finishes):

public aspect TimeWasterTimer {
	pointcut makeCall(): call(SomeResponse TimeWaster.makeLongRequest());

	after(): makeCall(){
		System.out.println("Pointcut worked..");
	}
}

I run the tests and see that yep…. that worked. So, to start small, we’ll make an around advice that simply returns the return value and run the tests to make sure they pass.

public aspect TimeWasterTimer {
	pointcut makeCall(): call(SomeResponse TimeWaster.makeLongRequest());

	SomeResponse around():makeCall(){
		SomeResponse resp = proceed();
		return resp;
	}
}

Tests pass, so let’s remove the timing code completely and run tests.

public class TimeWaster {
	public SomeResponse makeLongRequest(){
		for(long i = 0L; i < 200000000L;i++);
		return new SomeResponse("FOO");
	}
}

Now all of our tests related to elapsed time fail… perfect. Now to finish things off, we simply move the timing code to the aspect and set the elapsed time on the response before it’s returned.

public aspect TimeWasterTimer {
	private static final StopWatch TIMER = new StopWatch();

	pointcut makeCall(): call(SomeResponse TimeWaster.makeLongRequest());

	SomeResponse around():makeCall(){
		TIMER.start();
		SomeResponse resp = proceed();
		TIMER.stop();
		resp.setElapsedTime(TIMER.getTime());
		return resp;
	}
}

Rerun tests, and everything is green. Sweet! ;)

In this case, we were able to split out the responsibility of measuring execution time to a separate module by using an Aspect, allowing for a better separation of concerns. We could take this a step further an make it generic enough to be used in all cases that elapsed time is measured, perhaps by having an interface for classes that record elapsed time and a pointcut with some complex joinpoint matching magic. I’d also be interested in any alternative solutions others have come up with to solve this problem (I know Spring AOP is another way of tackling it).

Stay turned for more posts on Aspect Oriented Programming. :-P

Taking Eiffel for a Spin

November 20th, 2006 by James Carr

Lately I’ve been playing around with a multitude of languages, hoping to not only have yet more to add to my list of fluent languages to brag about, but to also gain further insight in other implementations and design from them. For this reason, I decided to play with Eiffel over the weekend.

Read More »

OOPSLA 2006: Design Fest Round 2

November 4th, 2006 by James Carr

As I mentioned earlier, I had attended the day long DesignFest session at OOPSLA my first day there. Since the whole experience picqued my interest, I decided to go ahead and attend a second session on Tuesday afternoon in an effort to perhaps inject myself more, and give it another shot.

I spotted Eric Evans sitting at one of the tables, so I decided to join them in hopes to get to see Eric tackle whatever design problem that was given, but unforunately he was only a moderator so he couldn’t participate much. :(

At first, things started a little shaky as they had at the previous DesignFest, although (thankfully) we all agreed on certain ground rules at the start, such as not being concearned about performance and such. There was quite a bit of debate on certain designs at first, and Eric even commented a bit that he felt like it was “maddening” because he couldn’t jump in and start working on the design with us. ;)

However, as time passed, we tended to find lots of common ground in our design, and suddenly everything began to mesh together in the design and it began to rapidly surface. We began quickly sketching out various sequence diagrams to show the object interactions that completed different user stories, and very interesting designs began to emerge.

The session wasn’t as long as the first one I attended, only being half a day (I believe the duration was possibly 3 hours or so). The interesting aspect was that despite the short amount of time, we actually accomplished quite a bit more than the team I was on did the first day.

Not to say anything bad of the team members of the previous team I was on, it really just boiled down to team cohesiveness. Although we were a little disjointed at first (as to be expected with a group that doesn’t know each other), as soon as we all broke the ice a bit, things began to run a bit more smoothly.

Another insight I gained from this is just about every group has “The Leader”, someone who tends to dominate the discussion at first and kind of drive the team. However, although we did have a “leader” at first, the role kind of melted away with many of us taking turns with the role, and sometimes even seeing it disappear entirely. The result was we were able to collaborate quicker and easier.

Definately a great experience.