• (cs)

    Longjmp is loooooooooong!

  • Alex (unregistered)

    Uh.... why did you even need to test which would be faster? A for loop compiles to a test and branch. A longjmp compiles to a freaking function call, one that has to inspect data structures and modify the return address on the stack.

  • monkeyPushButton (unregistered)

    If I had used long jump, maybe I could have been First!

  • Jimbo (unregistered) in reply to DaveK
    DaveK:
    Longjmp is loooooooooong!
    Grow up already you sad little boy.
  • Konrads (unregistered)

    People who write assembly code or setjmp like in this example in C for speed purposes are disregarding 30 years of work in field of compiler optimization. The better You can describe what You want to compiler, the better it optimizes.

  • adiener (unregistered)

    I've used longjmp in a unit test harness, to bail from an entire test suite if one of the test case asserts fails. libpng also uses it, longjmping back to a location in your code if it encounters an error while reading an image. I think of longjmp as more akin to throwing an exception than a goto.

    Also, to make sure your test is valid, you'll want to enable compiler optimization with -O3 or similar. I tried compiling your examples (after changing getch() to getchar()) with -O0 and -O3, and interestingly, it made no perceptible difference for the longjmp test, though it sped up the no_longjmps test greatly. So not only is the longjmp version a lot slower, it also isn't as optimizable.

  • (cs)

    Your code's no good! In no_longjmps.c, you're not even considering the amazing nuclear powers of longjmp(), while longjmp.c has gone to great lengths to make sure it finds a suitable place for it.

  • (cs) in reply to Alex
    Alex:
    Uh.... why did you even need to test which would be faster?
    I think Mark was showing how a professional approaches these sorts of implementation decisions.
    Alex:
    A for loop compiles to a test and branch. A longjmp compiles to a freaking function call, one that has to inspect data structures and modify the return address on the stack.
    Yes, it may be obvious, but apparently it wasn't obvious enough for the guy who wrote the WTF in the first place; and the point of doing the demonstration is to illustrate that, regardless of what is or isn't obvious, assumptions can be tested and measured, and if he had done so he wouldn't have written such a dumb bit of code.

    Oh, and TRWTF is to be still using that valuable antique from the prehistoric DOS era, getch(), in the 21st century. (Does it still overflow your application's stack by trying to read all the available console input events in one go when you've just cut and pasted a couple of hundred kB of text into the window?)

  • (cs) in reply to Jimbo
    Jimbo:
    DaveK:
    Longjmp is loooooooooong!
    Grow up already you sad little boy.
    Lighten up, you boring old fogey!
  • Anonymous (unregistered)

    I see three major WTFs here:

    longjmp - the point of the article, complete WTF; Daren - loves longjmp and can't actually spell his own name correctly; Mark Bowytz - willing Vista user.

    WTF??

  • (cs) in reply to adiener
    adiener:
    I've used longjmp in a unit test harness, to bail from an entire test suite if one of the test case asserts fails. libpng also uses it, longjmping back to a location in your code if it encounters an error while reading an image. I think of longjmp as more akin to throwing an exception than a goto.
    That's possibly even more appropriate than you think; on some platforms it's the basic mechanism used to implement exceptions.
    adiener:
    Also, to make sure your test is valid, you'll want to enable compiler optimization with -O3 or similar. I tried compiling your examples (after changing getch() to getchar()) with -O0 and -O3, and interestingly, it made no perceptible difference for the longjmp test, though it sped up the no_longjmps test greatly. So not only is the longjmp version a lot slower, it also isn't as optimizable.
    Makes sense. The major part of the overhead in longjmp/setjmp is saving and restoring the entire execution context (setting up empty stack frames and calling the function takes very little in comparison), and regardless of optimisation there's an absolute minimum time it takes just to dump that many bytes from CPU to memory. (All those SSE registers make for hundreds of bytes of loads and stores...)
  • magi (unregistered)

    nexus:~> gcc -o test2 -O3 no_longjmps.c /tmp/cceJVp2k.o: In function main': no_longjmps.c:(.text+0x154): undefined reference togetch' collect2: ld returned 1 exit status

    Removing the getch() I get

    7.472s for longjmps.c, 0.308s for no_longjmps.c

    (Linux on Sparc 900MHz)

  • JW (unregistered) in reply to Konrads
    Konrads:
    People who write assembly code or setjmp like in this example in C for speed purposes are disregarding 30 years of work in field of compiler optimization. The better You can describe what You want to compiler, the better it optimizes.
    I totally agree. Library code is often faster than "user-optimized code", because the compiler can apply optimizations at the level of CPU/platform-dependent calls.

    However: a linear algorithm ALWAYS outperforms a quadratic one, and compilers don't change algorithms ;)

  • CodeReviewer (unregistered)

    In your 'fast' version, wouldn't it be better to eliminate the extra if-condition?

    void
    calculate(struct salesinfo*sales){
        int i = 0;
        
        for(;;) {
            if (!(i<sales->count))
                RETURN_NOTHING;
            else
            {
                addvaluetosubtotal(sales->values[i]);
                i++;
            }
        }
    } 
    
  • (cs) in reply to Alex
    Alex:
    A for loop compiles to a test and branch.
    No. A 'goto' becomes just a branch, and is very fast on modern computers. The test would be part of an 'if'...
  • (cs)

    Unreadable, WTF code:

        int i = 0;
        for(;;) {
            if (!(i<sales->count))
                    RETURN_NOTHING;
            addvaluetosubtotal(sales->values[i]);
            if (i<sales->count){
                    i++;
            }
         }
    

    Nice, clean code, easier to read:

    int i;
    for (i = 0; i < sales->count; ++i)
    {
        addvaluetosubtotal(sales->values[i]);
    }
    RETURN_NOTHING;

    (sorry about multiple empty lines, I couldn't figure out how to remove them)

    The for statement has a nice syntax, why don't people stick to it? It's better to say "loop i from 0 until just before sales count" than "i is zero and you should loop, break out of the loop if i is not less than sales count, add values to subtotal and if i is still less than sales count, increment it." It's a lot easier to understand what's happening when the conditions are explained just before the loop, instead of having to find them yourself after your brain suffers from a neurological fracture(just coined that) after trying to process this statement "for(;;){" You could have used a while loop, it would have made a bit more sense... but NOOOOOO, people like you have to use for(;;){ instead of while(i<sales->count){ because it's much cooler to write incomprehensible code.

    And that's how you use "for" loops in C-like languages. And that's also why you should use them as the gods designed them.

    Remember, each "for(;;){" statement you write will give you 10 hacker points but it will also decrease your penis size by a tenth of an inch, and if you're a girl, it will decrease your breast size to 95% of what you had just before starting to write "for(;;){".

  • YDD (unregistered)

    The real WTF is of course that the original developer failed to realize setjmp is horribly broken; he should have added a #DEFINE setjmp somewhere else, preferably in a file called reinventingwheel.h

  • (cs) in reply to JW
    JW:
    Library code is often faster than "user-optimized code", because the compiler can apply optimizations at the level of CPU/platform-dependent calls.
    Theoretically so, but in practice library code is faster because libraries are usually written by people who know what they are doing (e.g. by avoiding bubblesort...)
  • Bob (unregistered)

    The real WTF is that people still use C. A decent compiler would optimise both examples so there would be no difference.

    /flamebait

  • (cs)
    #define ABS_ZERO (1-1)
    ???
  • (cs)

    TRWTF is that Mark needed a whole IDE to compile a simple one-file program. ;=]

    And getch(), obviously.

  • virgil (unregistered)

    Sure, a for loop is faster, but i'm amazed how many people fail to see that longjmp is so much more enterprisey...

  • moltonel (unregistered)

    I can easily imagine how the coder found longjmp() to be faster : if his original version called calculate() in lieu of longjmp() (using recursion, where a sane programmer would use a loop), he'd potentially build a huge call stack, that was more cpu-intensive to handle than a setjmp/longjmp.

    That said, longjmp can be the right tool for the job, but usually for low-level stuff. I remember using it in a shell for some kinds of error recovery.

  • tulcod (unregistered)

    the real wtf is that the author didn't consider the fact that the compiler might have optimized away all the loops in the non-longjmp version, making it not loop at all.

  • Daniel (unregistered)

    setjmp/lonjjmp is a library solution to exception handling, given that C doesn't have any kind of intrinsic exception handling, like try/catch.

    What setjmp/longjmp DOES is, generally speaking, what the compiler does when it sees a try/catch. Except, of course, that a language construct, like try/catch, isn't subject to such abuse as a library soluction, as this WTF illustrates so well.

  • Why is DailyWTF more like DailyWTFWTF (unregistered)

    [quote=JW] Library code is often faster than "user-optimized code", because the compiler can apply optimizations at the level of CPU/platform-dependent calls. [/quote]

    I read these comments and it makes me realize you're all a bunch of C#/Java babies. None of you get close to the real the machine. Hence the rage expressed by Alex and myself. You are all ignorant goobers and I don't know how to make you less ignorant.

    At least the goober at the top, who lacked knowledge of longjmp bothered to benchmark it and LEARN something (he should've read the man page, it would've alleviated the need for a benchmark).

  • vinnybad (unregistered)

    I can see where goto can be useful...especially if the task is OS code...but long jump? where can a thing like this be useful?

    The guys who make these instructions are smart...not dumb...can anyone think of a case where it can help?

  • Anonymous (unregistered) in reply to Why is DailyWTF more like DailyWTFWTF
    Why is DailyWTF more like DailyWTFWTF:
    <snipped rubbish>
    No, you're a towel.
  • Bobbo (unregistered)
    ... most C developers have not brought it in their everyday.

    Might be a regional/country thing, but what does that even mean?

  • (cs) in reply to Why is DailyWTF more like DailyWTFWTF
    Why is DailyWTF more like DailyWTFWTF:
    JW:
    Library code is often faster than "user-optimized code", because the compiler can apply optimizations at the level of CPU/platform-dependent calls.

    I read these comments and it makes me realize you're all a bunch of C#/Java babies. None of you get close to the real the machine. Hence the rage expressed by Alex and myself. You are all ignorant goobers and I don't know how to make you less ignorant.

    Goober yerself, goober! I've written perfectly hand-scheduled superscalar assembly when I've wanted to push a machine to the metal, but I don't bother doing it for strcmp every time I start coding on a new platform because someone's already done the actual benchmarking and profiling for me. Work smarter, not harder - NIH syndrome is the real WTF!
  • captncraig (unregistered)

    I remember in my operating systems class in university we used longjmp to simulate things like hardware interrupts and context switches and stuff for our toy os. Outside of that, and for exception handling code, I have never seen longjmp used in any way that did anything useful except cause wtfs like this.

  • Dirk Diggler (unregistered)

    I've been using C since the mid 80's and I thought I was pretty good at it. Mostly mixed C and assembly in micro-controllers. If I ever knew about this command, I've forgotten about it. So guess I learned something today.

  • jackalope (unregistered)

    I like the text saving define of RETURN_NOTHING which is so much shorter than return;

  • Why is DailyWTF more like DailyWTFWTF (unregistered) in reply to DaveK
    DaveK:
    Why is DailyWTF more like DailyWTFWTF:
    JW:
    Library code is often faster than "user-optimized code", because the compiler can apply optimizations at the level of CPU/platform-dependent calls.

    I read these comments and it makes me realize you're all a bunch of C#/Java babies. None of you get close to the real the machine. Hence the rage expressed by Alex and myself. You are all ignorant goobers and I don't know how to make you less ignorant.

    Goober yerself, goober! I've written perfectly hand-scheduled superscalar assembly when I've wanted to push a machine to the metal, but I don't bother doing it for strcmp every time I start coding on a new platform because someone's already done the actual benchmarking and profiling for me. Work smarter, not harder - NIH syndrome is the real WTF!
    Did you squeeze that in between posting pictures of cats on anonymous message boards frequented exclusively by 14 year old school kids? Yeah, right. Once you leave school you'll learn some real skills, hopefully.
  • Anonymous Coward (unregistered)

    The longjmp must prevent some handy compiler optimizations. The compiler obviously writes faster code! Remember "premature optimization is the root of all evil" -Donald Knuth

  • (cs) in reply to vinnybad
    vinnybad:
    I can see where goto can be useful...especially if the task is OS code...but long jump? where can a thing like this be useful?
    It's hardly ever needed. Think about the cases where goto is helpful. One major use for goto is to abort the execution of a deeply nested loop in case of error or trivial success (e.g. search complete). What if the looping construct is so complex that it spans many functions? If the looping depth is dynamically determined, a function call is pretty much required--- either that or restructure the algorithm. In this case, longjmp() takes the place of goto for performing an early exit, when it's prohibitive to put early exit detection and support code around every function invocation. Other cases exist, but it's hard to fathom situations where it isn't necessary to perform frame-by-frame stack unwinding along the way.

    goto is problematic because it is difficult for multiple people to maintain (especially when they don't talk to each other or understand each other's code or comments), and this can lead to leaked resources and incorrect logic. longjmp() has the same problem, but the damage is not limited to a single function. It's a much bigger bomb. With careful planning and execution, it can demolish a hillside to build a road, but one screw-up will make it demolish the work crew instead.

  • (cs)

    When I saw longjmp the first thing I though of was the long jump module in Half-Life. Take notice kids, video games will rot your brain...

  • Shai (unregistered)

    Nobody has commented on the minor-WTF RETURN_NOTHING macro, so I thought I should fill the void.

  • WinGuy (unregistered) in reply to Why is DailyWTF more like DailyWTFWTF
    Why is DailyWTF more like DailyWTFWTF:
    At least the goober at the top, who lacked knowledge of longjmp bothered to benchmark it and LEARN something (he should've read the man page, it would've alleviated the need for a benchmark).

    What's a 'Man Page'? I can't find it on my Start menu.

  • (cs)

    Almost all 'C' compilers turn off optimization for the entire function, and sometimes the entire file, when it encounters a setjmp or longjmp. This is likely to be the major cause of the performance delay.

    Over 20 years ago, I worked on a project that had one longjmp/setjmp pair. The application was an interpreter and there was no 'C++' and no exceptions. Lacking exceptions, this was a good way of handling errors. That was then.

    Addendum (2009-01-12 12:24): For extra credit, post a working application of a longjmp that crosses threads. :-)

  • (cs) in reply to DaveK
    Oh, and TRWTF is to be still using that valuable antique from the prehistoric DOS era, getch(), in the 21st century. (Does it still overflow your application's stack by trying to read all the available console input events in one go when you've just cut and pasted a couple of hundred kB of text into the window?)

    Aren't you confusing this with gets()? I don't see the implied buffer overflow vulnerability in a function that reads a single character.

    If I remember correctly, (and I'm too lazy to check) getch is (sometimes?) an unbuffered getchar(), useful for real-time probing of user input.

  • (cs) in reply to YDD
    YDD:
    The real WTF is of course that the original developer failed to realize setjmp is horribly broken; he should have added a #DEFINE setjmp somewhere else, preferably in a file called reinventingwheel.h

    I think you mean, "reinventingwhl.h", or "reinvntwhl.h".

    Why programmers don't just go ahead and add that one extra vowel when talking naming, well, anything is beyond me. I've read papers and blogs on variable names and overhead, but I would rather be able to maintain code without worrying about creating another variable because I missed a vowel than "make the code faster" by leaving out a vowel or two.

  • (cs) in reply to Why is DailyWTF more like DailyWTFWTF

    [quote user="Why is DailyWTF more like DailyWTFWTF"][quote=JW] Library code is often faster than "user-optimized code", because the compiler can apply optimizations at the level of CPU/platform-dependent calls. [/quote]

    I read these comments and it makes me realize you're all a bunch of C#/Java babies. None of you get close to the real the machine.[/quote]

    Ok, I normally disagree when people say that, but we are talking about C here, you don't use it except when you need low level programming, so... I second that! No library will be as optimized and trimmed to your application as your own code.

    Now, about the WTF, and answering to Bob (238533). No optimizer can work well with goto statements. Most of optimizing theory simply stops being useful when you start using it.

  • (cs) in reply to Bobbo
    Bobbo:
    ... most C developers have not brought it in their everyday.

    Might be a regional/country thing, but what does that even mean?

    "Most C developers have not incorporated it into their everyday activities", I think.

  • Vidar (unregistered) in reply to Andy Goth
    Andy Goth:
    In this case, longjmp() takes the place of goto for performing an early exit, when it's prohibitive to put early exit detection and support code around every function invocation. Other cases exist, but it's hard to fathom situations where it isn't necessary to perform frame-by-frame stack unwinding along the way.

    Implementing co-operative threading / co-routines is one example (not that it'd be a smart thing to do if you expect people to understand your code) - you can achieve that by having a yield() function that keeps track of the current thread, and uses setjmp() to save its state in a struct, then schedules the next thread and longjmp()'s to it. Using signals you can even do preemptive threading that way, and there are (old) thread packages that actively worked that way.

    Not something that's very useful today when anything but toy OS's or tiny embedded systems will have proper thread support.

  • (cs)

    I am long-jumping out of these comments.

  • anonymous_coder() (unregistered)

    Wow - what's up with the snark? Lolcats are stupid, yes. It's okay to laugh at them. That's what they're for.

    But I'd never seen longjmp() before - it looks moderately useful in specific instances, but dear god, can it be abused.

  • (cs) in reply to rohypnol
    rohypnol:
    It's a lot easier to understand what's happening when the conditions are explained just before the loop, instead of having to find them yourself after your brain suffers from a neurological fracture(just coined that) after trying to process this statement "for(;;){" You could have used a while loop, it would have made a bit more sense... but NOOOOOO, people like you have to use for(;;){ instead of while(i<sales->count){
    No, "for(;;)" is used for endless looks because "while (1)" makes the MSVC compiler complain "warning C4127: conditional expression is constant."
  • grg (unregistered)

    setjmp/longjmp can be immensely useful in some cases where you're nested very deeply and need to back out say ten levels. Like when you're in a compiler and detect an error when reading the next character. Without an inter-function goto you'd have to pass an error code back up ten levels, checking for it and returning yet another error code up many times over.

    But using it within a function is bizarre. A plain "goto" would do. And be much faster. setjmp/longjmp have to save and restore all the volatile machine registers, a slow operation on some CPUs.

  • Anonymous Cowherd (unregistered) in reply to JW
    JW:
    However: a linear algorithm ALWAYS outperforms a quadratic one, and compilers don't change algorithms ;)
    Uhm. No. A linear algorithm outperforms a quadratic algorithm for sufficiently large n. Sufficiently large n is highly variable. If you're sorting less than ten items, for example, the fastest algorithm is generally one of the old, primitive quadratic versions. Many good 'qsort' implementations don't use qsort at all once they get down to dealing with small ranges of items.

Leave a comment on “Longjmp - FOR SPEED!!!”

Log In or post as a guest

Replying to comment #:

« Return to Article