Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

To explain what I mean, let me start with an example.

Consider a deque that supports O(logn) concatenation with another deque and O(n) addition of n elements at one end. This dequeimplements a general seq interface (or type-class, or what have you) that allows iterating over a collection.

An explicit optimization approach would be, having a concat method (or function) for deque objects and a separate pushSeq method for seq objects. Each method would be documented with the appropriate complexity.

An implicit optimization approach would be to have a single concat method that accepts a seq. An internal dynamic type test checks whether the supplied argument is actually a deque, and if so, calls an implementation method for concating deques. This is documented in the API.

Obviously you could have both of these. The point of implicit optimization is that you don't give the user explicit control over optimization. It just "happens", unless the user deliberately looks for it.

Right now I'm writing a library and I'm facing a very similar choice. I very much like the idea of a compact interface where things just "work". An implicit approach also gives me a lot more freedom. For example, maybe I can perform ten dynamic type tests to optimize the concat operation for different collections. Having ten different methods wouldn't make sense.

What's your take on this? Which approach is better, and why?

share|improve this question
    
often times the optimization parameters are misused (as in guessing what they should be instead of testing and benchmarking and adjusting) –  ratchet freak Apr 15 '13 at 23:36

3 Answers 3

I don't optimize anything until I have identified a performance problem, measured and profiled the software and know where the problem is. I want your library to "just work". As it's a library, I expect it to be reasonably efficient most of the time for most use cases, but also do not expect it to be optimal for every case. If I find it is inefficient for a simple or typical use case, to kick off a performance concern, I will not be impressed - especially if it is trivial to fix - you, as the library vendor, should have (as you are doing now) thought of that.

Give me one method that works reasonably well most of the time, and if you feel the need, give some "advanced" API's that I can use when, and only if, needed. Make sure the advanced API's are clearly documented as such. e.g. "Use this API to optimize the speed/memory use/whatever...."

share|improve this answer
    
this kind of depends what the library is for in the first place –  jk. Apr 16 '13 at 7:47
    
+ Library routines should not be sluggards, but their main purpose is ease of use, correctness, and reliability. I discovered using LAPACK that it was actually not very efficient in the case of small matrices, but it still serves its purpose. –  Mike Dunlavey Apr 16 '13 at 12:26

Having a single method that can choose if/when to follow a pre-defined optimization is a better route to take. Just some advantages off the top of my head:

  • Easier to write unit tests
  • Will "Just Work" for anybody using it
  • Can update/change the code handling that section without requiring anyone relying on the code to change anything
  • Can add further optimizations or new types without affecting anything else
  • Less likely people will use it "wrong" and come complaining to you
  • Easier to learn how to use your library
  • Leads to better code reuse. What if an optimization actually works for 3 types, not just 1? You don't need to rewrite much, if anything, and the users don't need to worry about which one to use

All that said, and as has been said above, don't do too much premature optimization. Wait until you know there's a problem, which unit testing and getting users will help you find.

share|improve this answer

First off, I'm going to assume that you've verified that optimisation is actually needed. Much has been said on the perils of premature optimisation, so I'll just say that if you haven't already, then check that this code actually needs optimising (ie. it's responsible for a significant portion of the program's execution time, or, for libraries, it's significantly slower than the things it's going to be used alongside).

Having made sure that this optimisation is necessary, there are a few issues to consider. The first thing is to consider who will use it:

  • Some users don't care about optimisation. Having multiple methods provides them with multiple options in a place where one option would be fine. You're making them think more than they might otherwise have to, which is to be avoided - a programmer's brain is their most important resource; you should not eat into it lightly.
  • Some users may be less skilled or less experienced - they might not know about optimisation, or might not understand the difference between the methods. Presenting them with several options will confuse them, and they might use the wrong one, or just give up and go find another library.

To generalise the previous two points, if it's up to the user to optimise, then there will be some users who don't. This means you're limiting the performance of your library in a lot of its applications. If you can make your code faster, you'll probably want to make it faster for everyone who uses it.

It's also worth considering how the code will be used:

  • Your different methods might end up having different names, depending on your language's restrictions on method naming and argument types. This means that users have to remember which method goes with which type. This is more complicated than just knowing that collections have a concat method.
  • Hiding the optimisations away makes your library more useable - the user doesn't need to make any type checks themselves, or use different methods in different places. They can forget about performance and focus on functionality, using concat everywhere and knowing that the optimisations will be handled for them.

In general, I'd definitely advise making the optimisations implicit, tucked "under the hood". In most cases, a user should be required to understand as little as possible about the internal workings of your code in order to use it. They'll need to know what it does, but the details of how it does it shouldn't affect them.

The main argument I'd make in favour of letting the user access the internal methods is that they may be able to select the correct method themselves without needing the type checks you're making, simply by knowing from the structure of the logic what type the object will be at that point. If you want to leave this possibility open, then one approach I've seen work quite well is to have a layered namespace structure. This depends on what your language allows, and the nature of the methods in question (might not work with your situation) but in general, it works something like this:

  • A standard namespace, MyLibrary, which contains all the top-level functions with their checks and implicit optimisations. The majority of your users will just use this, and leave your code to optimise things.
  • A deeper namespace, MyLibrary.Core or something like that, which exposes some more of the internal methods. The standard namespace is mostly a wrapper to hide this deeper namespace from users who are happy to let your code handle the optimisations. Users who really want to squeeze a little extra performance can use this namespace to give them access to the internal methods, so they can handle the optimisation themselves.
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.