在vector的每个struct元素中重置值的最快方法
Fastest way to reset a value in every struct element of a vector?
非常像这个问题,除了vector<int>
换成了vector<struct myType>
。
如果我想为向量中的每个元素重置(或设置为某个值)myType.myVar
,最有效的方法是什么?
现在我正在遍历:
for(int i=0; i<myVec.size(); i++) myVec.at(i).myVar = 0;
但是既然vector保证是连续存储的,那么肯定有更好的方法吗?
重置将需要遍历向量的每个元素,因此它将需要至少 0 (n)的复杂度。你现在的算法需要O(n)
在这种特殊情况下,您可以使用operator[]
而不是at
(这可能会抛出异常)。但我怀疑这是你的应用程序的瓶颈。
在这一点上你应该使用std::fill
:
std::fill(myVec.begin(), myVec.end(), 0);
但是,除非您想进入字节级别并将一块内存设置为0
,这不仅会让您头痛,而且在大多数情况下还会使您失去可移植性,否则这里没有什么可改进的。
代替下面的代码
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;
"at"方法内部检查索引是否超出范围。但是由于循环索引负责(myVec.size()),因此可以避免额外的检查。否则,这是最快的方法。
编辑
除此之外,还可以在执行for循环之前存储vector对象的size()。这将确保在for循环中不会再调用size()方法。最快的方法之一是执行循环展开并打破导致大量现金溢出的传统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
中的底层技术,也是编译器将尝试的优化之一(通常是循环展开)(实际上它们非常擅长此道,所以我们不妨让它们完成自己的工作)。
除了前面所说的,您应该考虑如果您打开优化,编译器可能会执行循环展开,这将使循环本身更快。
同样,增量前++i
比增量后i++
需要的指令少。在这里解释
小心不要花太多时间考虑优化的细节,编译器会帮你处理的。
以下是我所理解的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
,另一个使用range 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
- 数组元素打印的递归方法
- 如何实现 Front() 方法以返回模板化双向链表C++的第一个元素?
- 有什么方法可以将元素添加到列表中,如图所示?
- 在向量中查找大于 0(或通常为 k)的最小元素的最佳方法是什么?
- C++将所有元素从矢量复制到地图/unordered_map的最佳方法
- 在C++中更改结构内部元素的方法?
- 通过作为指向 C++ 函数的指针传递来访问 std::array 元素的正确方法是什么?
- 有没有更快的方法可以在 std::vector 中插入元素
- 返回向量元素的 l 值的正确方法是什么?
- 按升序打印矢量的所有元素直到它为空而没有重复项的最有效方法是什么?
- C++去除前x个元素的有效方法,在不改变向量大小的情况下将第x+1个元素推到第一个
- 如何在不迭代的情况下对数组中的每个元素调用方法
- 是否有更有效的方法来检查元素是否在给定的区间内
- 这种获取模板参数包中最后一个元素的方法是否有隐藏的开销?
- 将__m256的奇怪元素提取到__m128中的有效(在锐龙上)方法?
- 为什么 memcpy() 是一种将元素添加到 'std::map' 的方法?
- 从长(且合理)稀疏向量中选择随机元素的最有效方法是什么?
- 如何使用返回第 n 个元素的方法创建元组
- 如何在不使用 vector::erase() 的情况下编写自定义 Vector 方法来删除元素?
- 从任意容器中廉价删除元素的惯用方法?