C++ 在循环中使用正态分布
我想知道把C++ 在循环中使用正态分布,c++,C++,我想知道把正态分布放在一个循环中是否会有问题 下面是以这种奇怪的方式使用正态分布的代码: std::default_random_engine generator; //std::normal_distribution<double> distribution(5.0,2.0); for (int i=0; i<nrolls; ++i) { std::normal_distribution<double> distribution(5.0,2.0);
正态分布
放在一个循环中是否会有问题
下面是以这种奇怪的方式使用正态分布的代码:
std::default_random_engine generator;
//std::normal_distribution<double> distribution(5.0,2.0);
for (int i=0; i<nrolls; ++i) {
std::normal_distribution<double> distribution(5.0,2.0);
float x = distribution(generator);
}
std::默认随机引擎生成器;
//标准:正态分布(5.0,2.0);
对于(int i=0;i将正态分布
对象放在循环外可能比放在循环内稍微有效。当它在循环内时,正态分布
对象每次都可以重新构造,而如果它在循环外,则只构造一次
组件的比较。
根据对程序集的分析,在循环外声明分发
更有效。
让我们看看两个不同的函数,以及相应的程序集。其中一个在循环内声明分发
,另一个在循环外声明它。为了简化分析,在这两种情况下,它们都声明为const,因此我们(和编译器)知道分发不会被修改
你可以看到
基本上,它
-构建分布
-使用发行版调用foo
-测试它是否应该退出循环
外环
总成
使用相同的编译选项,outside\u loop
只需重复调用foo
,而无需重新构建分发。指令更少,所有内容都保留在寄存器中(因此无需访问堆栈)
.L12:
移动rdi,rsp
添加ebx,1
调用foo(std::normal\u distribution const&)
cmp-ebp,ebx
jne.L12
是否有任何理由在循环中声明变量?
是。在循环中声明变量肯定是一个好时机。如果您在循环中以某种方式修改分布,那么每次重新构造它都是有意义的
此外,如果您从未在循环外部使用过变量,那么在循环内部声明变量就很有意义,只是为了可读性
适合CPU寄存器的类型(因此浮点型、整数型、双精度型和小型用户定义类型)通常没有与它们的构造相关的开销,通过简化编译器对寄存器分配的分析,在循环中声明它们实际上可以导致更好的汇编。查看正态分布的定义,有一个名为reset
的成员:
重置分发的内部状态
这意味着分发可能有一个内部状态。如果有,那么在每次迭代中重新创建对象时,您肯定会重置该状态。如果不按预期使用它,可能会产生一个不正常或效率低下的分发
它可能是什么状态?这当然是实现定义的。从LLVM的一个实现来看,正态分布是围绕它定义的。更具体地说,操作符()
是。查看代码,后续调用之间肯定会共享一些状态。更具体地说,在每次后续调用中,布尔变量\u V\u hot\u
的状态都会被翻转。如果为真,则执行的计算会显著减少,并且使用存储的\u V
的值。如果为假,则是从头开始计算的
我没有深入探讨他们为什么选择这样做。但是,仅从执行的计算来看,依赖内部状态应该更快。虽然这只是一些实现,但它表明该标准允许使用内部状态,在某些情况下是有益的
稍后编辑:
可以找到std::normal_distribution
的GCC libstdc++实现
调用另一个函数,\uuuu generate\u impl
,该函数在一个单独的文件中定义。虽然不同,但此实现具有相同的标志,在这里命名为\u M\u saved\u available
,它加快了每一次调用的速度。分发版具有内部状态,通过将其放入循环中,可以重置它。我不知道结果是否uld可能是错误的,但可能有所不同。为什么不将初始化移到循环之外?这里的“奇怪”可能意味着“低效且错误”。我正在阅读其他人以这种奇怪方式编写的代码。随机数的分布仍然是高斯分布,仍然是具有正确平均值和宽度的高斯分布。我只是想知道是否可能存在潜在问题。对于哪些编译器或标准库实现,这是真的?我上面提到的实现是LLVM库cxx:,在clang.@J.AntonioPerez中使用。我也研究了gcc。提供的libstdc++有一个非常类似的实现,具有类似的行为。如果您想看一看,我已经更新了我的答案。
// This function is here to prevent the compiler from optimizing out the
// loop entirely
void doSomething(std::normal_distribution<double> const& d) noexcept;
void inside_loop(double mean, double sd, int n) {
for(int i = 0; i < n; i++) {
const std::normal_distribution<double> d(mean, sd);
doSomething(d);
}
}
void outside_loop(double mean, double sd, int n) {
const std::normal_distribution<double> d(mean, sd);
for(int i = 0; i < n; i++) {
doSomething(d);
}
}
.L3:
movapd xmm2, XMMWORD PTR [rsp]
lea rdi, [rsp+16]
add ebx, 1
mov BYTE PTR [rsp+40], 0
movaps XMMWORD PTR [rsp+16], xmm2
call foo(std::normal_distribution<double> const&)
cmp ebp, ebx
jne .L3
.L12:
mov rdi, rsp
add ebx, 1
call foo(std::normal_distribution<double> const&)
cmp ebp, ebx
jne .L12