views:

508

answers:

11

Its common to hear about "highly optimized code" or some developer needing to optimize theirs and whatnot. However, as a self-taught, new programmer I've never really understood what exactly do people mean when talking about such things.

Care to explain the general idea of it? Also, recommend some reading materials and really whatever you feel like saying on the matter. Feel free to rant and preach.

+1  A: 

Optimization means trying to improve computer programs for such things as speed. The question is very broad, because optimization can involve compilers improving programs for speed, or human beings doing the same.

Kinopiko
Hello there, John Wayne. How's Hollywood been treating you?
Chris Lutz
Get off your horse and drink your milk.
Kinopiko
+26  A: 

Optimize is a term we use lazily to mean "make something better in a certain way". We rarely "optimize" something - more, we just improve it until it meets our expectations.

Optimizations are changes we make in the hopes to optimize some part of the program. A fully optimized program usually means that the developer threw readability out the window and has recoded the algorithm in non-obvious ways to minimize "wall time". (It's not a requirement that "optimized code" be hard to read, it's just a trend.)

One can optimize for:

  • Memory consumption - Make a program or algorithm's runtime size smaller.

  • CPU consumption - Make the algorithm computationally less intensive.

  • Wall time - Do whatever it takes to make something faster

  • Readability - Instead of making your app better for the computer, you can make it easier for humans to read it.

Some common (and overly generalized) techniques to optimize code include:

  • Change the algorithm to improve performance characteristics. If you have an algorithm that takes O(n^2) time or space, try to replace that algorithm with one that takes O(n * log n).

  • To relieve memory consumption, go through the code and look for wasted memory. For example, if you have a string intensive app you can switch to using Substring References (where a reference contains a pointer to the string, plus indices to define its bounds) instead of allocating and copying memory from the original string.

  • To relieve CPU consumption, cache as many intermediate results if you can. For example, if you need to calculate the standard deviation of a set of data, save that single numerical result instead looping through the set each time you need to know the std dev.

Frank Krueger
Well thought out answer.
carl
O(n^2) means that the algorithm gets 4 times slower every time the size of the problem (n) doubles in size. Imagine a nested loop that compares every value to every other value. Every time we double the number of values, we quadruple the amount of work. - So making an algorithm o(n log n) means that you are trying to work smarter, not harder.
Tom Leys
+5  A: 

The general idea is that when you create your source tree in the compilation phase, before generating the code by parsing it, you do an additional step (optimization) where, based on certain heuristics, you collapse branches together, delete branches that aren't used or add extra nodes for temporary variables that are used multiple times.

Think of stuff like this piece of code:

a=(b+c)*3-(b+c)

which gets translated into

        -
     *     + 
    + 3   b c
   b c

To a parser it would be obvious that the + node with its 2 descendants are identical, so they would be merged into a temp variable, t, and the tree would be rewritten:

        -
     *     t 
    t 3

Now an even better parser would see that since t is an integer, the tree could be further simplified to:

        *
     t     2

and the intermediary code that you'd run your code generation step on would finally be

int t=b+c;
a=t*2;

with t marked as a register variable, which is exactly what would be written for assembly.

One final note: you can optimize for more than just run time speed. You can also optimize for memory consumption, which is the opposite. Where unrolling loops and creating temporary copies would help speed up your code, they would also use more memory, so it's a trade off on what your goal is.

Blindy
Check your math, good sir: 3x - 1x != 4x
Chris Lutz
I forgot half way through what sign i used.. fixed!
Blindy
Blindy, these are pretty tame optimisations compared to what a compiler can actually do, and nothing compared to what a smart programmer will do. Optimsations are usually talking about unrolling loops, inlining and simplifying functions and (by hand) improving cache usage.
Tom Leys
Blindly: You talk about this as if it's happening at parse time. If someone who's not already familiar with compiler construction reads this, it will cause great confusion. Parsing and optimization are wholly unrelated, except in some hypothetical oddball 2-phase compiler (make AST, generate code).
Novelocrat
@Blindy: these things are all true and, in my experience, only relevant in terminal-phase optimizing. What comes before that, especially in large software, is mountains of speedup done by eliminating mid-stack function/method calls that can be done without, and only the programmer can do that, not the compiler.
Mike Dunlavey
... for example: http://stackoverflow.com/questions/926266/performance-optimization-strategies-of-last-resort/927773#927773
Mike Dunlavey
+4  A: 

Here is an example of some optimization (fixing a poorly made decision) that I did recently. Its very basic, but I hope it illustrates that good gains can be made even from simple changes, and that 'optimization' isn't magic, its just about making the best decisions to accomplish the task at hand.

In an application I was working on there were several LinkedList data structures that were being used to hold various instances of foo.

When the application was in use it was very frequently checking to see if the LinkedListed contained object X. As the ammount of X's started to grow, I noticed that the application was performing more slowly than it should have been.

I ran an profiler, and realized that each 'myList.Contains(x)' call had O(N) because the list has to iterate through each item it contains until it reaches the end or finds a match. This was definitely not efficent.

So what did I do to optimize this code? I switched most of the LinkedList datastructures to HashSets, which can do a '.Contains(X)' call in O(1)- much better.

instanceofTom
Good example, and I think the important point is that you started simple and didn't switch to the HashSet until you knew it was necessary.
Mike Dunlavey
+8  A: 

I'll mostly rant with no practical advice.

Measure First. Optimization should be done to places where it matters. Highly optimized code is often difficult to maintain and a source of problems. In places where the code does not slow down execution anyway, I alwasy prefer maintainability to optimizations. Familiarize yourself with Profiling, both intrusive (instrumented) and non-intrusive (low overhead statistical). Learn to read a profiled stack, understand where the time inclusive/time exclusive is spent, why certain patterns show up and how to identify the trouble spots.

You can't fix what you cannot measure. Have your program report through some performance infrastructure the thing it does and the times it takes. I come from a Win32 background so I'm used to the Performance Counters and I'm extremely generous at sprinkling them all over my code. I even automatized the code to generate them.

And finally some words about optimizations. Most discussion about optimization I see focus on stuff any compiler will optimize for you for free. In my experience the greatest source of gains for 'highly optimized code' lies completely elsewhere: memory access. On modern architectures the CPU is idling most of the times, waiting for memory to be served into its pipelines. Between L1 and L2 cache misses, TLB misses, NUMA cross-node access and even GPF that must fetch the page from disk, the memory access pattern of a modern application is the single most important optimization one can make. I'm exaggerating slightly, of course there will be counter example work-loads that will not benefit memory access locality this techniques. But most application will. To be specific, what these techniques mean is simple: cluster your data in memory so that a single CPU can work an a tight memory range containing all it needs, no expensive referencing of memory outside your cache lines or your current page. In practice this can mean something as simple as accessing an array by rows rather than by columns.

I would recommend you read up the Alpha-Sort paper presented at the VLDB conference in 1995. This paper presented how cache sensitive algorithms designed specifically for modern CPU architectures can blow out of the water the old previous benchmarks:

We argue that modern architectures require algorithm designers to re-examine their use of the memory hierarchy. AlphaSort uses clustered data structures to get good cache locality...

Remus Rusanu
Well written answer, but doesn't help someone who asks _what_ optimization is.
Georg
There are several other answers that address this, and this is an important part of the whole issue.
Chris Lutz
+2  A: 
Danny Varod
I'm not sure the CLR is a book I'd recommend to someone who's just learning about algorithms.
Jason Baker
That's the book I was taught from as a student. Any other recommendations?
Danny Varod
+1  A: 

I am going with the idea that optimizing a code is to get the same results in less time. And fully optimized only means they ran out of ideas to make it faster. I throw large buckets of scorn on claims of "fully optimized" code! There's no such thing.

So you want to make your application/program/module run faster? First thing to do (as mentioned earlier) is measure also known as profiling. Do not guess where to optimize. You are not that smart and you will be wrong. My guesses are wrong all the time and large portions of my year are spent profiling and optimizing. So get the computer to do it for you. For PC VTune is a great profiler. I think VS2008 has a built in profiler, but I haven't looked into it. Otherwise measure functions and large pieces of code with performance counters. You'll find sample code for using performance counters on MSDN.

So where are your cycles going? You are probably waiting for data coming from main memory. Go read up on L1 & L2 caches. Understanding how the cache works is half the battle. Hint: Use tight, compact structures that will fit more into a cache-line.

Optimization is lots of fun. And it's never ending too :)

A great book on optimization is Write Great Code: Understanding the Machine by Randall Hyde.

Chris Masterton
+2  A: 

Optimizing a program means: make it run faster

The only way of making the program faster is making it do less:

  • find an algorithm that uses fewer operations (e.g. N log N instead of N^2)
  • avoid slow components of your machine (keep objects in cache instead of in main memory, or in main memory instead of on disk); reducing memory consumption nearly always helps!

Further rules:

  • In looking for optimization opportunities, adhere to the 80-20-rule: 20% of typical program code accounts for 80% of execution time.
  • Measure the time before and after every attempted optimization; often enough, optimizations don't.
  • Only optimize after the program runs correctly!

Also, there are ways to make a program appear to be faster:

  • separate GUI event processing from back-end tasks; priorize user-visible changes against back-end calculation to keep the front-end "snappy"
  • give the user something to read while performing long operations (every noticed the slideshows displayed by installers?)
mfx
"Optimizing a program" doesn't necessarily mean "make it run faster." It's not a very specific statement. "Optimizing a program for speed", for example, would mean that. Besides the canonical time and space optimizations others have mentioned in their answers to this question, one could also optimize for power (e.g. allowing the CPU to remain in a low power state as much as possible). The point is that one should be clear about *what kind* of optimization they're referring to.
Void
Sure, you can optimize for anything - execution time, development time, memory consumption, startup time, energy consumption, code legibility, disk usage, fan noise. But the question was about the meaning of "highly optimized code", and that nearly always means "optimized for speed".
mfx
A: 

Make sure your application produces correct results before you start optimizing it.

Mats Wiklander
+2  A: 

This is a good question.

Usually the best practice is 1) just write the code to do what you need it to do, 2) then deal with performance, but only if it's an issue. If the program is "fast enough" it's not an issue.

If the program is not fast enough (like it makes you wait) then try some performance tuning. Performance tuning is not like programming. In programming, you think first and then do something. In performance tuning, thinking first is a mistake, because that is guessing.

Don't guess what to fix; diagnose what the program is doing. Everybody knows that, but mostly they do it anyway. It is natural to say "Could be the problem is X, Y, or Z" but only the novice acts on guesses. The pro says "but I'm probably wrong".

There are different ways to diagnose performance problems.

The simplest is just to single-step through the program at the assembly-language level, and don't take any shortcuts. That way, if the program is doing unnecessary things, then you are doing the same things, and it will become painfully obvious.

Another is to get a profiling tool, and as others say, measure, measure, measure.

Personally I don't care for measuring. I think it's a fuzzy microscope for the purpose of pinpointing performance problems. I prefer this method, and this is an example of its use.

Good luck.

ADDED: I think you will find, if you go through this exercise a few times, you will learn what coding practices tend to result in performance problems, and you will instinctively avoid them. (This is subtly different from "premature optimization", which is assuming at the beginning that you must be concerned about performance. In fact, you will probably learn, if you don't already know, that premature concern about performance can well cause the very problem it seeks to avoid.)

Mike Dunlavey
+2  A: 

However, as a self-taught, new programmer I've never really understood what exactly do people mean when talking about such things.

Let me share a secret with you: nobody does. There are certain areas where we know mathematically what is and isn't slow. But for the most part, performance is too complicated to be able to understand. If you speed up one part of your code, there's a good possibility you're slowing down another.

Therefore, anyone who tells you that one method is faster than another, there's a good possibility they're just guessing unless one of three things are true:

  1. They have data
  2. They're choosing an algorithm that they know is faster mathematically.
  3. They're choosing a data structure that they know is faster mathematically.
Jason Baker