Java Templates – Is Template Metaprogramming a Good Idea?

javamaintainabilityperformancetemplates

There is a source file in a rather large project with several functions that are extremely performance-sensitive (called millions of times per second). In fact, the previous maintainer decided to write 12 copies of a function each differing very slightly, in order to save the time that would be spent checking the conditionals in a single function.

Unfortunately, this means the code is a PITA to maintain. I would like to remove all the duplicate code and write just one template. However, the language, Java, does not support templates, and I'm not sure that generics are suitable for this.

My current plan is to write instead a file that generates the 12 copies of the function (a one-use-only template expander, practically). I would of course provide copious explanation for why the file must be generated programmatically.

My concern is that this would lead to future maintainers' confusion, and perhaps introduce nasty bugs if they forget to regenerate the file after modifying it, or (even worse) if they modify instead the programmatically-generated file. Unfortunately, short of rewriting the whole thing in C++, I see no way to fix this.

Do the benefits of this approach outweigh the disadvantages? Should I instead:

  • Take the performance hit and use a single, maintainable function.
  • Add explanations for why the function must be duplicated 12 times, and graciously take the maintenance burden.
  • Attempt to use generics as templates (they probably don't work that way).
  • Yell at the old maintainer for making code so performance-dependent on a single function.
  • Other method to maintain performance and maintainability?

P.S. Due to the poor design of the project, profiling the function is rather tricky… however, the former maintainer has convinced me that the performance hit is unacceptable. I assume by this he means more than 5%, though that is a complete guess on my part.


Perhaps I should elaborate a bit. The 12 copies do a very similar task, but have minute differences. The differences are in various places throughout the function, so unfortunately there are many, many, conditional statements. There are effectively 6 "modes" of operation, and 2 "paradigms" of operation (words made up by myself). To use the function, one specifies the "mode" and "paradigm" of operation. This is never dynamic; each piece of code uses exactly one mode and paradigm. All 12 mode-paradigm pairs are used somewhere in the application. The functions are aptly named func1 to func12, with even numbers representing the second paradigm and odd numbers representing the first paradigm.

I'm aware that this is just about the worst design ever if maintainability is the goal. But it seems to be "fast enough", and this code hasn't needed any changes for a while… It's also worth noting that the original function has not been deleted (although it is dead code as far as I can tell), so refactoring would be simple.

Best Answer

This is a very bad situation, you need to refactor this ASAP - this is technical debt in it's worst - you don't even know how important the code really is - only speculate that it's important.

As to solutions ASAP:
Something that can be done is adding a custom compilation step. If you use Maven that is actually fairly simple to do, other automated build systems are likely to cope with this as well. Write a file with a different extension than .java and add a custom step that searches your source for files like that and regenerates the actual .java. You may also want to add a huge disclaimer on the auto-generated file explaining not to modify it.

Pros vs using a once-generated file: Your developers will not get their changes to the .java working. If they actually run the code on their machine before committing they will find that their changes have no effect (hah). And then maybe they will read the disclaimer. You are absolutely right in not trusting your teammates and your future self with remembering that this particular file has to be changed in a different way. It also allows automatic testing as wel, as JUnit will compile your program before running tests (and regenerate the file as well)

EDIT

Judging by the comments the answer came off as if this is a way to make this work indefinitely and maybe OK to deploy to other performance critical parts of your project.

Simply put: it is not.

The extra burden of creating your own mini-language, writing a code-generator for it and maintaining it, not to mention teaching it to future maintainers is hellish in the long run. The above only allows a safer way to handle the problem while you are working on a long-term solution. What that will take is above me.

Related Topic