A note on efficiency: OO is not inefficient

Signal processing software applications have traditionally been developed under the structured programming paradigm and more precisely in C language. In many DSP applications, specially in embedded systems where the final code is in assembly language, the program efficiency both in execution speed and size is one of the most important factors. It is still not strange finding that some algorithms are implemented directly in assembler. All the benefits attributed to object-orientation are many times put aside with the intent of building something ``fast'' and ``light''.

In this context, it is of course difficult to convince signal processing engineers that object-oriented (and in fact any sort of code structuring that focuses on reusability and understandability) is worthwhile. In the course of the CLAM framework development (see chapter 3) we have had the opportunity to convince many different people that well-structured object-oriented code is not inefficient and can indeed yield more efficient and far more robust applications (see [de Champeaux et al., 1993], for instance).

At this point though the choice of an appropriate object-oriented programming language is very important. Typically languages such as Java, running on a virtual machine, cannot yield efficient code. It is also important to be able to manage low-level issues like memory allocation policies whenever necessary. The choice of C++ as the most appropriate object-oriented language comes naturally, even more when C++ is already a de facto standard for DSP applications and a natural follow-up of the C programming language. It may be argued that C++ is not a ``true'' object-oriented language, even its creator advertises it as a multiparadigm language [Stroustrup, 1995]. Nevertheless this language can be effectively be used to build a completely object-oriented framework, abandoning the paradigm only for strictly necessary low-level issues such as hardware access or memory handling but hiding these details in such a way that the user does not even need to be aware they exist.

But the choice of an object-oriented language does not guarantee the quality of the resulting code. Many signal processing applications are in fact written in C++ in its ``a better C'' meaning, forgetting about all the advantages and tools offered by the object-oriented paradigm. A non-exhaustive list of common FUDs about object oriented efficiency, particularized to the case of the C++ language, are summarized in the following1.2:

  1. Encapsulation is inefficient: Some signal processing developers argue that the indirection introduced by adding a Set or Get operation to an attribute yields a less efficient executable. Because of this they consciously break the encapsulation principle by making all attributes public. The disadvantages of doing so are as important as the mixing-up of state and behavior, the existence of a non-homogeneous interface, or the lack of an implementation that can evolve independently from its interface. On the other hand it is definitely not true that the introduction of an operation call introduces inefficiency. In C++ an operation can be ``inlined'' so the call of an operation does not introduce a memory indirection. Although this is only feasible in methods with very little computation time and space requirements, this is exactly what we face when implementing a Getter or a Setter.
  2. Modularity is inefficient: Clearly a memory indirection introduces some overhead that at the end may result in a less efficient final application. But, as already mentioned, this is only so whenever inlining is not feasible. And inlining is not possible when the operation executed in the method is so complex that the time of its execution is several orders of magnitude greater than the time taken for the memory indirection. In this case it is also clear that the overhead of the indirection can be neglected and is by far surpassed by the benefits introduced, which will be later commented.
  3. Inheritance is inefficient: When declaring a base class with a virtual operation (needed in any case in order to implement polymorphism) all objects instance of any of its subclasses will have a virtual function pointer table. This produces memory inefficiency as objects will occupy more memory than the strictly necessary for storing their attributes. Furthermore, whenever a virtual operation is invoked on a pointer or reference, dynamic binding is introduced and therefore a new indirection that will add inefficiency. Also, virtual operations cannot be inlined. The solution is to treat inheritance with care and not introduce virtuality on any method that could be inlined (i.e. the cost of an indirection is comparable to the cost of the method itself). On the other hand, inheritance, just as modularity, introduces far more benefits than inconveniences.
Although these previous and other related misbelieves can, as already commented, be minimized the main benefit of object-orientation can be better understood in the mid-term. When building a well-structured system we are not only worrying about short-term efficiency issues as the ones commented but we are also setting the grounds for further refactorings in order to improve overall efficiency. In a well-structured system it is much easier to detect efficiency bugs and treat them in a correct way. The clearer, more modular and well-structured the code is the easier it is to optimize.

We will better understand all these issues in the following example.