非常类似于this question,除了代替vector<int>
我有vector<struct myType>
。
如果我想为向量中的每个元素重置(或者就此而言,设置为某个值)myType.myVar
,那么最有效的方法是什么?
现在我正在迭代:
for(int i=0; i<myVec.size(); i++) myVec.at(i).myVar = 0;
但是由于保证向量是连续存储的,所以肯定是一种更好的方法吗?
答案 0 :(得分:7)
重置需要遍历向量的每个元素,因此需要至少 O(n)复杂度。您当前的算法需要O(n)。
在这种特殊情况下,您可以使用operator[]
代替at
(可能会引发异常)。但我怀疑这是你申请的瓶颈。
在此说明中,您应该使用std::fill
:
std::fill(myVec.begin(), myVec.end(), 0);
但除非你想要字节级别并将一块内存设置为0
,这不仅会让你头疼,而且还会让你在大多数情况下失去可移植性,这里没有什么可以改进的。
答案 1 :(得分:6)
而不是下面的代码
for(int i=0; i<myVec.size(); i++) myVec.at(i).myVar = 0;
按如下方式进行:
size_t sz = myVec.size();
for(int i=0; i<sz; ++i) myVec[i].myVar = 0;
由于&#34; at&#34; 方法在内部检查索引是否超出范围。但是当你的循环索引正在照顾(myVec.size())时,你可以避免额外的检查。否则,这是最快的方法。
修改强>
除此之外,我们可以在执行for循环之前存储向量的size()。这将确保for循环中没有进一步调用方法size()。
答案 2 :(得分:2)
最快的方法之一是执行循环展开并打破传统for
循环造成的速度极限,导致现金溢出。在你的情况下,因为它是一个运行时的东西,没有办法应用模板元编程,所以好的旧 Duff的设备的变化可以做到这一点
#include <iostream>
#include <vector>
using namespace std;
struct myStruct {
int a;
double b;
};
int main()
{
std::vector<myStruct> mV(20);
double val(20); // the new value you want to reset to
int n = (mV.size()+7) / 8; // 8 is the size of the block unroll
auto to = mV.begin(); //
switch(mV.size()%8)
{
case 0: do { (*to++).b = val;
case 7: (*to++).b = val;
case 6: (*to++).b = val;
case 5: (*to++).b = val;
case 4: (*to++).b = val;
case 3: (*to++).b = val;
case 2: (*to++).b = val;
case 1: (*to++).b = val;
} while (--n>0);
}
// just printing to verify that the value is set
for (auto i : mV) std::cout << i.b << std::endl;
return 0;
}
这里我选择执行8块展开,以重置myStruct
结构的值(比方说)b。可以调整块大小并有效地展开循环。请记住,这是memcpy
中的基础技术和编译器将尝试的一种优化(一般是循环展开)(实际上他们对此非常擅长,所以我们不妨让他们完成自己的工作)。
答案 3 :(得分:1)
除了之前所说的,你应该考虑如果你打开优化,编译器可能会执行循环展开,这将使循环本身更快。
答案 4 :(得分:1)
预增量++i
也只需要少于增量i++
的指令。
解释here
答案 5 :(得分:1)
谨防花费大量时间考虑编译器将为您处理的优化细节。
以下是我理解OP的四个实现,以及使用gcc 4.8和--std=c++11 -O3 -S
生成的代码
声明:
#include <algorithm>
#include <vector>
struct T {
int irrelevant;
int relevant;
double trailing;
};
显式循环实现,大致来自提供给OP的答案和注释。除标签外,两者都生成相同的机器代码。
.cfi_startproc
movq (%rdi), %rsi
void clear_relevant(std::vector<T>* vecp) { movq 8(%rdi), %rcx
for(unsigned i=0; i<vecp->size(); i++) { xorl %edx, %edx
vecp->at(i).relevant = 0; xorl %eax, %eax
} subq %rsi, %rcx
} sarq $4, %rcx
testq %rcx, %rcx
je .L1
.p2align 4,,10
.p2align 3
.L5:
void clear_relevant2(std::vector<T>* vecp) { salq $4, %rdx
std::vector<T>& vec = *vecp; addl $1, %eax
auto s = vec.size(); movl $0, 4(%rsi,%rdx)
for (unsigned i = 0; i < s; ++i) { movl %eax, %edx
vec[i].relevant = 0; cmpq %rcx, %rdx
} jb .L5
} .L1:
rep ret
.cfi_endproc
另外两个版本,一个使用std::for_each
,另一个使用范围for
语法。这里有两个版本的代码(标签除外)的细微差别:
.cfi_startproc
movq 8(%rdi), %rdx
movq (%rdi), %rax
cmpq %rax, %rdx
je .L17
void clear_relevant3(std::vector<T>* vecp) { .p2align 4,,10
for (auto& p : *vecp) p.relevant = 0; .p2align 3
} .L21:
movl $0, 4(%rax)
addq $16, %rax
cmpq %rax, %rdx
jne .L21
.L17:
rep ret
.cfi_endproc
.cfi_startproc
movq 8(%rdi), %rdx
movq (%rdi), %rax
cmpq %rdx, %rax
void clear_relevant4(std::vector<T>* vecp) { je .L12
std::for_each(vecp->begin(), vecp->end(), .p2align 4,,10
[](T& o){o.relevant=0;}); .p2align 3
} .L16:
movl $0, 4(%rax)
addq $16, %rax
cmpq %rax, %rdx
jne .L16
.L12:
rep ret
.cfi_endproc