Forgot your password?
typodupeerror
Programming Technology

Optimizations - Programmer vs. Compiler? 1422

Posted by Cliff
from the who-can-obfuscate-better dept.
Saravana Kannan asks: "I have been coding in C for a while (10 yrs or so) and tend to use short code snippets. As a simple example, take 'if (!ptr)' instead of 'if (ptr==NULL)'. The reason someone might use the former code snippet is because they believe it would result in smaller machine code if the compiler does not do optimizations or is not smart enough to optimize the particular code snippet. IMHO the latter code snippet is clearer than the former, and I would use it in my code if I know for sure that the compiler will optimize it and produce machine code equivalent to the former code snippet. The previous example was easy. What about code that is more complex? Now that compilers have matured over years and have had many improvements, I ask the Slashdot crowd, what they believe the compiler can be trusted to optimize and what must be hand optimized?"
"How would your answer differ (in terms of the level of trust on the compiler) if I'm talking about compilers for Desktops vs. Embedded systems? Compilers for which of the following platforms do you think is more optimized at present - Desktops (because is more commonly used) or Embedded systems (because of need for maximum optimization)? Would be better if you could stick to free (as in beer) and Open Source compilers. Give examples of code optimizations that you think the compiler can/can't be trusted to do."
This discussion has been archived. No new comments can be posted.

Optimizations - Programmer vs. Compiler?

Comments Filter:
  • Security (Score:0, Interesting)

    by Anonymous Coward on Friday February 25, 2005 @04:48PM (#11781203)
    The future's pretty clear.

    You MUST trust the compiler more and more to protect the code from buffer overflows and other trivial, but hard-for-humans-to-detect mistakes.

  • Clear & Concise Code (Score:5, Interesting)

    by kwiqsilver (585008) on Friday February 25, 2005 @04:52PM (#11781263)
    It's better to write clear, legible code that saves a human minutes of reading, than complex code that might save a computer a few milliseconds of processing time per year, because human time costs more than machine time.
    Also the clear code will result in fewer misinterpretations, which will mean fewer bugs (especially when the original author is not the one doing maintenance years later), further reducing costs in dollars, man hours, and frustration.
  • by sporty (27564) on Friday February 25, 2005 @04:53PM (#11781276) Homepage
    Common idioms should be compiled away, like !x or x!=0. Uncommon idioms can't and probably shouldn't be attempted, i.e. if(!(x-x)) (which is always false). Ask your compiler maker and see if patches can be made for these types of things. 'cause if you think to do it one way, chances are, many others may try it too. It would be for their benefit to make a better compiler.
  • Beware of habits. (Score:5, Interesting)

    by SharpFang (651121) on Friday February 25, 2005 @04:54PM (#11781289) Homepage Journal
    I got in the habit of writing "readable but inefficient" code, taking care that my constructs don't get too sophisticated for the optimizer but then depending on gcc -O3 thoroughly. And then it happened I had to program 8051 clone. Then I learned there are no optimizing compilers for '51, that I'm really tight on CPU cycles, and that I simply don't know HOW to write really efficient C code.
    Ended up writing my programs in assembler...
  • by LiquidCoooled (634315) on Friday February 25, 2005 @04:54PM (#11781297) Homepage Journal
    An example would be searching and sorting algorythms (I know there have been thousands of variations, and entire libraries now exist, but theres always some other need for a non generic search function)

    I could write sloppy code which appears to be significant, but then realise that holding this here, and keeping that register there, then I can do such a thing just a fraction quicker than before.

    Its not so much going to the assembler level anymore, but a tightly coded loop tuned by human intuition will almost always still be faster than anything an optimiser can give.

    I recently had an issue with sorting collections containing thousands of none trivial objects. Every time I adjusted the adready fast quicksort, I gained a little more speed.

    Its always been the way, and until genetic compilation and optimisation comes along trying every combination, it will continue to be the case.
  • by sabre (79070) on Friday February 25, 2005 @04:58PM (#11781354) Homepage
    LLVM is an aggressive compiler that is able to do many cool things. Best yet, it has a demo page here: http://llvm.org/demo [llvm.org], where you can try two different things and see how they compile.

    One of the nice things about this is that the code is printed in a simple abstract assembly language that is easy to read and understand.

    The compiler itself is very cool too btw, check it out. :)

    -Chris
  • by zapatero (68511) on Friday February 25, 2005 @05:01PM (#11781418) Journal

    Those that advocate the constant first in if boolean comparisons are the feeble minded who believe ever bit of propaganda and PC reporting they read.

    It's especially annoying to see it done in programming languages like Java where assignments are not even allowed in if statements. When I see java code like this:
    if (0 == x)
    I'm thinking "loser".

    Back to C. And besides gcc since C-99 has issued warnings for assignments in the "if" expression when not embedded in parenthesis.
    example...
    if (x = 3) ... will result in a warning.
    if ((x = 3)) ... no warning.
  • Re:Clear Code (Score:3, Interesting)

    by Tr0mBoNe- (708581) on Friday February 25, 2005 @05:03PM (#11781455) Homepage Journal
    I agree. I write my code under the assumption (rightly so) that my code will be reviewed by many people in the near future, and then added onto after a bit.

    In fact, working at Research In Motion has shown me how to write better code... sometimes, the most efficient or clever solution are the worst. I would rather see longer variable names, descriptive control structures and a total lack of goto statements. If you write sensible code, and use an optimization setting of 4 or 5, then you will have better programs in the long run. Also, the more complex and "optimized" your code is, the less chance the compilier would be able to optimize it and may even slow it down a little.

    But the biggest thing is to make it readable... I think writing code that executes SO FAST would be useful only in real time systems and large servers.

    Now... back to my realtime system... gotta make those blade servers smoke!
  • by hpa (7948) on Friday February 25, 2005 @05:06PM (#11781504) Homepage
    if (0!=ptr) is a hideous abortion, because it's not readable.

    Also, your statement that "relying on NULL to be compatible with all pointer types is risky" is just plain wrong.
  • Re:Clear Code (Score:1, Interesting)

    by android79 (213151) on Friday February 25, 2005 @05:09PM (#11781555)
    Actually, 'since' is incorrect (or atleast less correct, depending on whom you ask). 'Because' would be a better word in that case.

    'Since' implies a temporal relationship while 'because' more clearly shows a causal relationship.
  • by aixguru1 (671173) <aixguru1@unixsystemengineer.com> on Friday February 25, 2005 @05:13PM (#11781619) Homepage
    A good programmer who codes in C should be able to understand what is going on. Still it is no substitute for real documentation... Someone should let the folks that enter the IOCCC [ioccc.org] that they have some of the worst self documenting code I have seen though. It's like trying to read the Xlib programming manuals after a few to many drinks (not recommended and may result in a nasty hangover in the morning).
  • by Flyboy Connor (741764) on Friday February 25, 2005 @05:16PM (#11781664)
    Quite agree, it's about the algorithm, not about the code.

    One of the finest moments in my programming career was when my boss asked me to see if I could gain a speed improvement in a program that surveyed a huge datastore and generated volumes of text from it. This program had to run once a month, and deliver its result in the same month. The program that was originally written, unfortunately, took three months to run (it started out OK, but the data store had grown considerably). They had asked one of our "best programmers" to create a faster version of the program. He did that by reprogramming the entire thing in assembly (you may now understand why managers thought he was one of the best programmers). It took him six whole months to finish the new version. The resulting program completed the task in just about one month. However, my boss was afraid that when the datastore would grow a bit more, we would again be in trouble. That's when he asked me to look it over. I started by investigating the problem, which at first glance looked like a network traversing problem. I soon realised it could be solved by a nested matrix multiplication (which is, of course, a standard way to discover paths in a network). It was a matrix with about a million rows and columns, but since it contained only zeroes and ones (with a couple of thousands times more zeroes than ones), the multiplication was easy to implement in a fast way. Within half-a-day, I had built a prototype program in a high-level language which did the whole job in a few hours.

    While I am still pleased with this result, I really think it came off so well not because I was so smart, but because the assembly programmer was not really worthy of the name. Still, I often use this as an illustration for students who are writing illegible code and argue that it is so very fast.

  • Re:Clear Code (Score:5, Interesting)

    by david duncan scott (206421) on Friday February 25, 2005 @05:23PM (#11781759)
    Yup, that's why your bank throws away all three zillion lines of COBOL every year -- because there's a greater risk in maintenance than in new code.

    I wish I could put my hands on an article I read a couple years back on the code in the Space Shuttle. They go at that code base with an attitude that makes the average paranoid look happy-go-lucky. In fact, they approach software engineering kind of like other engineers do -- as if lives depended on it. It's old, it's slow, and it works. (Oh, wait, here it is. [fastcompany.com]) That's how code is maintained.

  • by 14erCleaner (745600) <FourteenerCleaner@yahoo.com> on Friday February 25, 2005 @05:25PM (#11781799) Homepage Journal
    I disagree with you about the readability of (!ptr), but at least it is a very common idiom in C, and I can accept that.

    But please, oh, please, don't write this:

    if (!strcmp(x,y))

    Intuitively, that looks exactly backwards from what it's testing (equality).

    On the "optimize for the compiler" issue, I think what's been said already is right: don't do it unless it's in a critical spot, write code for readability (and big-picture efficiency) first, then worry about local optimizations only if there's a problem.

  • by dragons_flight (515217) on Friday February 25, 2005 @05:50PM (#11782161) Homepage
    As someone that probably operates in the world of the other 1% (academic progammer with computational tasks measured in hours, days, or even weeks), I can still say that it is almost always about the algorithm.

    Every once in a while it really is worth careful instruction level optimization when you have a loop that must execute more than 100 million times, or some such. But the biggest gains in computational speed are always about figuring out the best approach to a problem.
  • by willow (19698) on Friday February 25, 2005 @06:08PM (#11782386)
    Most compiled programs don't need any optimization from the compiler or programmer because their performance is just not that important.

    If the program must be fast your first concern should be getting the right answer. I can make any program lightning fast as long as it doesn't have to return the right answer. This may seem trivially obvious but you'd be surprised by how many times optimization attempts end up optimizing away the right answer.

    Pick the right algorithm and implement it clearly.

    If it's too slow, break out the profiler and optimize.

    If it's still too slow, you screwed yourself by not including performance requirements at the very beginning. Maximum performance must be designed in from the start (e.g., look at high performance matrix multiply libraries)
  • I would use if (!ptr) for a different reason. It is not hard to accidently type if(ptr = NULL) instead of if(ptr == NULL) thereby accidently add a reason for your program to segfault.

    If you are going to do this, it is better to do if(NULL == ptr) so that if you omit one of the equal signs, it won't compile.

    In the end we can argue these things forever. People see code differently and may find one idea elegant and easy while the other unmaintainable and unreadible. Sort of like the thread v process debate (ongoing for now what... 30 years?).
  • by PoopJuggler (688445) on Friday February 25, 2005 @06:32PM (#11782646)
    Any company who makes a C compiler where NULL != 0 will not get much business methinks
  • Re:Clear Code (Score:4, Interesting)

    by lymc (862849) on Friday February 25, 2005 @06:35PM (#11782674)
    Now, let me tell you the flip side of that story. I was working on the Viking Mars Lander program long ago (1976). The code for the lander programs was frozen a year before launch (which itself was a year before landing). Some dozen "books" of the assembly code were created and archived for use when the landers finally landed. All went well for about 6 months after landing. Updates were made and duly entered into the master asssembly listings. After six months of this, the listings were so xed out, and all the margin space used with notes, that errors started creeping in. Finally an uploaded patch wrote over part of the antenna pointing table, and the lander was lost, but for a fortuitious accident which allowed the table to be re-established (with much howling and gnashing of teeth). Sometimes new is better, even if it is painful.
  • Re:Clear Code (Score:3, Interesting)

    by mveloso (325617) on Friday February 25, 2005 @06:40PM (#11782706)


    Well, in Java this may speed things up. All instance variable references won't be cached locally. It's been a while since I checked this, but I believe that it's still true. Just something to think about if you're looping a few million times.

    Sometimes it helps, sometimes it doesn't. The only way to really know what your compiler does is to look at the output of the compiler, and understand what all those switches do.

    Now there are some things you can do to optimize your code while you're writing it, but a lot of that is heavily dependent on your platform, runtime, and environment. In general, you should write your code in a straightforward way, then write an optimized version of it later (if you determine it's necessary).

    Of course, the best way to help your optimizer is to write things in the simplest manner possible.
  • Re:Clear Code (Score:3, Interesting)

    by ArbitraryConstant (763964) on Friday February 25, 2005 @06:43PM (#11782734) Homepage
    When passing pointers, the "const" thing is a big deal.

    It has to make sure it uses the copy in memory everywhere if anyone else gets a copy of the pointer. That function might keep a copy, pass it around there's no way to know.

    If you use "const" in the function decleration, it can assume no one elsewhere will be changing it.

    Of course, in my always humble opinion using a compiled language at all is a premature optimization most of the time. :)
  • by Anonymous Coward on Friday February 25, 2005 @07:09PM (#11782999)
    Parent wrote:

    Every time I adjusted the adready fast quicksort, I gained a little more speed.

    Beware of trying to "tune" quicksort. The stock quicksort that comes with your stdlib is already tuned for maximum *average* performance.

    Keep in mind that quicksort will always have a worst case performance of O(n^2); however, on average it runs about 10% faster than the fastest possible heapsort and mergesort algorithms, which are both O(log n). The main reason for using quicksort is the fact that it's 10% faster *on average* than the pure O(log n) algorithms.

    Any 'optimizations' you make to quicksort may boost performance for degenerate data sets that hit the 'worst case' on several recursions of quicksort, but they might hurt average performance. If you end up doing more than 10% damage to the average, then you might as well be using a strictly O(log n).

    If you're not willing to get your modifications to quicksort peer reviewed, don't bother touching it.

    -my 2 cents

  • by rleibman (622895) on Friday February 25, 2005 @07:11PM (#11783014) Homepage
    Of course, the knife cuts both ways. If you comment everything and then the logic changes during maintenance without a corresponding change to the comments, they becomse worse than convoluted code (at least convoluted code can run through a debugger to give you an idea of what's happening).
    That would then be because your commented the wrong thing. Comments should have nothing to say on what is being done, that should be obvious by the code, comments have much more value, become obsolete less often and make code cleaner when they explain why things are done: comments give the big picture, the code the details. Steve McConnel's "Code Complete" is a wonderful treatise on this and other good things.
    As a trivial example:
    int a = b + 1; //Make a equal b plus one That's clearly stupid, but it's surprising how often I see code like this.
  • Re:Clear Code (Score:5, Interesting)

    by drgonzo59 (747139) on Friday February 25, 2005 @07:15PM (#11783052)
    Same goes for code that runs on the airplanes (like Boeing passenger aircraft). In fact the developers have to prove that each possible! branch that code could ever take won't lead to unpredictable behavior or crash. If you have 100 independent 'if' statements that is at least 2^100 possibilites. The code they write is very linear, they avoid branching at all cost.

    There is a whole are of study involved in correctness checking, which is related to the SAT (Satisfiability) problem.

    The operating system choice is also interesting. Linux doesn't even come close to what they need. Having device drivers in the kernel is just not a good idea. It needs to have a separation kernel, at least that is the goal. I presently think they use the INTEGRITY operating system by Green Hills, but I could be wrong.
  • by Jugalator (259273) on Friday February 25, 2005 @07:19PM (#11783084) Journal
    There's often no point in fiddling around with details, and you should instead focus on optimizing for "the big picture", e.g. whether you should really re-establish a database connection each time a user selects OK in a dialog box.

    As for simple code optimizations, here's what a modern compiler (Microsoft Visual C++ .NET 2003 in this case) optimize code as: (yes, I've checked these myself as I've been writing this post)
    for (int i = 0; i < 5; i++)
    {
    int j = 42;
    if (j == 13)
    {
    printf("j is %d", j); j = 42;
    }
    }
    return 0;
    ... optimized to ...
    xor eax, eax
    ret 0
    In other words, it basically threw away all that code since the program wouldn't make use of any values that would be calculated there. And this:
    for (int i = 0; i < 100; i++)
    {
    int j = 42;
    i += j;
    }
    printf("i is %d", i);
    return 0;
    ... optimized to ...
    push 129
    push OFFSET FLAT:??_C@_07JEGIODIB@i?5is?5?$CFd?$AA@
    call _printf
    add esp, 8
    xor eax, eax
    ret 0
    Do you see what that means -- yep, the compiler understood that the resulting value would be 129 by analyzing the for loop and simply just push that value onto the stack as a parameter to printf. It basically optimizes to:
    printf("i is %s", 129);
    return 0
    That's probably considered some simplistic analyzing too, by today's compilers. ;-)
  • Re:Clear Code (Score:3, Interesting)

    by say (191220) <sigve&wolfraidah,no> on Friday February 25, 2005 @07:28PM (#11783177) Homepage

    no compiler I've sean can optimize an algorithm

    Although I definitely agree with your conclusions, the above sentence is not correct. Most implementations of Scheme, for instance, does a lot of algorithmic optimisations, basically by turning tail-recursion into (functional) iteration when possible.

    These kind of optimizations are extremely effective in some scenarios because it significantly reduces memory use and read/write of memory, and can be illustrated quite easily by turning optimization off in any good Scheme interpreter (like DrScheme).

  • Re:Not always. (Score:3, Interesting)

    by EsbenMoseHansen (731150) on Friday February 25, 2005 @07:39PM (#11783261) Homepage
    why, oh why, is NULL considered to be more clear than 0?

    In C, this is because 0 != NULL. NULL = (void*) 0. This makes a difference in function calls, where calling myfoo(int a) with myfoo(NULL) is a compiler error/warning, but myfoo(0) is legal.

    In C++, NULL = 0. Really. Here's the header from gcc (well, really the kernel):

    #if defined(__cplusplus)
    #define NULL 0
    #else
    #define NULL ((void *)0)
    #endif

    This is because C++ has points to members, I believe (not sure, don't use void* pointers). Instead I use this struct, which IMHO should be included in STL (slightly shortened in the interest of brevity, and written from memory, so may not compile):

    struct Null_ {
    template<typename T>
    operator T*() { return 0; }

    template<typename T, typename S>
    operator S::T*() { return 0; }
    } my_null;

    The first member ensures that my_null can be converted to any pointer other than pointer-to-member, and the last one takes care of those. This method makes the C case above work as it should, i.e. give an error.

    As for clarity, it separates two concepts: The NULL pointer (a pointer pointing to nothing) and 0 (an integer).

    As an aside, I actually prefer !p, since it say to me "If p not a valid pointer....". But I can handle both :)

  • by Mr_Huber (160160) on Friday February 25, 2005 @08:09PM (#11783500) Homepage
    Consider all the hype last week about the cell processor. Here is a processor in which the CISC optimizing portions have been removed, trusting the compiler to create pre-optimized code. The cell processor will run this code blindingly fast and with no modification. The compiler must be a smart optimizer. IBM, Toshiba and Sony are betting a lot on a smart optimizer. I'm guessing they won't be disappointed.

    Further, consider the Parrot system to be used by Perl, Python and Ruby. There's a strong similarity here to the cell system. All three languages are to be compiled to a common register-based representation. That representation is to be optimized by the compiler before execution. They chose this model because w we have decades of research on optimizing code for register based computers (as opposed to Java's stack based computer).

    In short, some very large, very important projects already have a lot of faith in these optimizers. They are not going away. I suggest the best approach is to work with them.

    So how do you cooperate with your optimizer? Write cleanly and clearly. Don't try to outsmart the optimizer, because if you do so, your code will most likely be slower, not faster. And don't do any work until you need to. Write the project correctly and clearly, then profile. If you need to modify things, then you have a working baseline to compare your optimizations with.

    Finally, when you get the faster version, check it in, then refactor the design to something reasonable, rechecking the speed as you do so. Ideally, for a small performance hit, you can end up with fast, efficient and easy to maintain code.
  • by Anonymous Coward on Friday February 25, 2005 @09:04PM (#11783929)
    I recently wrote some code to transform an image (proof-of-concept, no filtering) according to a mapping table, like this:

    unsigned char *origimg = malloc (width * height * 3);
    unsigned char *imgdest = malloc (horpix * vertpix * 3);
    unsigned char **mapping =
    malloc (horpix * vertpix * sizeof (unsigned char *));

    with some formula to morph the image, getting a pointer to the input pixel for every output pixel:

    mapping[y * horpix + x] =
    origimg + somey() * width * 3 + somex() * 3;

    Now the interesting part. To do the mapping itself (repeatedly, think movie), this looks about the worst you can come up with:

    for (y = 0; y < vertpix; y++)
    for (x = 0; x < horpix; x++)
    {
    imgdest[(y * horpix + x) * 3] = *mapping[y * horpix + x];
    imgdest[(y * horpix + x) * 3 + 1] = *(mapping[y * horpix + x] + 1);
    imgdest[(y * horpix + x) * 3 + 2] = *(mapping[y * horpix + x] + 2);
    }

    YET: Try ANY hand-optimization that performs the exact same function, and gcc-2.95.2 -O9 -funroll-loops on i386 will produce SLOWER code! Even having a single var for (y*horpix+x) is SLOWER! If you don't believe me, try it. I did, and I was _very_ surprised.

    The fastest version I ended up with actually optimizes differently by not copying 3, but 4 bytes at once:

    for (y = 0; y < vertpix; y++)
    for (x = 0; x < horpix; x++)
    {
    *((unsigned int*) (imgdest+((y * horpix + x) * 3))) =
    *((unsigned int *) mapping[y * horpix + x]);
    }

    But this is only a few percent faster than the original, and again, do NOT optimize further!

    -- JAB
    (Code donated to the public domain)
  • Re:Clear Code (Score:3, Interesting)

    by John Hasler (414242) on Friday February 25, 2005 @09:05PM (#11783931) Homepage
    > There is a whole are of study involved in
    > correctness checking, which is related to
    > the SAT (Satisfiability) problem.

    Knuth:
    "Please don't assume that the above code is error-free. I have only proven it correct, not tested it." (or words to that effect)
  • by wiredlogic (135348) on Friday February 25, 2005 @09:06PM (#11783938)

    No, "implementation defined" means that it is up to the compiler, OS (if present), and possibly the hardware to determine what will be used to represent the NULL address.

    This is addressed in the C-FAQ [eskimo.com] where systems from Prime, Data General, and Honeywell-Bull are noted for having non-zero null pointers (at least for their C-compilers).

    This issue brings up one of my pet peeves with C++. The designers (I don't think Stroustroup deserves all the blame)went all out in adding weak type safety to the language by eliminating automatic casts but then they determined that "((void)(*0))" violated they safety model and decided to invoke special "spooky" behavior when comparing pointers against the literal zero. This of course leads to the annoying practice of leaving out the Boolean operator altogether and using "if(foo)" and "if(!foo)".

    I imagine the C++ architects were at least partially motivated by their "macros are evil" mantra (why they didn't do anything about the need for header guard macros is beyond me). They should have had the guts to introduce a new "null" keyword. This would not conflict with existing ANSI C code using NULL and code could easily be ported with substitution or:

    #define NULL null
    I can't believe that this wasn't proposed at some time. It must have been shot down because someone complained that it would break their legacy C code. Only a fool would put case variants of standard macros and keywords into their C code and porting older pre-standard-NULL K&R code would require revisions anyway. They deserve any breakage if they tried to port such crap to C++.
  • by mystran (545374) <mystran@gmail.com> on Friday February 25, 2005 @10:11PM (#11784359) Homepage
    Actually, while I agree on the main point, I must disagree to a certain degree, because my empirical experience suggests something different: If something looks complicated, there's a good chance that by trying to write it in a more clear/straightforward (so as to make it easier to understand) will also result in more efficient machine code (as benchmarked).

    As such, I'd say: if something is too slow, benchmark it, then try to write a more simple version and benchmark that. Many times the result will be faster (and rest of the time you probably need a better algorithm).

    As funny as it sounds, because compilers do fancy optimizations, writing code to "explain the logic to the compiler" might well enable optimizations that a compiler couldn't use on "more optimized" code. One obvious (and simple) example is the "const"-modifier in C, which helps clarify extent of usage to both other programmers AND the compiler; the result in performance can be quite dramatic in truly CPU-bound code.

  • by Anonymous Coward on Friday February 25, 2005 @10:20PM (#11784435)
    #1 of course is choice of good algorithm, hash structures, lookup structures, whatever.

    As an example, I had some terrain rastering program starting with isoheight lines to optimize. Some computer scientists had already optimized the searches, cutting the running time for jobs with in the order of 100000 line segments in half. From every raster point, searches were done in eight directions to find the next isoheight line, then a distance-weighted average was taken.

    The code had become unreadable with the optimizations and still ran a day. I decided to rewrite it, matching the data structures the other way round. Instead of going through the raster points and looking for the lines, I went through the lines and registered every of their crossing with the straight and diagonal grid (Bresenham). Then I sorted the grid crossings and worked out the stuff.

    It took me two hours of debugging after 10 days of implementation to find out why my program would exit unexpectedly after less than a minute. I finally got it. It was finished.

    So here is rule #1: design your algorithm well. Here is #2: when fitting regularly spaced data with irregularly spaced data, walk through the irregular stuff and find the corresponding regular stuff: that can be addressed immediately by indexing without requiring a costly search.

    This is macro optimization. There comes a point of time when you actually need to optimize at the small level. Don't go there until your profiler tells you.

    And here is the number one rule for optimizing C code: don't use pointers if you can avoid them.

    There is a clever trick to implement twodimensional arrays as pointer arrays pointing to the actual rows of the array. This saves you a multiplication for pointer arithmetic.

    Making use of this clever trick with modern processors and compilers will cause your performance to drop by about a factor of 5. I am not kidding you. The compiler's aliasing and parallism detection passes are made useless by this technique, strength reduction fails, the out of cache line accesses cause thrashing in memory accesses, and the additionally necessary memory accesses are much slower than address calculation which happens at full processor speed.

    Use C99's variable dimensioned arrays instead: those are not half as confusing to the compiler, and much more efficient, anyway. And, sad to say: think of using numeric subroutines in Fortran when having to deal with multidimensional arrays. C99 has not been around for so long, and almost no code making use of its features exists.
  • by smcdow (114828) on Friday February 25, 2005 @10:45PM (#11784575) Homepage
    I've been using it for a while...
  • Re:Not always. (Score:2, Interesting)

    by Impy the Impiuos Imp (442658) on Friday February 25, 2005 @11:13PM (#11784748) Journal
    > People that honestly believe that "if it's
    > well written it doesn't NEED comments"

    They are lazy, and lack attention to detail. This is a mental escape on their part that assuages their small-frog egos.

  • by Impy the Impiuos Imp (442658) on Friday February 25, 2005 @11:30PM (#11784837) Journal
    That's not possible, but a language that doesn't use pointers per se could, and do, exist. Of course they really use pointers behind the scenes, complete with integrity, i.e. NULL checks, so you're not gaining anything.

    Now a processor that zero-checked a pointer on memory deference, auto-built to skip the deference, combined with a language that supported it, would have the check in hardware... ...although zero check is one of the simplest ALU operations, and thus the same speed anyway. Nevermind.
  • by Anonymous Coward on Saturday February 26, 2005 @01:28AM (#11785405)
    > But please, oh, please, don't write this:
    > if (!strcmp(x,y))

    #define strings_match(str1, str2) (!strcmp(str1, str2))

    Much clearer.
  • If that's true.... (Score:3, Interesting)

    by TheLink (130905) on Saturday February 26, 2005 @07:17AM (#11786186) Journal
    If it's 99% true, then perhaps more than 80% of programmers shouldn't be writing in C. (Judging from the posts on this discussion I think I'm right ;) ).

    Because IMO nowadays writing in C is usually a premature optimization.

    With all the GHz CPUs, higher level languages are often more than fast enough. It's usually the design and algorithms that you have to get right.

    The advantage of writing in a higher level language first is you write far fewer lines of code - esp if the Customers keep changing their minds every month.

    Optimizing at low levels only gives you linear improvements in speed. Doesn't help you if the system slows exponentially.

    What I mean by linear is:

    Say you use the same algorithm.
    #1: fast low level language, optimized.
    #2: in a fast low level language.
    #3: high level language.

    If #3 is 4 times slower than #1 in most cases it'll always be 4 times slower. Same if #2 is 10% times slower than #1.

    Whereas a high level optimization can gain you lots more.

    On a fair size complex system it's probably best to write stuff/modules in a high level language first and then replace them with C or hand assembly later if necessary.

    Maybe it is wasteful. But at least you can say to the people doing it - the informal spec is: "it's got to work exactly like what you are replacing - only faster". After all that module is already working :).

    Call the stuff in the high level language your pseudocode. The equivalent of the plastic/clay model or prototype.
  • by Kell_pt (789485) on Saturday February 26, 2005 @10:15AM (#11786602) Homepage
    Given a pointer to an object, it is not the pointer which is null. The pointer is zero, it is the object which is null, seeing as it is impossible to obtain its value from a pointer with value zero. We just do the trick of using both pointer and its contents to represent incomplete information [temple.edu].

    The concept of NULL is an extension to the domain of a variable. It means "no value". In C/C++, we often think of the pointer as the object itself, and as such we do the little trick of using two different operations to get that valuable extension to the domain: we either read the value of the pointer (a memory address) and compare it to zero or we load its content ( the -> operator ). However, why doesn't one usually compare pointers to static values other than zero( eg, ptr != 3 )? In not doing it, we bring to light the fact that we're just using part of the domain of the pointer variable.
    To make this clear, think of what you'd have to do to have a null integer. In C you'd likely do:
    int * val = malloc( sizeof(int) );
    And then you'd use "val" for the null comparison and "*val" for the integer. Funny how in doing that you're spending more memory than if you did:
    typedef struct { int value; bool isnull; } NullableInt;
    NullableInt a;
    With this you'd spend 5 bytes per variable. With the other approach you spend 4 bytes per variable plus 4 bytes for the pointer, for a total of 8 bytes (on 32 bit architectures). :)

    Obviously, these considerations are a bit pointless, and one benefits little from them in the context of C/C++ programming. :=)

    On the other hand, some languages have specific support for nullable variables, without having to resort to pointers (at least syntactically). NULLs are famous in SQL, although they are also infamous for being used incorrectly many of the times. ;)

Never say you know a man until you have divided an inheritance with him.

Working...