c++ 对于循环,在循环外检查向量的大小是否更快?[重复]

ne5o7dgx  于 12个月前  发布在  其他
关注(0)|答案(5)|浏览(94)

此问题在此处已有答案

Is the condition of a loop re-evaluated each iteration? [duplicate](3个答案)
In a "i < vector.size()" loop condition, is size() called each iteration?(10个答案)
2天前关闭。
我的问题是关于时间优化的。通过执行以下操作,for循环会更快吗?

std::vector<int> myVec = {0,1,2,3};
for(int i = 0; i < myVec.size(); i++){}

字符串
还是说,最好的做法是事先计算好规模?

std::vector<int> myVec = {0,1,2,3};
int myVecSize = myVec.size();
for(int i = 0; i < myVecSize ; i++){}


我在这里不仅想知道纯时间执行,而且想知道它是否会以某种方式导致一些问题

voj3qocg

voj3qocg1#

一般来说,编译器需要在循环的每次迭代中调用size函数。C++没有函数纯度的概念,除非编译器可以看到函数定义,否则它不能假设函数没有副作用(如日志语句)。
现在一个stl向量被定义在一个头文件中,所以编译器确实有可见性。现在考虑到size的简单性,它很可能被内联和优化掉。

kzipqqlq

kzipqqlq2#

这一点都不重要。优化编译器会删除这两三行代码,它们什么也不做。
说真的。如果编译器可以推断出一个容器在循环中没有改变,它会做你手动做的优化。为了帮助编译器应用优化,你甚至可以声明一个容器是常量(例如vector):

const std::vector<int> myVec = {0,1,2,3};

字符串

n3schb8v

n3schb8v3#

一般来说,答案取决于你的看法,但是,假设你在一个标准容器的元素上循环(比如std::vector<int>),那么它也取决于循环的作用。
如果循环做了任何改变vector大小的事情,那么在每次迭代时都需要将索引与大小进行比较,因为重复一个vector会使所有迭代器无效,并且可能导致索引在不再有效时被使用(导致未定义行为)。
作为一个粗略的规则,如果你没有在循环体中调用向量,使用迭代器通常比使用索引更好。使用索引需要向量的operator[]()函数在每次调用时找到感兴趣的元素,而迭代器提供了对特定元素的更直接的访问

// case where vector is not resized

 for (std::vector<int>::iterator i = myVec.begin(), end = myVec.end();  i != end; ++i)
 {
      //    do something with the iterator i
 }

字符串
在C++11和更高版本中,如果循环体没有调整容器的大小,只需使用一个基于范围的for循环,该循环适用于每个元素

for (auto &element : myVec)
{
     // do something with element
}

bq3bfh9z

bq3bfh9z4#

根据我的经验,我会使用第一个选项,而不使用myVecSize变量,因为它是多余的,使代码不那么可读,不必要的更长。此外,我认为上面提到的这些原因在编码中比你可能永远不会注意到的执行时间差更重要。

g2ieeal7

g2ieeal75#

像往常一样,这取决于:-)
如果vector是一个局部变量,那么一个足够聪明的编译器应该能够推断出大小在循环体中不会改变,即使调用了具有隐藏实现的函数。
如果向量尚未在本地创建,(例如,它已经通过引用传递给了函数-无论是否是const引用),那么编译器必须是偏执狂,并且在循环体中无法完全理解任何内容(即任何不可能被内联到基本操作的东西)理论上可以改变向量的大小,因此生成的代码将不得不在每次迭代时检查可能改变的大小和重定位。
请注意,即使在本地创建了vector的情况下,编译器也必须假设任何被告知vector对象地址的外部函数可能会立即甚至在将来对该对象进行变异。

void foo() {
    std::vector<int> myvec{1,2,3,4};
    printf("%p\n", (void *)&myvec); // Address of the vector object
    for (int i=0; i<int(myvec.size()); i++) {
        printf("Hello, world (%i)\n", int(myvec.size()));
        printf("Hello again (%i)\n", int(myvec.size()));
    }
}

字符串
编译器不能假设循环将被执行4次,甚至不能假设在第一次和第二次调用printf函数之间大小没有改变。原因是对象的地址已经在循环外的调用中传递给全局外部函数,这个函数可能已经改变了对象,甚至可以存储这个地址,以便在以后的调用中改变它。
在上面的情况下,编译器可能会特别标记printf,以知道这是一个“行为良好”的函数,不会做这样的事情.但如果不是printf,这是一个通用的用户定义的C++函数,其主体在编译时是未知的,那么必须使用“偏执模式”。
不幸的是,没有可移植的C++方法来声明一个函数是无副作用的,并向编译器保证这种糟糕的事情不会发生。

相关问题