定义概率分布的成本是否很高

Is defining a probability distribution costly?

本文关键字:是否 概率分布 定义      更新时间:2023-10-16

我正在编写物理模拟,现在我觉得需要优化它。我正在考虑改进一点:我的一个类的方法之一(我在几种情况下调用了十亿次)定义了每次概率分布。这是代码:

void myClass::myMethod(){ //called billions of times in several cases
uniform_real_distribution<> probd(0,1);
uniform_int_distribution<> probh(1,h-2);
uniform_int_distribution<> probv(1,v-2);
    //rest of the code
}

我可以将发行版作为类的成员传递,这样我就不必每次都定义它们吗?只需在构造函数中初始化它们并在 h 和 v 更改时重新定义它们?这能是一个很好的优化进度吗?最后一个问题,当使用标志 -O3 或 -O2 编译时,编译器(在我的例子中为 g++)是否已经纠正了?

提前谢谢你!

更新:我

对其进行了编码并计时:程序实际上变慢了一点(百分之几),所以我回到了我开始的地方:在每个循环中创建概率分布

答案 A:我不应该这么认为,对于均匀分布,它只是将参数值复制到适当的位置,也许使用少量的算术,这将得到很好的优化。

但是,我相信分布对象可以具有状态。它们可以使用调用生成器的部分随机数据,并允许保存其余的随机性,以便下次使用分布时使用,以减少对生成器的调用总数。因此,当您销毁分发对象时,您可能会丢弃一些可能代价高昂的随机数据。

答案B:停止猜测并测试它。

对代码进行计时,然后将static添加到probd的定义中,然后再次计时。

  1. 是的
  2. 是的
  3. 嗯,可能有一些优势,但 AFAIK 这些对象并不是真正的重量级/构建成本。此外,对于局部变量,您可能会在数据局部性和优化器可以做出的假设中获得一些东西。
  4. 我不认为它们会自动作为类变量移动(特别是如果你的类是 POD - 在这种情况下,我怀疑编译器是否敢于修改其布局);最有可能的是,相反,它们被完全优化了 - 只有被调用方法的代码 - 特别是 operator() - 可能保留,直接引用 h 和 v。但这必须通过查看生成的程序集来检查。

顺便说一下,如果你有性能问题,除了优化明显的点(内部循环中使用的非最优算法,连续内存分配,删除大对象的无用副本,...),你应该真正尝试使用探查器来找到代码中真正的"热点",并集中精力优化它们,而不是随机浏览所有代码。

>uniform_real_distribution保持类型为 param_type 的状态,该状态是两个double值(使用默认模板参数)。构造函数分配给这些,否则是微不足道的,析构函数是微不足道的。

因此,与初始化 1 个指针(或引用)或通过 this 间接寻址相比,在函数中构造临时值具有存储 2 个double值的开销。从理论上讲,它可能因此更快(尽管看起来更快的东西,或者运行得更快有意义的东西没有必要更快)。由于工作量不大,因此当然值得尝试和计时是否存在差异,即使是微优化。

一些额外的3-4个周期通常可以忽略不计,但既然你说的是"数十亿次",它当然可以很好地产生可衡量的差异。 3 个周期乘以 10 亿是 3GHz 机器上的 1 秒。

当然,没有分析的优化总是有点...尴尬。您可能会发现,代码中调用数十亿次的不同部分可以节省更多的周期。

编辑:
由于您不打算修改它,并且由于第一个发行版是用文字值初始化的,因此您实际上可以将其设置为常量(例如constexpr或命名空间级别static const)。无论其他两个如何,这都应该允许编译器在任何情况下为该编译器生成最有效的代码。