Monday, October 29, 2012

Polymorphism is not better than conditionals? Runtime overhead issue.

As I was taught, OOP is better than structural programming and my hands always start to itch when I see that there is a code that begs replacing conditional with polymorphism. I was surprised when discovered that the gurus of  design patterning do not see any polymorphism advantage over case statements! They say

It appears that polymorphism does NOT:
  • make code change-friendly (at least not in my domain)
  • reduce code size
  • fix OnceAndOnlyOnce
It is important to have small footprint for the objects that you may have in numbers.
  (parameter polymorphism: through method overriding or field configuration?)


OOP experts say that OOP is not better than structural programming! IMO, they are just not hackers and forget (or intentionally disregard) the performance issue. At first, switch-checking takes CPU cycles. This is understandable. But nobody of them also recalled the idea of OOP to combine the relevant code with data. Such data localization makes very good argument for performance improvement and debugging ease. That is, they say that procedural style demands as much coding as OOP:


Procedural:
Operation 1
 ---Subtype A
 ---Subtype B
 ---Subtype C
 Operation 2
 ---Subtype A
 ---Subtype B
 ---Subtype C
 Operation 3
 ---Subtype A
 ---Subtype B
 ---Subtype C
OOP:
Subtype A
 ---Operation 1
 ---Operation 2
 ---Operation 3
 Subtype B
 ---Operation 1
 ---Operation 2
 ---Operation 3
 Subtype C
 ---Operation 1
 ---Operation 2
 ---Operation 3


But, they omit the OOP fact that subtypeA needs some state variable, call it fieldA. SubtypeB needs another state fieldB. So, in reality, switching solution looks like


class MultiValue {
  int fInt;
  real fReal
  String fString
  char fChar; 
  Type fType;
  constructor (Type type, Builder builder) {
    fType = type;
    fInt = builder.fInt;
    fReal = builder.fReal;
    fString = builder.fString;
    fChar = builder.fChar;

 Operation1() {
    switch (fType)
      case INT: useInt1()
      case REAL: useReal1()
      case String: useString1()

If we translate into OOP,

class MultiValue {

  // no special field declaration
  abstract Operation1() {}
  abstract Operation2() {} 

subclass INT extends MultiValue {
  int fInt; // declare only one field we need
  INT(type, builder)  {
    assert type == INT;
    fInt = builder.getInt()
  } 
  operation1()  {
    useInt()
  }

 subclass REAL extends MultiValue {
   real fReal; // only one field is declared

 
The coding size is still the same but runtime does not allocate any unnecessary fields (in the memory)! Therefore, OOP applications have lower memory footprint. This not only saves memory (uses it more efficiently) but also better fits into cache and thus makes application faster. Better cache-hit rate further improves performance in multithreaded applications it unloads the modern system bottleneck - the main memory access bus.

Developer performance is also improved because his look is not cluttered with unused fields, when he inspects the values of object fields in debugger.

You may appreciate the performance improvement after conditional style into OOP improves performance in real application

> Preliminary optimization is root of all evils. You should replace conditionals with polymorphism not because latter has performance advantages but because you must prefer containment to polymorphism.

Ok, I see.

No comments: