std::vector::operator[]
должен быть довольно эффективным, однако компилятор должен быть параноиком, и для каждого вызова функции он должен предполагать, что вектор мог быть перемещен в другое место в памяти.
Например, в этомcode
for (int i=0,n=v.size(); i<n; i++)
{
total += v[i] + foo();
}
, если код foo
заранее неизвестен, компилятор вынужден каждый раз перезагружать адрес начала вектора, поскольку вектор мог быть перераспределен как следствие кода внутри foo()
.
Если вы точно знаете, что вектор не будет перемещен в памяти или перераспределен, то вы можете выделить эту операцию поиска с помощью чего-то вроде
double *vptr = &v[0]; // Address of first element
for (int i=0,n=v.size(); i<n; i++)
{
total += vptr[i] + foo();
}
с помощью этого подходаодна операция поиска в памяти может быть сохранена (vptr
может оказаться в регистре для всего цикла).
Также другой причиной неэффективности может быть очистка кэша.Чтобы увидеть, является ли это проблемой, простой способ состоит в том, чтобы просто перераспределить ваши векторы на некоторое неравное количество элементов.
Причина в том, что из-за того, как работает кэширование, если у вас много векторов, например, с 4096 элементамииз них случится, что в адресе будут одни и те же младшие биты, и вы можете потерять большую скорость из-за недействительности строк кэша.Например, этот цикл на моем ПК
std::vector<double> v1(n), v2(n), v3(n), v4(n), v5(n);
for (int i=0; i<1000000; i++)
for (int j=0; j<1000; j++)
{
v1[j] = v2[j] + v3[j];
v2[j] = v3[j] + v4[j];
v3[j] = v4[j] + v5[j];
v4[j] = v5[j] + v1[j];
v5[j] = v1[j] + v2[j];
}
выполняется примерно за 8,1 секунды, если n == 8191
, и за 3,2 секунды, если n == 10000
.Обратите внимание, что внутренний цикл всегда от 0 до 999, независимо от значения n
;отличается только адрес памяти.
В зависимости от процессора / архитектуры я наблюдал даже 10-кратное замедление из-за очистки кэша.