|
Optimized QuickSort — C Implementation August 2005, July 2007 Wikipedia contains a C language implementation of QuickSort which looks like this: [UPDATE: Wikipedia’s content is constantly shifting, and is no longer accurately reflected by the following code sample.]
This Wiki code is closely replicated on many other sites, and probably in many in-use implementations. I have rewritten it here to execute more efficiently, as follows:
Advantages of my code over the Wikipedia code: 1. FUNCTION CALLS My implementation avoids function calls. Calling (and returning from) a function is time-consuming, and not because the content of the function takes time to execute — just getting into the function, and back out of it, uses processor time. 2. STACK My implementation does not use the stack to store data. Recursive functions look neat and simple in part because they don’t have to have big arrays like my beg[] and end[]. But all they’re really doing is using the stack as their own private array. This is much slower than using a real array, and could cause stack overflow on some systems, crashing the app that called the quicksort. My implementation simply returns NO if this kind of failure occurs. 3. SWAP VARIABLE In any one pivot round, the Wikipedia version can pass items through a swap variable many times. My code passes only one item (the pivot) through a swap variable, and only once per round. 4. MULTIPLE MOVES In any one pivot round, the Wikipedia version can move the same item more than once in the list. My code never moves an item to a new position in the list more than once per round. 5. UNNECESSARY MOVES In any one pivot round, the Wikipedia version will sometimes move items to new positions in the list even though they were already on the correct side of where the pivot item was going to wind up. My code never moves an item if its current position is on the correct side of the pivot’s destination. 6. SELF-SWAPS In each pivot round, the Wikipedia version has about a 50% chance of swapping one of the list items with itself. (And this swap incurs the costs described above in #1, #2, and #3.) (Note that the benefits of all six advantages are greatly increased if the items being sorted are substantially sized data structures, as opposed to simple integers as in these code examples.) Limitations of my code vs. the Wiki code: 1. COMPARISONS My code performs more comparisons, on average, than the Wiki version. Wiki does 30% fewer comparisons by my (very rough) estimate. Both functions seem to do about the same number of increments/decrements, as far as I can tell. Here’s an example of how my code handles one pivot round (a.k.a. “partition”):
Notice that the data values 3, 8, and 7 (in slots [2], [5], and [7], respectively) do not move because they don’t need to. All other items (except the pivot) are moved only once, and each item is compared with the pivot only once. Now — here’s how the Wiki code handles the same pivot round. (I will assume that the programmer has the good sense to declare the “swap” function “inline”, so that there is no function-call overhead during the processing of a pivot round.)
Of the data values 3, 8, and 7 (none of which need to move) only 3 is left alone, and 8 is moved twice. The swap variable is used five times (or five and a half if you count copying out the pivot value, which is part of the single swap my code uses). No-Fail Version Here’s a modified version that never fails — i.e. it never runs into its MAX_LEVELS limit:
This might be slightly slower than the first one, but it will never fail because it always performs the smaller partition first. Hence, the only way it could run into the limit is if the to-be-sorted array was at least 2MAX_LEVELS elements in size. Since 2300 is greater than 1090, and there are only about 1080 fundamental particles in this universe from which a human-made computer can be built, no larger limit will ever be needed. Bugs? Performance tests? Questions? I’d love to hear about it — please shoot me an e-mail. Back to Tutorials. |