views:

477

answers:

14

A two parter:

1) Say you're designing a new type of application and you're in the process of coming up with new algorithms to express the concepts and content -- does it make sense to attempt to actively not consider optimisation techniques at that stage, even if in the back of your mind you fear it might end up as O(N!) over millions of elements?

2) If so, say to avoid limiting cool functionality which you might be able to optimise once the proof-of-concept is running -- how do you stop yourself from this programmers habit of a lifetime? I've been trying mental exercises, paper notes, but I grew up essentially counting clock cycles in assembler and I continually find myself vetoing potential solutions for being too wasteful before fully considering the functional value.

Edit: This is about designing something which hasn't been done before (the unknown), when you're not even sure if it can be done in theory, never mind with unlimited computing power at hand. So answers along the line of "of course you have to optimise before you have a prototype because it's an established computing principle," aren't particularly useful.

+5  A: 

My big answer is Test Driven Development. By writing all your tests up front then you force yourself to only write enough code to implement the behavior you are looking for. If timing and clock cycles becomes a requirement then you can write tests to cover that scenario and then refactor your code to meet those requirements.

Josh
This is a design issue -- test driven development is only useful when you have a design to work from.
Richard Franks
TDD is all about design: You can implement several designs with the same test, to make sure that your designs fulfill your needs. You could write tests such that if your design fails, your tests fail too.
Jon Limjap
It's rather like trying to pull the design into existence out of it's own ass - it'll either pop into reality whole and functioning, or not at all. Each component is trivial to test individually - but each component is only useful if it interacts perfectly with a dozen or so others. TDD won't work.
Richard Franks
+1  A: 

"actively not consider optimisation" sounds really weird to me. Usually 80/20 rule works quite good. If you spend 80% of your time to optimize program for less than 20% of use cases, it might be better to not waste time unless those 20% of use-cases really matter.

As for perfectionism, there is nothing wrong with it unless it starts to slow you down and makes you miss time-frames. Art of computer programming is an act of balancing between beauty and functionality of your applications. To help yourself consider learning time-management. When you learn how to split and measure your work, it would be easy to decide whether to optimize it right now, or create working version.

aku
+2  A: 

Optimization isn't exactly a danger; its good to think about speed to some extent when writing code, because it stops you from implementing slow and messy solutions when something simpler and faster would do. It also gives you a check in your mind on whether something is going to be practical or not.

The worst thing that can happen is you design a large program explicitly ignoring optimization, only to go back and find that your entire design is completely useless because it cannot be optimized without completely rewriting it. This never happens if you consider everything when writing it--and part of that "everything" is potential performance issues.

"Premature optimization is the root of all evil" is the root of all evil. I've seen projects crippled by overuse of this concept. At my company we have a software program that broadcasts transport streams from disk on the network. It was originally created for testing purposes (so we would just need a few streams at once), but it was always in the program's spec requirements that it work for larger numbers of streams so it could later be used for video on demand.

Because it was written completely ignoring speed, it was a mess; it had tons of memcpys despite the fact that they should never be necessary, its TS processing code was absurdly slow (it actually parsed every single TS packet multiple times), and so forth. It handled a mere 40 streams at a time instead of the thousands it was supposed to, and when it actually came time to use it for VOD, we had to go back and spend a huge amount of time cleaning it up and rewriting large parts of it.

Dark Shikari
The key word is premature. It sounds like performance was a key criteria for the application, and it was ignored long past when it was appropriate to consider it.
dvorak
Richard Franks
+2  A: 

Like security and usability, performance is something that has to be considered from the beginning of the project. As such, you should definitely be designing with good performance in mind.

1800 INFORMATION
Even when trying something which hasn't been done before, and you're not even sure if it can be done in theory, with unlimited computing power?
Richard Franks
Then in that case it would be perfectly fine not to take any account of good programming principles and completely throw the baby out with the bathwater.
1800 INFORMATION
</sarcasm>
1800 INFORMATION
I think that your sarcasm is somewhat misplaced
Richard Franks
You'll get over it
1800 INFORMATION
Didn't take long. I'm just not sure why you bothered to answer my question when you evidently had no intention of trying to understand the problem at hand.
Richard Franks
I always give answers with care and thought. I am also somewhat opinionated. Seriously though if you aren't even sure if the thing can be done at all, why would you be writing code? Surely your time would be better spent in design and analysis.
1800 INFORMATION
It is a design-centric question - asking for techniques to avoid premature optimisation.
Richard Franks
+2  A: 

The old Knuth line is "We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil." O(N!) to O(poly(N)) is not a "small efficiency"!

The best way to handle type 1 is to start with the simplest thing that could possibly work (O(N!) cannot possibly work unless you're not scaling past a couple dozen elements!) and encapsulate it from the rest of the application so you could rewrite it to a better approach assuming that there is going to be a performance issue.

Captain Segfault
N! for N=24 ("a couple dozen") = 620448401733239439360000. Unless you were thinking of a baker's dozen? :-)
Svein Bringsli
The constant factor could be very small.
Captain Segfault
It would have to be tiny indeed :-)
Svein Bringsli
To be clear, "a couple dozen" is supposed to be a loose upper bound -- although there is technically nothing keeping an O(N!) algorithm from scaling much further, even ignoring constant factors; it might not be possible to construct a "hard" input with N=24.
Captain Segfault
A: 

If I'm concerned about the codes ability to handle data growth, before I get too far along I try to set up sample data sets in large chunk increments to test it with like:

1000 records 10000 records 100000 records 1000000 records

and see where it breaks or becomes un-usable. Then you can decide based on real data if you need to optimize or re-design the core algorithms.

Ron

Ron Savage
I think this is the key element op is looking for. I just recently got to the point of being able to write a test-harness for my primary data structure, you can just about forget all the vote-up/vote-down and rather well informed advice. I found that suddenly, somewhere just past 40,000 new in a loop - the entire jvm choked down suddenly in a manner that will get you downvoted if you try to tell people that. I do not think we have an O( ? ) for fails in unpredictable ways.
Nicholas Jordan
+1  A: 

I think it is quite reasonable to forget about O(N!) worst case for an algorithm. First you need to determine that a given process is possible at all. Keep in mind that Moore's law is still in effect, so even bad algorithms will take less time in 10 or 20 years!

First optimize for Design -- e.g. get it to work first :-) Then optimize for performance. This is the kind of tradeoff python programmers do inherently. By programming in a language that is typically slower at run-time, but is higher level (e.g. compared to C/C++) and thus faster to develop, python programmers are able to accomplish quite a bit. Then they focus on optimization.

One caveat, if the time it takes to finish is so long that you can't determine if your algorithm is right, then it is a very good time to worry about optimization earlier up stream. I've encountered this scenario only a few times -- but good to be aware of it.

torial
A: 

I will give a little story about something that happened to me, but not really an answer.

I am developing a project for a client where one part of it is processing very large scans (images) on the server. When i wrote it i was looking for functionality, but i thought of several ways to optimize the code so it was faster and used less memory.

Now an issue has arisen. During Demos to potential clients for this software and beta testing, on the demo unit (self contained laptop) it fails due to too much memory being used. It also fails on the dev server with really large files.

So was it an optimization, or was it a known future bug. Do i fix it or oprtimize it now? well, that is to be determined as their are other priorities as well.

It just makes me wish I did spend the time to reoptimize the code earlier on.

mattlant
A: 

Think about the operational scenarios. ( use cases)

Say that we're making a pizza-shop finder gizmo.

The user turns on the machine. It has to show him the nearest Pizza shop in meaningful time. It Turns out our users want to know fast: in under 15 seconds.

So now, any idea you have, you think: is this going to ever, realistically run in some time less than 15 seconds, less all other time spend doing important stuff..

Or you're a trading system: accurate sums. Less than a millisecond per trade if you can, please. (They'd probably accept 10ms), so , agian: you look at every idea from the relevant scenarios point of view.

Say it's a phone app: has to start in under (how many seconds)

Demonstrations to customers fomr laptops are ALWAYS a scenario. We've got to sell the product.

Maintenance, where some person upgrades the thing are ALWAYS a scenario.

So now, as an example: all the hard, AI heavy, lisp-customized approaches are not suitable. Or for different strokes, the XML server configuration file is not user friendly enough.

See how that helps.

Tim Williscroft
+5  A: 

I say all the following not because I think you don't already know it, but to provide moral support while you suppress your inner critic :-)

The key is to retain sanity.

If you find yourself writing a Theta(N!) algorithm which is expected to scale, then you're crazy. You'll have to throw it away, so you might as well start now finding a better algorithm that you might actually use.

If you find yourself worrying about whether a bit of Pentium code, that executes precisely once per user keypress, will take 10 cycles or 10K cycles, then you're crazy. The CPU is 95% idle. Give it ten thousand measly cycles. Raise an enhancement ticket if you must, but step slowly away from the assembler.

Once thing to decide is whether the project is "write a research prototype and then evolve it into a real product", or "write a research prototype". With obviously an expectation that if the research succeeds, there will be another related project down the line.

In the latter case (which from comments sounds like what you have), you can afford to write something that only works for N<=7 and even then causes brownouts from here to Cincinnati. That's still something you weren't sure you could do. Once you have a feel for the problem, then you'll have a better idea what the performance issues are.

What you're doing, is striking a balance between wasting time now (on considerations that your research proves irrelevant) with wasting time later (because you didn't consider something now that turns out to be important). The more risky your research is, the more you should be happy just to do something, and worry about what you've done later.

Steve Jessop
Ha! Thanks for the moral support :-) I wonder if it is a viable route - I don't hear too many success stories like "we started out by burning the book of good practice". Meta-practice wise, I'd suppose evolving an unoptimised prototype to be less successful than just keeping the data and recoding.
Richard Franks
Writing a non-working (in this case because too slow) prototype isn't burning the book of good practice. You do have to be confident you will be allowed to throw it away, unlike the test code Dark Shikari complained about that ended up needing unanticipated optimisation.
Steve Jessop
Oh yes, and I generally agree that evolving a prototype is a bad plan, but people do sometimes do it anyway. A good way to defend against the urge to do that is to prototype in a language incompatible with the real target!
Steve Jessop
I do not think that evolving a prototype is a bad plan, quite the opposite. You will however have to actually refactor stuff before it becomes too painful. People often skip the refactoring for convenience's sake, and regret it later...
sleske
+1  A: 

Following on from onebyone's answer there's a big difference between optimising the code and optimising the algorithm.

Yes, at this stage optimising the code is going to be of questionable benefit. You don't know where the real bottlenecks are, you don't know if there is going to be a speed problem in the first place.

But being mindful of scaling issues even at this stage of the development of your algorithm/data structures etc. is not only reasonable but I suspect essential. After all there's not going to be a lot of point continuing if your back-of-the-envelope analysis says that you won't be able to run your shiny new application once to completion before the heat death of the universe happens. ;-)

Andrew Edgecombe
I think I'm finding "being mindful of scaling issues" to be detrimental at this point due to the fact that I've gone back (in desperation) to older ideas I'd initially discarded due to seeming impracticality; found them to have merit, and in conjunction with another development, newly optimisable.
Richard Franks
That said, it's been 21 months so far of this process - and given that most things _are_ optimisable anyway - I'm almost ready to throw out all considerations of optimisation ;-) It doesn't _sound_ like a winning strategy though.
Richard Franks
+1  A: 

"First, make it run. Then make it run fast."

or

"To finish first, first you have to finish."

Slow existing app is usually better than ultra-fast non-existing app.

Vilmantas Baranauskas
+2  A: 

First of all peopleclaim that finishign is only thing that matters (or almost).

But if you finish a product that has O(N!) complexity on its main algorithm, as a rule of thumb you did not finished it! You have an incomplete and unacceptable product for 99% of the cases.

A reasonable performance is part of a working product. A perfect performance might not be. If you finish a text editor that needs 6 GB of memory to write a short note, then you have not finished a product at all, you have only a waste of time at your hands.. You must remember always that is not only delivering code that makes a product complete, is making it achieve capability of supplying the costumer/users needs. If you fail at that it matters nothing that you have finished the code writing in the schedule.

So all optimizations that avoid a resulting useless product are due to be considered and applied as soon as they do not compromise the rest of design and implementation proccess.

OldMan
I agree, but the point is how to know which optimisations will or won't compromise the rest? If it works at all, thrashing through 60GB of swap - that's a success. Imagine you're trying to create something as ambitious as "a program to make all your dreams come true", and you'll see my dilemma :-)
Richard Franks
+1  A: 

I like this question, so I'm giving an answer, even though others have already answered it.

When I was in grad school, in the MIT AI Lab, we faced this situation all the time, where we were trying to write programs to gain understanding into language, vision, learning, reasoning, etc.

My impression was that those who made progress were more interested in writing programs that would do something interesting than do something fast. In fact, time spent worrying about performance was basically subtracted from time spent conceiving interesting behavior.

Now I work on more prosaic stuff, but the same principle applies. If I get something working I can always make it work faster.

I would caution however that the way software engineering is now taught strongly encourages making mountains out of molehills. Rather than just getting it done, folks are taught to create a class hierarchy, with as many layers of abstraction as they can make, with services, interface specifications, plugins, and everything under the sun. They are not taught to use these things as sparingly as possible.

The result is monstrously overcomplicated software that is much harder to optimize because it is much more complicated to change.

I think the only way to avoid this is to get a lot of experience doing performance tuning and in that way come to recognize the design approaches that lead to this overcomplication. (Such as: an over-emphasis on classes and data structure.)

Here is an example of tuning an application that has been written in the way that is generally taught.

Mike Dunlavey