gpt4 book ai didi

c++ - 使用 .size() 与 const 变量进行循环

转载 作者:太空宇宙 更新时间:2023-11-04 15:02:08 24 4
gpt4 key购买 nike

我有一个 vector :

vector<Body*> Bodies;

它包含指向我定义的 Body 对象的指针。

我还有一个 unsigned int const,其中包含我希望在 bodies 中拥有的 body 对象的数量。

unsigned int const NumParticles = 1000;

我已经使用 NumParticles 数量的 Body 对象填充了 Body

现在,如果我想遍历一个循环,例如在 Body 中调用每个 Body 的 Update() 函数,我有两种选择可以做:

首先:

for (unsigned int i = 0; i < NumParticles; i++)
{
Bodies.at(i)->Update();
}

或者第二个:

for (unsigned int i = 0; i < Bodies.size(); i++)
{
Bodies.at(i)->Update();
}

各有利弊。我想知道在安全性、可读性和约定方面哪一个(如果有的话)是更好的做法。

最佳答案

我希望,鉴于编译器(至少在这种情况下)可以内联 std::vector 中的所有相关代码,它将是相同的代码 [除了 1000 是机器代码中的真正常量文字,并且 Bodies.size()将是一个“可变”值]。

调查结果的简短总结:

  • 编译器没有为 size() 调用函数每次迭代的 vector ,它在循环开始时计算它,并将其用作“常量值”。

  • 循环中的实际代码是相同的,只是循环的准备不同。

  • 一如既往:如果性能非常重要,请使用您的数据和编译器在您的系统上进行测量。否则,编写对您的设计最有意义的代码(我更喜欢使用 for(auto i : vec) ,因为它简单直接 [并且适用于所有容器])

支持证据:

取咖啡后,我写了这段代码:

class X
{
public:
void Update() { x++; }
operator int() { return x; }
private:
int x = rand();
};

extern std::vector<X*> vec;
const size_t vec_size = 1000;

void Process1()
{
for(auto i : vec)
{
i->Update();
}
}

void Process2()
{
for(size_t i = 0; i < vec.size(); i++)
{
vec[i]->Update();
}
}


void Process3()
{
for(size_t i = 0; i < vec_size; i++)
{
vec[i]->Update();
}
}

(连同填充数组的 main 函数,并调用 Process1()、Process2() 和 Process3() - main 位于单独的文件中,以避免编译器决定内联所有内容并使其成为很难说什么是什么)

这是 g++ 4.9.2 生成的代码:

0000000000401940 <_Z8Process1v>:
401940: 48 8b 0d a1 18 20 00 mov 0x2018a1(%rip),%rcx # 6031e8 <vec+0x8>
401947: 48 8b 05 92 18 20 00 mov 0x201892(%rip),%rax # 6031e0 <vec>
40194e: 48 39 c1 cmp %rax,%rcx
401951: 74 14 je 401967 <_Z8Process1v+0x27>
401953: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
401958: 48 8b 10 mov (%rax),%rdx
40195b: 48 83 c0 08 add $0x8,%rax
40195f: 83 02 01 addl $0x1,(%rdx)
401962: 48 39 c1 cmp %rax,%rcx
401965: 75 f1 jne 401958 <_Z8Process1v+0x18>
401967: f3 c3 repz retq

0000000000401970 <_Z8Process2v>:
401970: 48 8b 35 69 18 20 00 mov 0x201869(%rip),%rsi # 6031e0 <vec>
401977: 48 8b 0d 6a 18 20 00 mov 0x20186a(%rip),%rcx # 6031e8 <vec+0x8>
40197e: 31 c0 xor %eax,%eax
401980: 48 29 f1 sub %rsi,%rcx
401983: 48 c1 f9 03 sar $0x3,%rcx
401987: 48 85 c9 test %rcx,%rcx
40198a: 74 14 je 4019a0 <_Z8Process2v+0x30>
40198c: 0f 1f 40 00 nopl 0x0(%rax)
401990: 48 8b 14 c6 mov (%rsi,%rax,8),%rdx
401994: 48 83 c0 01 add $0x1,%rax
401998: 83 02 01 addl $0x1,(%rdx)
40199b: 48 39 c8 cmp %rcx,%rax
40199e: 75 f0 jne 401990 <_Z8Process2v+0x20>
4019a0: f3 c3 repz retq

00000000004019b0 <_Z8Process3v>:
4019b0: 48 8b 05 29 18 20 00 mov 0x201829(%rip),%rax # 6031e0 <vec>
4019b7: 48 8d 88 40 1f 00 00 lea 0x1f40(%rax),%rcx
4019be: 66 90 xchg %ax,%ax
4019c0: 48 8b 10 mov (%rax),%rdx
4019c3: 48 83 c0 08 add $0x8,%rax
4019c7: 83 02 01 addl $0x1,(%rdx)
4019ca: 48 39 c8 cmp %rcx,%rax
4019cd: 75 f1 jne 4019c0 <_Z8Process3v+0x10>
4019cf: f3 c3 repz retq

虽然每种情况下的汇编代码看起来都略有不同,但在实践中,我想说你很难衡量这些循环之间的差异,事实上,perf 的运行在代码上显示它是“所有循环的同一时间”[这是在一个循环中有 100000 个元素和 100 次对 Process1、Process2 和 Process3 的调用,否则时间由 new X 支配在 main ]:

  31.29%  a.out    a.out                [.] Process1
31.28% a.out a.out [.] Process3
31.13% a.out a.out [.] Process2

除非您认为 1/10% 很重要 - 它可能适用于需要一周才能运行的东西,但这只是十分之几秒 [在我的机器上是 0.163 秒],并且可能需要更多的测量错误比其他任何事情都重要 - 更短的时间实际上是理论上应该最慢的时间,Process2,使用 vec.size() .我用更高的循环次数又运行了一次,现在每个循环的测量值彼此相差 0.01% - 换句话说,花费的时间相同。

当然,如果仔细观察,您会发现所有三个变体的实际循环内容基本相同,除了Process3 的前面部分。这更简单,因为编译器知道我们将至少执行一个循环 - Process1Process2必须在第一次迭代之前检查“vector 是否为空”。这会对非常短的 vector 长度产生影响。

关于c++ - 使用 .size() 与 const 变量进行循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30952191/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com