首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在循环中使用normal_distribution

在循环中使用normal_distribution
EN

Stack Overflow用户
提问于 2019-04-25 21:15:34
回答 2查看 167关注 0票数 2

我想知道在循环中放置normal_distribution是否有问题。

下面是以这种奇怪的方式使用normal_distribution的代码:

代码语言:javascript
复制
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);

}
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-04-25 21:51:56

normal_distribution对象置于循环之外可能比将其放置在循环中的效率略高。当它在循环中时,每次都可以重新构造normal_distribution对象,而如果它在循环之外,则只能构造一次。

程序集的比较。

根据对程序集的分析,在循环外声明是更有效的。

让我们看看两个不同的函数,以及相应的程序集。其中一个在循环中声明distribution,另一个在循环之外声明它。为了简化分析,在这两种情况下都声明了const,所以我们(和编译器)知道发行版不会被修改。

你可以看到整个集会在这里。

代码语言:javascript
复制
// 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优化)。

代码语言:javascript
复制
.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,而不重新构造发行版。指令少了,所有的东西都留在寄存器中(所以不需要访问堆栈)。

代码语言:javascript
复制
.L12:
        mov     rdi, rsp
        add     ebx, 1
        call    foo(std::normal_distribution<double> const&)
        cmp     ebp, ebx
        jne     .L12

是否有任何理由在循环中声明变量?

是的。绝对是在循环中声明变量的好时机。如果您在循环中以某种方式修改distribution,那么每次只通过重新构造它就可以重新设置它。

此外,如果您从未在循环之外使用变量,那么在循环中声明变量只是为了提高可读性是有意义的。

适合CPU寄存器(所以浮动、int、双和小用户定义的类型)的类型通常没有与它们的构造相关联的开销,在循环中声明它们实际上可以通过简化编译器对寄存器分配的分析而导致更好的组装。

票数 1
EN

Stack Overflow用户

发布于 2019-04-25 21:53:59

查看正态分布的接口,有一个名为reset的成员,他:

重置分布的内部状态。

这意味着分布可能具有内部状态。如果是这样的话,那么当您在每次迭代中重新创建对象时,您肯定会重新设置它。不按预期使用它可能会产生一个不正常的发行版,或者只是效率低下。

会是什么状态?这当然是定义的实现。从LLVM的一个实现来看,正态分布是围绕这里定义的。更具体地说,operator()这里。查看代码,在随后的调用之间肯定存在某种状态共享。更具体地说,在每次后续调用时,都会翻转布尔变量_V_hot_的状态。如果为真,则执行的计算大大减少,并且使用存储的_V_的值。如果它是假的,则从零开始计算_V_

我并没有深入研究他们为什么选择这样做。但是,只看执行的计算,依赖内部状态应该更快。虽然这只是一些实现,但它表明该标准允许使用内部状态,而且在某些情况下是有益的。

稍后编辑:

GCC libstdc++的std::normal_distribution实现可以找到这里。注意,operator()调用另一个函数__generate_impl,它是在一个单独的文件这里中定义的。虽然不同,但是这个实现有相同的标志,这里命名为_M_saved_available,它加快了其他调用的速度。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/55857740

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档