Tuesday, June 29, 2004

A Perilous Lack of Vision

Warren Buffett has made passing reference to the unenviable position mutual-fund managers find themselves in. They are constrained by the need to show short-term (often quarterly) results, which often means that they must prematurely exit from investments that, for various reasons, have taken a turn for the worse. Conversely, they must often invest in what are clearly short-term upswings in order to make up the numbers. This results in a kind of flocking behaviour ("follow the leader"), which leads to the observed erratic, manic-depressive behaviour in the stock market that has little basis in economic reality.

This type of flocking is not limited to investors, of course. Fundamentally, it represents a kind of risk-aversion that is endemic in modern capitalist systems. This is the type of thinking that goes, "I may not achieve the best results in the long term, but at least I'll never suffer a major setback in the short term. Or at any rate, I'll be no worse off than my competitors". There is nothing wrong in principle with an avoidance of risk: I myself am one of the most risk-averse people I know. However, it becomes a problem when it begins to strangle growth and the achievement of one's potential.

The Operational Efficiency Chimera


The operational efficiency of a business can be measured by dividing its overhead costs by its gross profit. The ratio can be improved either by increasing gross profitability, or by reducing overhead costs (usually by means of retrenchments -- although executives can occasionally be convinced to give up the corporate jet, too). Since increasing profitability is usually extremely difficult, it's always tempting to focus exclusively on eliminating overhead. In moderation, this is a good exercise; but cut too deeply into the fat, and you start to hit the muscle and the bone. An amusing if not entirely valid extension to this analogy is that the fat often underlies the muscle.

It is logical then that good managers should also be asking themselves: what can I do to increase gross profitability? The answer to this varies from industry to industry. In the 'commodity' industries (steel, agriculture, general manufacturing), the answer is unfortunately "not much". Improved processes are about the only thing that will do the trick. The conclusion we should draw is that these industries should invest heavily in R&D, which can be viewed as a mini-industry in itself. R&D is more like a service industry.

Services are Different


In service industries, we run into an accounting convention that I believe is detrimental to efficient management. The problem is that service industries rely almost entirely on human capital to produce the goods. However, the cost of salaries is still recorded as an overhead cost. This makes accounting sense, but it can lead to bad decisions when managers try to treat services the same way they treat manufacturing. Basically, overhead is seen as "bad", and since services typically operate at low or even zero gross marginal cost, there doesn't seem to be much improvement to be made at that end of the system. Thus managers are compelled to cut overhead, even though this 'overhead' should properly be treated as a direct cost of production!

This brings me to the crux of my argument. The manufacturing model of accounting is inappropriate for service-type industries. The salary costs of those actually performing the work are in fact a direct cost of sales. However, the problem lies not in the accounting but in the way the figures are used to make decisions: humans providing services are assets, not expenses, and they should be treated as such.

The Human Asset


The remarkable thing about these human assets is that they do not depreciate. In fact, handled correctly, your people actually increase in value. Yet we do not see nearly enough investment in human assets -- certainly not below executive level. The trend I have noticed is that positions are hired rather than people. The candidate should be able to do X, Y and Z, and that is the end of that. This is a huge mistake. A person hired to do a job that involves thinking is an investment. You do not want such a person simply to perform mandated functions: you want them to contribute to the value of your business.

This argument is eminently reasonable, and I have never heard anyone disagree with it. Yet there is a reason why this theoretical ideal is not reflected in practice. You guessed it: risk-aversion. The problem with competent people is that you come to rely upon them. This is a risk (one might reason). What if the person leaves? What if I have to compensate him in an extraordinary fashion for his extraordinary service? Far better, surely, to rely only upon fungible minimum-standard employees, and not put my business at risk?

This line of thinking is, if you'll forgive me, rubbish. It's completely wrong-headed. Yes, competent employees are a risk. But that's what business is all about! You take a risk by investing, and you reap the rewards. If you don't invest, you'll never see a return. Imagine what we would say if a steel mill owner decided only to purchase the cheapest furnaces, on the grounds that the premier models are expensive to run and expensive to rebuild if they crack. False economy? You bet.

In Conclusion...


So what am I saying? In service industries (and, importantly, service divisions of companies in other industries), people form the bulk of the assets that are missing from the balance sheet. The money that is paid to them should be viewed as a cost of production rather than overhead cost. As in any other business, the job of the managers of a service company is to select and maintain the appropriate assets for the work in question. That means finding skilled people, and nurturing them. The more competent the better! It is absolutely a false economy to try to minimize the necessary skill levels. Trying to create fungible employees is actually counter-productive. When you attempt to treat skilled employees as interchangeable units, you are harming your business.

There is vast untapped productivity in the human asset. Ability should be allowed to flourish rather than supressed into conformity. Managing a services company is extremely difficult, because it's a lot harder to measure the performance of people than it is to make similar measurements of machines. It takes a different kind of manager, certainly, and a different kind of approach.

Unfortunately, it's taking a long time for us to become aware of the fact that providing services is not like producing steel. The attempts to fit services into the old manufacturing patterns represent a perilous lack of vision. Too much attention is paid to things that are irrelevant (fixed assets), and too little is paid to things that most certainly are relevant (human capital). This is an instance of experience letting us down: we're trying to apply a pattern that we're familiar with to a new subject, and it doesn't fit. That is why service industries, and software in particular, are so inefficient and risky. We're squeezing them in the wrong places.

What if we were to start listing our employees on the balance sheet as assets? What if their salaries were written up as costs of sales?

If you're in services, do as Apple says, and "think different".

Monday, June 21, 2004

Garbage Collection, RAII, and 'using'

If you fall exactly within the median range of computer programmers, then you've probably heard that "all modern languages use garbage collection", and you've taken to the idea with glee: you like the idea of having your garbage collected for you. However, you're probably also a little concerned that perhaps garbage collection is just a teensy bit less efficient than 'manual' memory management, although you'd never admit it in public (in case someone started forcing you to use malloc() and free() again).

On the other hand, you've probably not heard of RAII. If you have, you may know that it stands for "resource acquisition is initialization", although you're still not quite clear what that means. If it means anything.

If you do know about RAII, then you were probably extremely wary of the C# programming language - that is, until you discovered the 'using' statement. It's all good now, right?

To enlighten those who are at this point saying, "huh?", and to avoid (I hope!) a number of straw-man arguments, here's a brief summary of each of the terms in the title.

Garbage collection


Garbage collection is often referred to as "automatic memory management", although this can be a little misleading, as we'll see. The purpose of a garbage collector is to 'collect' blocks of memory that won't be used again and recycle them. The trick, of course, is knowing which blocks won't be used. There are several strategies for doing this, which I won't go into here. Generally, however, what happens is that allocated memory tends to stay allocated for a little longer than it otherwise would. This can lead to several efficiencies in the memory allocation scheme: most particularly, the malloc() function (or its equivalent) can be simplified enormously, as it no longer has to consider fragmentation problems. Generally speaking, this means that garbage collectors are more efficient for programs that allocate many small chunks of memory. Of course, the downside is that at any given moment, more memory is in use than is actually necessary - but then we're no longer trying to squeeze code, data and scratch space into 16k, are we?

Any actions carried out by an object at the point at which the garbage collector releases the memory allocated to it I will refer to as "finalization".

RAII


RAII is an idiom that has been floating around in C++ circles for some time now. The essential idea is that a resource (memory, file handle, database connection...) is acquired when an object is initialized (i.e. in the object's constructor). Conversely, the resource is released when the object is destroyed. The point of destruction is the interesting bit, and we'll have more on that later. However, suffice it to say that in C++, 'auto' or stack-allocated objects are destroyed as soon as they go "out of scope", meaning the variable that was bound to them when they were created is no longer accessible.

Generally when C++ programmers talk about RAII they're talking about wrapping up a resource in an object in such a way as to make certain guarantees about the way in which the resource will be used. In particular, they are used for exception safety: C++ does not have a 'finally' block. Bjarne Stroustrup explains why here.

There is an interesting twist to this story. It's possible to implement garbage collection using the RAII idiom (amongst other things). In fact, if you look here, you'll find an example of just such a thing. More commonly C++ programmers use a simpler reference-counting implementation often referred to as a "smart pointer". You can find a good one here. The good thing about a reference-counted implementation is that you can accurately predict when the object in question will be destroyed. What about cycles, you say? Consider weak references. I've found that they clarify my thinking on a number of issues revolving around ownership, lifecycles, and authority.

You can find out more about RAII here.

The 'using' statement


Here is where things get interesting. In C#, as in Java, the garbage collector makes no guarantees about when an object will be collected. C# supports 'destructors', but they're more accurately referred to as finalizers, since they are associated with the release of the memory rather than with the end of an object's useful lifecycle. This means that you can't use C# 'destructors' to manage resources as you can in C++. However, the C# team were aware of this, and they were also aware that Java-like try/finally constructs were bad: they involve a lot of typing, they make the code less readable, they detract from the purpose or intent of the code, and (and this is a big and) it's very easy for programmers -- even experienced ones -- to forget to use them.

The C# solution was to introduce a new construct: the using statement. This is essentially just shorthand for try/finally. When the scope of the using statement ends, the IDispose interface of the object that was 'used' is automatically called. This approach has two weaknesses. The first is minor: it limits the lifetime of the disposable object to the scope of a single statement. This is often not what we want. More serious, however, is the second objection: it still doesn't solve the problem of end-user forgetfulness. We're all human. We forget. We don't read the documentation. We often don't realise that we need to do anything other than just use the object we want to use. There's no safety net.

Some inferences


Here is a list of some of the things we can conclude from the preceding paragraphs:

  1. Garbage collection frees the programmer from having to use 'free'.

  2. Garbage collection is often more efficient than malloc/free.

  3. Garbage collection defers the point at which the memory is released, often indefinitely.

  4. What happens when the memory is released is called finalization.

  5. RAII relies on the timely destruction of objects to manage resources.

  6. RAII leads to easy-to-read ("intentional") code, with no need for try/finally or using statements.

  7. RAII makes exception safety very much easier.

  8. The using statement is really just try/finally in disguise.

  9. The end-user still needs to be told when to use 'using'.



Now we can say something interesting about the relationships between these concepts. Garbage collection (GC) solves the problem of memory management, whereas RAII solves the more general problem of resource management. GC uses the concept of "finalization"; RAII uses "destruction". "Using" is an attempt to bring the elegance and power of RAII into a GC context, but it's not much better than try/finally.

The problem, finally


Ideally, we would like to use both GC and RAII. Why can't we? Well, mostly because we can't be sure when a GC finalizer will run (if at all). This rules out the use of RAII in contexts where it's most useful -- mutex locks, for example. But why should we assume that the concepts of finalization and destruction are necessarily co-dependent? Destruction is the controlled, predictable end-of-life of an object. Finalization is the unpredictable point of memory-release. Surely these concepts are orthogonal?

Let's run with this a little further. We want controlled destruction, but we want to avoid getting the end-user involved in the details of when this should happen. One way of achieving these ends (in a language like Java or Python where [almost] all variables are by-reference) is through reference-counting. Reference-counted objects are almost garbage collected (except when they're involved in cyclical reference graphs), and they allow deterministic destruction. Since RAII objects tend not to refer to anything except the resource they represent, the problem of cycles is not really a significant one. Were objects with destructors implicitly subject to reference counting, and the destructor called when the reference count dropped to zero (the memory could be freed and the finalizer called at some later point), we'd have achieved the end of having both garbage collection and RAII in a single box.

The great thing about this solution is that it allows the programmers who design libraries to ensure that their objects won't be abused [too much]. The end-user doesn't have to remember that this particular object should have Dispose() or Close() (or whatever it is) called -- he just writes what comes naturally:

string[] getConfig() {

File f = new File("config.txt");
return f.readLines();
}


...and all is taken care of.

There are some wonderful things that can be done once this technique is admitted; there are also some gritty technical problems that have to be overcome. But that's a discussion for later.