5

如果有一个名为 arr 的向量包含大量数据,我将打印该向量中的所有值。我要么使用:

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

或以这种方式实现它:

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

在我看来,第一种实现方式会将向量的大小提取到缓存中,从而在第一次未命中后使条件更快。第二个实现呢?是不是比较慢?每次满足条件时系统都会调用size()方法吗?

编辑:假设它使用 C++。

4

5 回答 5

6

概括为具有任意主体的循环,您给出的两个变体之间存在一个关键区别:如果arr循环期间的大小发生变化怎么办?

对于第二种情况,如果编译器可以假设它没有改变,那么它可以优化循环,因此arr.size()只调用一次,生成的代码与第一种情况大致相同。

但是如果循环体调用了一个外部函数(很有可能),那么它就不能再做这个假设了,必须检查arr.size()每个循环迭代。

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.

于 2012-04-30T10:20:22.307 回答
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.

于 2012-04-30T10:25:57.060 回答
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, ","));
于 2012-04-30T10:35:56.493 回答
0

第二种选择更好:

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

循环结束后,变量 i 将被释放。arr.size() 将在循环初始化后计算。不会分配内存来存储 arr_size 变量。

于 2012-04-30T10:13:10.963 回答
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.

于 2019-10-22T04:59:31.343 回答