5

If there is a vector called arr which contains a huge number of data, and I am to print all the values in that vector. I would either use:

int arr_size = arr.size();
for(int i=0; i<arr_size; ++i) {//print the values}

or implement it this way:

for(int i=0; i<arr.size(); ++i) {//print the values}

In my opinion, the first way of implementation will fetch the size of the vector into the cache and hence make the condition faster after the first miss. What about the second implementation? Is it slower? Will the system call the size() method every time when it meet the condition?

Edit: Let's say it is using C++.

xiaoyao
  • 254
  • 3
  • 10
  • 1
    The language you're using might have an influence on whether this matters at all or not. – Mat Apr 30 '12 at 10:01
  • @Mat, and what about compiler? – nullpotent Apr 30 '12 at 10:01
  • @AljoshaBre: compiler (and compiler options) matter less than the language (obviously a really bad compiler and or very sophisticated debug options will influence this) – Mat Apr 30 '12 at 10:04
  • If you really want to know then look at the assembly generated by your compiler (with optimizations on). My opinion is to use the second option, it's simpler and unlikely to be a performance bottleneck. – Blastfurnace Apr 30 '12 at 10:17
  • C++ or C#? And which collection class? In C# the answer is likely different for array and `List`. – CodesInChaos Apr 30 '12 at 10:21
  • If it is C#, use `foreach` and let the compiler do the thinking. – Mathias Schwarz Apr 30 '12 at 10:21
  • @xiaoyao Please, correct your question and clearly specify which programming language it concerns. If it concerns both, make it clear too. Now, it looks like your tags are mismatched. – mloskot Apr 30 '12 at 10:31
  • You could always do: for (int size = arr.size(), i = 0; i < size; ++i) – Matthew Watson Apr 30 '12 at 10:34
  • Ok guys, I made it clearer. Let's talk about this situation in c++. I didn't add both c++ and c# tags to confuse yall in the first place. – xiaoyao Apr 30 '12 at 10:38
  • @xiaoyao Thanks for correcting. Now, check the answer given by Saeed Amiri and learn how complexity of size() operation may depend on container. – mloskot Apr 30 '12 at 10:41
  • possible duplicate of [Why use iterators instead of array indices?](http://stackoverflow.com/questions/131241/why-use-iterators-instead-of-array-indices) – johnsyweb Apr 30 '12 at 10:51

5 Answers5

6

Generalising for a loop with arbirary body, there is one critical difference between the two variants you have given: what if the size of arr changes during the loop?

For the second case, if the compiler can assume that it doesn't change then it can optimize the loop so arr.size() is only called once, and the generated code becomes about the same as for the first case.

But if the loop body so much as calls an external function (highly likely) then it can't make this assumption anymore and must check arr.size() every loop iteration.

Having said that arr.size() will probably work out to a simple structure member access which would be no slower than storing the value in a local variable, so there isn't much to be gained out of using the first variant anyway. Unless arr is a pointer or reference in which case it's an indirect and then an access, so the first version would be a litte faster.

If it's a particularly commonly run loop and you have to compile without optimisation for some reason, you might want to go with the first case to speed it up.

But then, the readability of the code is not impaired much at all by that one extra line is it?

To sum up, I would go with variant 2 unless the loop was an inner loop that had to be tight, in which case I would go with variant 1 just to be sure.

And of course if elements are added to arr inside the loop and the loop needs to cover those elements then only the second variant will be correct.

Michael Slade
  • 13,802
  • 2
  • 39
  • 44
  • Also, if 'arr' is passed into the function, the compiler can't know if another thread is changing it, so assuming .size() doesn't change would be unsafe. – Matthew Watson Apr 30 '12 at 10:28
  • 2
    @MatthewWatson If another thread is changing things, and there are no locks or other synchronization, it is undefined behavior. So the compiler can assume that this is not the case. – James Kanze Apr 30 '12 at 10:38
4

I would suggest if is possible for you use iterators instead of indices. e.g:

In c++:

for( const_iterator it = arr.begin(), ite = arr.end();
     it != ite; ++it)
{
 ....
}

In c#:

foreach(var item in arr){....}

One of the advantages in c++ is when you have list instead of vector, in vector size() is O(1) but in list it's O(n). Also this prevents from situations which calls, arr.Size() too many times.

General advantage is more readable codes in both c# and c++ but still there are some situations that you can't use them, and you should use indices.

Saeed Amiri
  • 22,252
  • 5
  • 45
  • 83
  • This is actually a nice way using the iterator! Thank you for the suggestion. – xiaoyao Apr 30 '12 at 10:40
  • Is it legal to make two unrelated declarations that way? Shouldn't that be: `for (const_iterator it = arr.begin(), ite = arr.end(); ...)`? – jamesdlin Apr 30 '12 at 10:57
1

Second option is better:

for(int i=0; i<arr.size(); ++i) {//print the values}

the variable i will be release after the loop is over. arr.size() will be calculated once the loop is initialized. Memory will be not assigned to store arr_size variable.

Romil Kumar Jain
  • 20,239
  • 9
  • 63
  • 92
  • 1
    Or, if the c# tag means anything, you could use foreach. – Christoph Grimmer Apr 30 '12 at 10:17
  • Looks like C++ to me. He called it a "vector" and "size()" starts with a lowercase letter and its a method, not a property. And a variable name contains an underscore. ;) – Matthew Watson Apr 30 '12 at 10:21
  • The performance question also applies to both language with same syntax. – Romil Kumar Jain Apr 30 '12 at 10:22
  • 1
    -1: Second option is better, but for a different reason: it's more readable. Depending on the compiler, it will probably call size() for each iteration and will therefore be a tiny bit slower. – Henrik Apr 30 '12 at 10:22
  • @Romil: Can't use "foreach" unless it's C#, that's why I mentioned it. – Matthew Watson Apr 30 '12 at 10:26
  • @MatthewWatson, you can, see my answer. – Saeed Amiri Apr 30 '12 at 10:27
  • Sorry, I don't understand - you are saying you can use "foreach" in C++? As in "foreach (int x in y)"... – Matthew Watson Apr 30 '12 at 10:33
  • @MatthewWatson, foreach in c# is just usage of [iterator pattern](http://en.wikipedia.org/wiki/Iterator_pattern#C.2B.2B), also we have this in c++ (iterator pattern implemented built-in for vectors and lists via pointers), but yes their syntax is different (you shouldn't expect both c# and c++ have a same syntax). – Saeed Amiri Apr 30 '12 at 10:37
1

If you have a modern enough compiler:

for (auto i: arr)
{
    //print the values
}

Or better still... avoid rolling your own for-loop (this will work with pre-C++11 compilers, too):

std::copy(arr.begin(), arr.end(), std::ostream_iterator<int>(std::cout, ","));
johnsyweb
  • 136,902
  • 23
  • 188
  • 247
0

I did a few tests, and caching the values seem to be [marginally] better than evaluating the condition with every iteration.

Here are my test results when using std::vector.

Code: Testing for-loop caching with std::vector Timing results (4 runs for evaluated and cached runs each):

Evaluated:
- Compilation time: 2,16 sec, absolute running time: 10,94 sec, absolute service time: 13,11 sec
- Compilation time: 1,76 sec, absolute running time: 9,98 sec, absolute service time: 11,75 sec
- Compilation time: 1,76 sec, absolute running time: 10,11 sec, absolute service time: 11,88 sec
- Compilation time: 1,91 sec, absolute running time: 10,62 sec, absolute service time: 12,53 sec

Cached:
 - Compilation time: 1,84 sec, absolute running time: 9,55 sec, absolute
   service time: 11,39 sec
 - Compilation time: 1,75 sec, absolute running
   time: 9,85 sec, absolute service time: 11,61 sec
 - Compilation time:
   1,83 sec, absolute running time: 9,41 sec, absolute service time:
   11,25 sec
 - Compilation time: 1,86 sec, absolute running time: 9,87
   sec, absolute service time: 11,73 sec

Here are my test results when using std::list.

Code: Testing for-loop caching with std::list Timing results (2 runs for evaluated and cached runs each):

Evaluated:
 - Compilation time: 1,9 sec, absolute running time: 17,94 sec, absolute service time: 19,84 sec
 - Compilation time: 1,84 sec, absolute running time: 17,52 sec, absolute service time: 19,36 sec

Cached:
 - Compilation time: 1,81 sec, absolute running time: 17,74 sec, absolute service time: 19,56 sec
 - Compilation time: 1,92 sec, absolute running time: 17,29 sec, absolute service time: 19,22 sec

The absolute running time is what I used as a comparison metric. Caching the condition is consistently (yet marginally) better than evaluating the condition.