er routines are harder to understand, debug, and maintain.
Even if the new specialized code is no faster than the original, there is still hope. Profiling of the new code may find hotspots within it that can be improved by code rewriting. You may also find that not all specializations are faster; since the generalized code is still available, you can revert to using it for the slower specializations. Each specialization is independent, so you can simply revert them one by one to see which ones improve runtime.
You may also find that some domain knowledge is helpful, especially if you are not the original author. Subtle relationships between the parameters can allow more simplification. Looking at the code by itself may not tell you about these relationships. Try to find another developer or a user who can give you guidance about the specializations you have identified.
Last but not least, once you have removed several special cases from the generalized code, you may find that it can be simplified as well. Do this cautiously, particularly if you have not yet seen any overall runtime improvement. Save a copy of the original code (you are using a version control system, right?) so you can go back to it. If you have been following my suggestions, up until now you have always had the original implementation available, but after you simplify that, it is hard to bring code back in.
You may find that removing some of the code from the general-purpose routine gives you a set of sleek routines that each do one thing well, rather than a single sluggish "kitchen sink" routine that tries to do everything. This can be an improvement in code quality in addition to runtime.
Downsides of Optimization by Pattern Matching
There are downsides to any optimization strategy, and pattern matching techniques increase the amount of code to write, debug and maintain. In particular, copying the master routine and specializing it violates the rule that you should do something in only one place. Make sure you document the original source of the specializations and note in the general-purpose routine that code has been copied. Otherwise, you may find that you have copied a bug to multiple places, but fixed it only once.
Inline functions may help here. If you can put some or all of the copied code into inline functions, you can fix any bugs once and have them propagate to all of the specializations automatically without any function call overhead.
Complex code is harder to test as well. It is crucial that you specialize code only after it is working properly, to ensure that the new faster code gets the right results. Use white box directed tests to exercise each specialized routine, or at the very least compare the final outputs vs. an application-level golden output.
Not all programs can be improved by pattern matching and specialization. You may find that the code required to detect common patterns is slower (or, for memory consumption, uses no less memory) than the code it means to replace, or that the specialized versions of routines are no simpler than the original. In that case, optimization by code rewriting or data caching may be more appropriate.
Many problem domains, however, have highly variable inputs with unique processing requirements, and these often benefit greatly from pattern matching. It's a valuable skill for your programming toolbox.