В следующем коде:
std::vector<int> var;
for (int i = 0; i < var.size(); i++);
Вызывается ли функция-член size()
для каждой итерации цикла или только один раз?
В следующем коде:
std::vector<int> var;
for (int i = 0; i < var.size(); i++);
Вызывается ли функция-член size()
для каждой итерации цикла или только один раз?
В теории это называется каждый раз, поскольку цикл for:
for(initialization; condition; increment)
body;
расширяется до значения
{
initialization;
while(condition)
{
body;
increment;
}
}
(обратите внимание на фигурные скобки, поскольку инициализация уже находится во внутренней области)
На практике, если компилятор понимает, что часть вашего условия инвариантна по всей длительности цикла, и у нее нет побочных эффектов, она может быть достаточно умна, чтобы переместить ее. Это обычно делается с помощью strlen
и таких вещей (что компилятор хорошо знает) в циклах, где его аргумент не написан.
Однако следует отметить, что это последнее условие не всегда тривиально доказать; в общем, легко, если контейнер локален для функции и никогда не передается внешним функциям; если контейнер не является локальным (например, он передается по ссылке, даже если он const
), а тело цикла содержит вызовы для других функций, компилятор часто должен предположить, что такие функции могут его изменить, тем самым блокируя подъем длины расчет.
Выполнение этой оптимизации вручную является достойным, если вы знаете, что часть вашего условия оценивается "дорого" (и такого условия обычно нет, поскольку обычно это сводится к вычитанию указателя, который почти наверняка встроен).
Изменить:, как говорили другие, в общем случае с контейнерами лучше использовать итераторы, но для vector
это не так важно, потому что произвольный доступ к элементам через operator[]
гарантированно будет O (1); на самом деле с векторами обычно это сумма указателя (векторная база + индекс) и разыменование с увеличением указателя (предшествующий элемент + 1) и разыменование итераторов. Поскольку целевой адрес все тот же, я не думаю, что вы можете получить что-то от итераторов с точки зрения локальности кеша (и даже если это так, если вы не идете по массивам в сложных циклах, вы не должны даже заметить такие вид улучшений).
Вместо списков и других контейнеров использование итераторов вместо случайного доступа может быть очень важным, поскольку использование произвольного доступа может означать прогулку каждый раз, когда список, а приращение итератора - просто разыменование указателя.
Он называется "каждый раз", но я помещаю вызов в кавычки, потому что это, вероятно, всего лишь встроенный вызов метода, поэтому вам не нужно беспокоиться о его производительности.
Почему бы не использовать vector<int>::iterator
вместо этого?
Функция-член size()
вызывается каждый раз, но это была бы очень плохая реализация, которая не была бы встроена в нее, и странная, где это не был бы простой доступ к фиксированному дате или вычитанию два указателя.
В любом случае, вы не должны беспокоиться о таких мелочах, пока не профилируете свое приложение и не узнаете, что это узкое место.
Однако вы должны обратить внимание на следующее:
std::vector<T>::size_type
.i++
может быть медленнее, чем ++i
.Следовательно, цикл должен быть:
for(vector<int>::size_type i=0; i<var.size(); ++i)
...
Он должен вызываться каждый раз, потому что size() может каждый раз возвращать другое значение.
Поэтому нет большого выбора, который он просто должен быть.
Как говорили другие
поверх которого
I подумайте, что если компилятор может окончательно вывести, что переменная var
не изменяется внутри "тела цикла"
for(int i=0; i< var.size();i++) {
// loop body
}
то вышеуказанное может быть перенесено на нечто эквивалентное
const size_t var_size = var.size();
for( int i = 0; i < var_size; i++ ) {
// loop body
}
Однако я не совсем уверен, поэтому комментарии приветствуются:)
Кроме того,
В большинстве ситуаций функция члена size()
встроена, поэтому проблема не вызывает беспокойства
Возможно, проблема связана с end()
, которая всегда используется для циклизации на основе итератора, т.е. it != container.end()
Пожалуйста, используйте size_t
или vector<int>::size_type
для типа i
[См. комментарий к Стив Джессопу ниже.]
Но это можно сделать таким образом (при условии, что этот цикл намеревается читать или писать только без изменения размера вектора):
for(vector<int>::size_type i=0, size = var.size(); i < size; ++i)
{
//do something
}
В вышеприведенном цикле у вас есть только один вызов размера независимо от того, какой размер был встроен или нет.
как говорили другие, компилятор должен решить, что делать с написанным кодом. Ключом является то, что он называется каждый раз. Но если вы хотите повысить производительность, лучше написать код с некоторыми соображениями. Ваш случай - один из них, есть и другие, как разница между этими двумя фрагментами кода:
for (int i = 0 ; i < n ; ++i)
{
for ( int j = 0 ; j < n ; ++j)
printf("%d ", arr[i][j]);
printf("\n");
}
for (int j = 0 ; j < n ; ++j)
{
for ( int i = 0 ; i < n ; ++i)
printf("%d ", arr[i][j]);
printf("\n");
}
Разница в том, что первая не будет слишком сильно изменять страницу разметки на ссылки, но другая будет исчерпывать ваш кеш и TLB и другие вещи.
Также встроенный не поможет! потому что порядок вызывающей функции будет оставаться равным n (размер вектора) раз. Однако это помогает в некоторых местах, но лучше всего переписать свой код.
Но! если вы хотите, чтобы компилятор делал это с оптимизацией по вашему коду, NEVER put volatile, например:
for(volatile int i = 0 ; i < 100; ++i)
Это предотвращает оптимизацию компилятора. Если вам нужен другой совет для использования, используйте регистр вместо volatile.
for(register int i = 0 ; i < 100; ++i)
Компилятор попытается не переместить я из регистров CPU в ОЗУ. Это не гарантирует, что он может это сделать, но он будет делать это лучше всего;)
Протестировал его на 900 000 итераций. Его время составляет 43 секунды для предварительно рассчитанного размера и 42 секунды для использования вызова size().
Если вы гарантированно не изменили размер вектора в цикле, лучше использовать предварительно рассчитанный размер, в противном случае выбора нет и необходимо использовать size().
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> v;
for (int i = 0; i < 30000; i++)
v.push_back(i);
const size_t v_size = v.size();
for(int i = 0; i < v_size; i++)
for(int j = 0; j < v_size; j++)
cout << "";
//for(int i = 0; i < v.size(); i++)
// for(int j = 0; j < v.size(); j++)
// cout << "";
}