我想知道在循环中放置normal_distribution是否有问题。
下面是以这种奇怪的方式使用normal_distribution的代码:
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);
}发布于 2019-04-25 21:51:56
将normal_distribution对象置于循环之外可能比将其放置在循环中的效率略高。当它在循环中时,每次都可以重新构造normal_distribution对象,而如果它在循环之外,则只能构造一次。
程序集的比较。
根据对程序集的分析,在循环外声明是更有效的。
让我们看看两个不同的函数,以及相应的程序集。其中一个在循环中声明distribution,另一个在循环之外声明它。为了简化分析,在这两种情况下都声明了const,所以我们(和编译器)知道发行版不会被修改。
你可以看到整个集会在这里。
// 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);
}
}inside_loop装配
循环的程序集看起来如下(用gcc 8.3编译的O3优化)。
.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基本上,它构造发行版-使用发行版测试调用foo,以查看它是否应该退出循环。
outside_loop装配
使用相同的编译选项,outside_loop只重复调用foo,而不重新构造发行版。指令少了,所有的东西都留在寄存器中(所以不需要访问堆栈)。
.L12:
mov rdi, rsp
add ebx, 1
call foo(std::normal_distribution<double> const&)
cmp ebp, ebx
jne .L12是否有任何理由在循环中声明变量?
是的。绝对是在循环中声明变量的好时机。如果您在循环中以某种方式修改distribution,那么每次只通过重新构造它就可以重新设置它。
此外,如果您从未在循环之外使用变量,那么在循环中声明变量只是为了提高可读性是有意义的。
适合CPU寄存器(所以浮动、int、双和小用户定义的类型)的类型通常没有与它们的构造相关联的开销,在循环中声明它们实际上可以通过简化编译器对寄存器分配的分析而导致更好的组装。
发布于 2019-04-25 21:53:59
查看正态分布的接口,有一个名为reset的成员,他:
重置分布的内部状态。
这意味着分布可能具有内部状态。如果是这样的话,那么当您在每次迭代中重新创建对象时,您肯定会重新设置它。不按预期使用它可能会产生一个不正常的发行版,或者只是效率低下。
会是什么状态?这当然是定义的实现。从LLVM的一个实现来看,正态分布是围绕这里定义的。更具体地说,operator()是这里。查看代码,在随后的调用之间肯定存在某种状态共享。更具体地说,在每次后续调用时,都会翻转布尔变量_V_hot_的状态。如果为真,则执行的计算大大减少,并且使用存储的_V_的值。如果它是假的,则从零开始计算_V_。
我并没有深入研究他们为什么选择这样做。但是,只看执行的计算,依赖内部状态应该更快。虽然这只是一些实现,但它表明该标准允许使用内部状态,而且在某些情况下是有益的。
稍后编辑:
GCC libstdc++的std::normal_distribution实现可以找到这里。注意,operator()调用另一个函数__generate_impl,它是在一个单独的文件这里中定义的。虽然不同,但是这个实现有相同的标志,这里命名为_M_saved_available,它加快了其他调用的速度。
https://stackoverflow.com/questions/55857740
复制相似问题