Dismiss Notice
Join Physics Forums Today!
The friendliest, high quality science and math community on the planet! Everyone who loves science is here!

Conditionally performing a procedure inside a function

  1. Apr 22, 2014 #1
    Suppose I have a function that gets called and performs a procedure provided that a certain condition is met. One way to do it is, of course, like the following real example from something I'm working on in JavaScript right now.

    Code (Text):

    if (window.File && window.FileReader && window.FileList && window.Blob) {
      //do your stuff!
    } else {
      alert('The File APIs are not fully supported by your browser.');
    }
     
    That's how they do it here http://www.htmlgoodies.com/beyond/j...e-javascript-filereader.html#fbid=gN9gIXxlV_E and of course it's perfectly valid.

    However, to me, it seems weird to have it written that way, because the way I think about things like this is, "Check that I'm not prevented from doing X, and if I am, make a note about it, otherwise continue onto doing X." So I prefer to use an early
    Code (Text):
    return
    statement in languages that support it, such as JavaScript. I do this:

    Code (Text):

    if (!window.File || !window.FileReader || !window.FileList || !window.Blob)
    {
      alert('The File APIs are not fully supported by your browser.');
      return;
    }
    else
    {
       // do the procedure
    }
     
    I'm just wondering whether one is definitely better than another or whether my mind is starting to become poisoned with religion after reading too many threads here haha. I also realize that the first piece of code is faster because
    Code (Text):
    window.File && window.FileReader && window.FileList && window.Blob
    is, on average, executed faster than
    Code (Text):
    !window.File || !window.FileReader || !window.FileList || !window.Blob
    .

    Should I even be asking this question or am I becoming overly concerned about things that don't matter?
     
  2. jcsd
  3. Apr 22, 2014 #2

    harborsparrow

    User Avatar
    Gold Member

    if (A || B || C || D)
    {
    // do something
    }
    else
    {
    // do something else
    }
    return;

    The above is risky in some languages, because if condition A or B fails (just for example), C or D will never be evaluated, which in some cases is not the desired behavior.

    Also, unless you have objectively measured performance of a code structure executing in every browser, I wouldn't be too sure about what executes faster than what else. Even if it does, is it more significant than readable code?
     
  4. Apr 22, 2014 #3
    I thought that was the case with && in Java, but I didn't know it also happened with || in some languages. A bug like that in anyone's code could easily bring hours of headaches.
     
  5. Apr 22, 2014 #4

    D H

    User Avatar
    Staff Emeritus
    Science Advisor

    That both && and || (or their equivalent in some other language) are short circuit operators is true in *most* languages. Some few provide both eager and short-circuit versions (e.g., and versus and then in Ada), and one (Fortran) leaves the decision as to whether the operation is eager or short circuit up to the compiler.

    Note that C++ boolean expressions are short circuit only with respect to built-in types. Expressions involving overloaded operator&& or operator|| are not short circuit. (You should never overload those two operators, or the ternary operator, for this very reason).


    Jamin2112 - You worry to much about micro optimizations. Donald Knuth wrote "We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%."

    Is this boolean expression part of the critical 3% of your program?
     
  6. Apr 23, 2014 #5

    AlephZero

    User Avatar
    Science Advisor
    Homework Helper

    If you write code that only "works" because the logical tests in your IF statements perform the correct side effects, you deserve everything you get IMHO.
     
  7. Apr 23, 2014 #6

    AlephZero

    User Avatar
    Science Advisor
    Homework Helper

    I once worked on developing a code over several years, which demonstrated that fact big-time.

    The first version spent about 95% of its execution time in just one small loop, which was three lines of code out of a total of about 200,000.

    On average, we speeded up the run time of the complete code by a factor of about 2x per year, for several years. The final version ran more than 100x faster than the original. Those improvements were scaled to the same computer hardware - the actual speed up was close to 1000x including the hardware performance improvements as well.

    So what did we do to those 3 lines of code to make the program run 100 times faster? NOTHING. What we did was improve the high level algorithms to solve the complete problem, so those lines of code were called less often.
     
  8. Apr 23, 2014 #7

    D H

    User Avatar
    Staff Emeritus
    Science Advisor

    Very similar experience. A dozen lines of code out of a similarly sized code base was responsible for 90% of the CPU usage. Those dozen or lines were in a spherical harmonics expansion function. The code looked something like this:
    Code (Text):

    for (int ii = 2; ii <= degree; ++ii) {
       // Lots of code elided
       for (int jj = 1; jj <= order; ++jj) {
          // Lots of code elided
          sum_gamma += (jj+ii+1) * Pnm[ii][jj] *
                       (Cnm[ii][jj] * C_tilde[jj] + S_nm[ii][jj] * S_tilde[jj]);
          // Lots of code elided
       }
       // Lots of code elided
    }
    The code had to calculate the potential, it's gradient, and the Hessian. Some of our users insisted on compiling unoptimized because a bug traced to an optimizing compiling supposedly crashed a satellite some twenty years ago.

    The Pnm, Cnm, Snm, and such are data members. When compiled unoptimized, we could see the assembly code extracting the value Pnm[ii][jj] as if we had written *(*((*this).Pnm+ii)+jj). We were going through three pointers! We cached those things so that only one indirection was performed in the inner loop. This also helped with the performance when the code was compiled optimized. But that wasn't enough. The code was still too slow.

    Next problem: There were lots of common expressions that could not possibly have been identified as such and calculated once when the code was compiled unoptimized. Even if we did enable optimization, we saw that the compiler wasn't seeing a lot of those common expressions as such, probably because of worries about aliasing. So we manually collected those common expressions.

    And it was still too slow. The cause? It turned out the remaining drain on the CPU involved multiplying an integer and a double. There are lots of such multiplications in a spherical harmonics expansion. Converting an integer to a double turns out to be *expensive*. The solution was to build an int_to_double array at instance initialization time. We knew the minimum (zero) and maximum values to be converted at that time. Doing a lookup in an array that maps 0 to 0.0, 1 to 1.0, etc., turned out to be a much, much faster conversion than the incredible amount of bit mangling needed to convert an integer to a double.

    Did we carry this notion forward to other places where we multiplied an integer and a double? No. It makes the code look ugly and harder to understand. We did it in that spherical harmonics code because we had to. It would have been a case of premature optimization anywhere else.
     
    Last edited: Apr 23, 2014
  9. Apr 23, 2014 #8

    AlephZero

    User Avatar
    Science Advisor
    Homework Helper

    Similar but different. In my case the loop was effectively just a BLAS level 1 "AXPY" operation. Replacing it with a call to a BLAS routine did nothing except add the overhead of a function call.

    But attacking the complete problem from the other end, and figuring out how to make a solution algorithm that converged in 20 iterations (taking about an hour each) converge in 10, gave the first fiactor of 2. The others were from similar high level changes, but too complicated to explain in one paragraph.

    After somebody on the team had figured out how to get one of those savings, the consensus was usually "OK, that's the end point, there's nothing else we can do here" - until 6 months later somebody said "hey, what if we tried .......". Insight develops at its own pace. You can't make it happen by putting milestones on a project management chart.
     
  10. Apr 23, 2014 #9

    D H

    User Avatar
    Staff Emeritus
    Science Advisor

    True. That small chunk of our code is now ugly as sin. That's almost always the inevitable result of programming for utmost efficiency. Your code is still nice and clean. That is oftentimes a more common result from seeing some hoggish function. You take one look at the code and go :eek: : "They're using bubble sort to sort a list containing thousands of elements!" or "They're calling new and delete inside a triply nested loop!".


    That you can't make insights happen on demand doesn't seem to stop project management from expecting 1.3 deep insights per developer per month.
     
  11. Apr 23, 2014 #10

    Borek

    User Avatar

    Staff: Mentor

    Rule number one: code that is never executed doesn't take processor time.
     
  12. Apr 23, 2014 #11

    Borek

    User Avatar

    Staff: Mentor

    I think I am going to hang it on the wall here.
     
Know someone interested in this topic? Share this thread via Reddit, Google+, Twitter, or Facebook




Similar Discussions: Conditionally performing a procedure inside a function
Loading...