|Welcome to the Monastery|
I think performance optimization should not be the principle determining factor in deciding which technique to use. Unless your application is running unacceptably slowly and you've identified the multi-branch if/else section as a bottleneck, it's ridiculous to change that to something else just for a performance gain.
Maintainability, and design — as in consistency of — those are of primary importance.
I like dispatch tables. They're clever. But at the same time, they raise a red flag for me — the same one I see whenever hashes are used to store a static set of entities in an application: It completely undermines the safety of strict. (There are steps you can take to regain some of this safety, such as using Tie::StrictHash.)
The solution is to make the subs named class methods. I recently did this on a project at work.
I had two dispatch tables, one for the "real" functions and one for a set of stubs, to be used when debugging.
Now, it sometimes won't be convenient to do this, due to the complication of packages. In my case, it was; in fact, it was a significant improvement, since it gave me a convenient place to encapsulate all my "system functions", which hitherto had lived in the main namespace.
In the very simplest cases, you don't need to worry about packages at all:
Note: I only left the shift in as a reminder that it will always be necessary unless the sub takes no arguments (or, as I sometimes do, you pop the args rather than shifting them).
Finally, what about default cases? One possibility is to use AUTOLOAD.
We're building the house of the future together.