3

Does it save time if the value is stored first and then used?
For example;

while(i<strlen(s))
 //code

and

int n = strlen(s);
while(i<n)
 //code

Is the time complexity lower for the second method as the return value of the function is first stored and then used,thus avoiding calling the function every time it enters the loop?

gsamaras
  • 71,951
  • 46
  • 188
  • 305
yobro97
  • 1,125
  • 8
  • 23
  • This is only a comment since I'm not really sure, but yes. The second option is more efficient for the exact reason you mentioned. But, and this is the part where I'm not sure, I'm pretty sure most compilers know how to make these optimizations in the code, thus making both options pretty much the same. – Gershon Papi Jul 31 '16 at 05:44
  • See [this](http://stackoverflow.com/questions/3388029/is-using-strlen-in-the-loop-condition-slower-than-just-checking-for-the-null-c) answer where you can read the explanation on why `strlen` is O(N). – David Gomes Jul 31 '16 at 05:50
  • 4 answers were posted, you should comment/accept one... – gsamaras Aug 05 '16 at 22:20

2 Answers2

6

In the first example, assuming i is initialized appropriately (to 0 probably) and is incremented in the loop body, you will count the number of characters in the string s each time around the loop, so if there are N characters in the string, you'll do N2 comparisons (because strlen() compares each character in the string against '\0', and you'll do the explicit loop N times, and the loop inside strlen() N times each call), making the code O(N2).

In the second example, again assuming i is initialized and incremented in the loop body, you will count the number of characters once, so you'll do N comparisons, making the code O(N).

Whether the optimizer can safely optimize the calls to strlen() out of the loop control depends on what is in the body of the loop. If the compiler can see everything, or can determine that the called code cannot legitimately modify the string s, then it may be able to move the call to strlen() out of the loop control, effectively giving the O(N) performance of the second loop. OTOH, if the body of the loop calls a function and passes a non-const pointer to s to the function, then the compiler may not be able to assume that the string is unmodified and may have to call strlen() on each iteration.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • The compiler will take care of the multiple function calls. – brianxautumn Jul 31 '16 at 05:46
  • 2
    @brianxautumn: The compiler will _sometimes_ take care of multiple function calls. It will do this only if it can _prove_ that the string cannot be modified in the loop. If it's not sure, you end up with multiple function calls. – Mooing Duck Jul 31 '16 at 05:52
  • You still would not up the complexity to n^2 unless there is a nested loop. If you have n function calls, the complexity is still O(n) – brianxautumn Jul 31 '16 at 05:54
  • 1
    @brianxautumn: there are N calls to the `strlen()` function and each call to it operates in O(N) time, making the composite performance O(N^2) if the optimizer decides it can't optimize the function call out of the loop control. – Jonathan Leffler Jul 31 '16 at 05:57
  • 1
    @brianxautumn: Depends on what you're measuring. As for _function calls_, you're right. It's O(n). As for _comparisons_, Johnathan is right, it's O(n^2). As for _memory writes_, it's O(1). Everyone always forgets you have to actually say what n is :/ – Mooing Duck Jul 31 '16 at 05:57
2

A clever compiler will do the second for you. So don't worry about this and don't be a victim of premature optimization!


You want the second one, because the length has a fixed value and you will avoid the call of the function per loop.


But will the compiler be clever enough to do so? Only if you tell him too! See the examples below:

First, I am compiling without optimization flags:

C02QT2UBFVH6-lm:~ gsamaras$ gcc -Wall nostore.c
C02QT2UBFVH6-lm:~ gsamaras$ ./a.out
That took 27.701602000 seconds wall clock time.
C02QT2UBFVH6-lm:~ gsamaras$ gcc -Wall store.c
C02QT2UBFVH6-lm:~ gsamaras$ ./a.out
That took 12.100676000 seconds wall clock time.

but what happens if we use an optimization flag?

C02QT2UBFVH6-lm:~ gsamaras$ gcc -O3 -Wall nostore.c
C02QT2UBFVH6-lm:~ gsamaras$ ./a.out
That took 0.004949000 seconds wall clock time.
C02QT2UBFVH6-lm:~ gsamaras$ gcc -O3 -Wall store.c
C02QT2UBFVH6-lm:~ gsamaras$ ./a.out
That took 0.005283000 seconds wall clock time.

Compiler did it better than me! Compilers are not that dumb nowadays, as some people mistakenly think.

For timing I used the Nomimal Animal's approach from my Time measurements.

Here is the cod I executed:

char* str = "What's faster? What's a premature optimization? Am I a vi$
int i = INT_MAX;

// int n = strlen(str);
// while(n);
while(strlen(str)) {
    // do some random work so that you don't get 0 timing
    int a = 3;
    double pi = 3.14;
    a /= pi;
    a = a % (i + 1);
    // break when 'i' is 0
    if(!i)
        break;
    --i;
}

But are optimization flags that important in benchmarking? YES, check this question: Poor performance of stl list on vs2015 while deleting nodes which contain iterator to self's position in list

Community
  • 1
  • 1
gsamaras
  • 71,951
  • 46
  • 188
  • 305
  • 1
    Clever pun but the question is, is the compiler as clever. – o_weisman Jul 31 '16 at 05:44
  • 1
    You're making the assumption that the length has a fixed length, but there's nothing in the question backing that assumption. – Mooing Duck Jul 31 '16 at 05:58
  • @o_weisman aha, I will update! Mooing Duck, well not in human language, but in C code this assumption is done. But I asked myself the same thing before posting this answer, so I feel you. – gsamaras Jul 31 '16 at 06:01
  • 2
    @Mooing Duck The OP making the distinction implies that s is constant for the body of the loop – Tibrogargan Jul 31 '16 at 06:02