Figure 1: An example of the kind of hierarchy that I'm talking about.
Consider the following simple class as an illustration of the main problem. Ideally all internal methods of the class would be like
getDescriptionLength_01() and use the public getter method for accessing all of its private fields. This provides maximum flexibility in the API as only the
setDescription() methods would need to be overridden in a subclass to completely take over the behaviour of the
getDescriptionLength_01() involves an unnecessary string copy as Java
Strings are not passed by reference, so it is really returning the length of a copy of the
m_sDescription field. On the other hand, the method
getDescriptionLength_02() accesses the private field directly - which uses less resources and is faster, but means that a subclass would have to override it too if the
Description property is to be fully taken over.
This is not a problem if the subclass simply wants to moderate the content going into and out of the private String field or to synchronize it with an external source. However, if it wanted to store the description content somewhere other than inside the
m_sDescription field, then it’s a problem.
This is just one example and obviously not all properties involve storing and copying strings. However the cost of using getters and setters internally is at least one additional function call per access. In a complex method that accesses many properties multiple times, this is a measurable overhead. It is possible to avoid calling the same getter multiple times in a block by assigning it to a temporary variable, but even that involves memory allocation and additional processor cycles, as well as occasional garbage collection. In methods that are used very frequently you can probably rely on the Java VM to cache a lot of these calls, but you can never really be sure how well an Android system (for example) with a relatively slow processor might handle this.
To better illustrate what I’m talking about, consider the following really simple example. The
isInTimeRange() method shows the three different ways of dealing with properties. If this method is called very frequently and the start time typically occurs before the end time, then the choice of which one to use is not at all obvious if performance is an important consideration. Now extrapolate this simple example out over a much more complex set of GUI classes with methods that are called hundreds of time a second during user interactions and hopefully you can see what I mean.
Thus, the dilemma is whether to trade more flexibility for a little less speed, or to have a bit more speed for a little less flexibility.
When coding the GUI classes I decided at the outset to favour maximum flexibility and always use getter/setter methods instead of accessing any underlying properties/fields directly, even inside base classes. However, as the functionality developed and got a bit more complex, the number of these method calls started to mount up worryingly. To counter this a bit in methods where a property was used several times, I would call the getter only once and store the value in a temporary variable, calling the setter at the end of the method only if the value was changed. This made the profiling look better and in most simple methods was fine, but in others - such as calculating a complex layout with multiple children in each region - it wasn’t.
Apart from simply shifting the burden onto the stack, the main issue with temporary internal variables was that they started to make the code in large functions a lot less readable, breaking the direct relationship between what the code was doing and what it was actually working on. I know that this could probably be managed with better thought-out temporary variable names and that any function greater than 25 lines should really be broken up into a number of smaller and more concise functions. However, the more I did that the more arguments I needed to pass between functions or the more times each smaller function needed to call getter/setter methods - adding even more overhead and burdening the stack.
In the end I changed policy and completely rewrote all classes to make their properties protected rather than private and all internal access via the raw property value, but all assignments to use the setter method. A trivial change but it made a heap of difference - more readable code, simpler methods and faster execution (though in Java pinning those numbers down in an objective and repeatable way is nigh-on impossible).
A General Policy
Thinking through this process really galvanised my view on properties. Ultimately a property has to be used by its base class in some known way. If the property is a string then, in the end, its value has to be storable and communicable as a string. Ditto with other basic types. Thus, even if a subclass were to use an entirely different means of obtaining the data for that property, it still has to provide the base class with a string in order for it to be used. Thus, it is a relatively trivial request for a subclass to simply use that property as a place to store the obtained data and to update its value whenever the external source changes.
It is also important to complement this approach with some changes to the functionality of each class. Instead of having just a single public
draw() method, for example, have protected
drawContent() methods as well, called in that order by the
draw() method. This way a subclass could choose to bypass the caption property entirely and render whatever it wanted by simply overriding
drawCaption() and then
setCaption() to provide a string-based version of whatever the actual caption is.
For this API, in the end I opted for more speed at the expense of a little flexibility, primarily for reasons of nicer coding and because it is intended for use embedded in web pages and on Android devices where processing resources may be limited. Within a user interface, hard-coding some level of standardised behaviour in each class is probably appropriate - this is what makes a button different from a list in the end anyway. Moreover, it is likely that the vast majority of end-user subclasses will mainly implement slight tweaks to existing functionality, and those implementing entirely new behaviours will probably want to override the lower level base classes and interfaces anyway.
Other Trade-offs for Speed
The other main trade-off in favour of speed and efficiency is the extensive use of nested internal classes within a couple of all-embracing outer class objects. This makes creating new instances of inner classes a tiny bit trickier, but significantly simplified the design as unrelated classes can access the protected methods and fields of other classes, allowing for a much clearer and concise exposed API. It also meant that inner classes have access to a whole range of state variables that don’t need to be constantly passed up and down hierarchies or between siblings. For example, an entire GUI may have lots of separate workspaces (or views) with many different widgets distributed over them, but only ever has one active event to process and only a single blocking menu, modal dialog, keyboard focus and pointer-capture item. These objects can be accessed directly from within any internal class completely thread-safe and specific to each GUI instance. Again, we’re only talking tiny gains but the GUI is constantly processing events and redrawing even as the pointer moves around, so many tiny savings add up in terms of system resources available to the actual host application.
I may do another post to go into the nightmares that this decision caused me when translating the GUI into C# as inner classes in C# don’t have access to the instance fields and properties of the outer class as they do in Java. Thus out went pretty well all the major benefits and now synchronising changes between the two libraries is fast becoming untenable.
This is further aggravated by the Java version’s internal event management system as all internal event handlers act directly upon the parent GUI’s own
ActiveEvent object - rather than passing around an event reference as an argument to each handler. As event management is continuous and often recursive, especially with
POINTER_MOVED events, the rationale was that any savings that could be made in temporal stack and heap allocations were probably worth it. Some extensive redesign is required here if I am to better reconcile the Java and C# versions.
Neil Almond 13 March, 2013 - 06:51
I was puzzling for a while over the following lines in the second code block of this post:
if (isVeryClose(startTime, endTime) && isVeryClose(startTime, 0.0)) return true; // If both zero, assume full 24 hours.
It just didn’t look right to me for some reason and I couldn’t see why you wouldn’t have just tested to see if both were zero instead:
if (isVeryClose(startTime, 0.0) && isVeryClose(endTime, 0.0)) return true; // If both zero, assume full 24 hours.
Then it hit me that if startTime was zero and endTime was any other value, then both tests would always be done. If you do the tests your way, the second is only ever done when the two values are the same. Which I imagine is a much rarer case.
If I’m right, and now I can’t see any other reason for you doing that way, then you’re one sick dude going to that detail…
Dr. Andrew Marsh 23 March, 2013 - 20:02
Basically that’s pretty right.
In this case the code is from my
PTimeAndDayRange class in the performative design analysis library. I’m not saying that I’m not sick, but it’s an obvious optimisation as I could imagine a lot of people choosing 0-24 as the range to test in the API. Thus, by testing first if the two are equal, you cut down significantly the chances of needing to do the subsequent test. A trivial saving, but why not if the code still works…
Click here to comment on this page.