1

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

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++)更正了?

先感谢您!

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

4

3 回答 3

5

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

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

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

为您的代码计时,然后添加static到定义probd并再次计时。

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

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

于 2013-10-31T12:18:14.733 回答
2

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

因此,double与初始化 1 个指针(或引用)或通过this. 从理论上讲,它可能因此更快(尽管看起来更快,或者运行得更快有意义的东西并不需要更快)。由于工作量不大,因此即使是微优化,当然也值得尝试并确定是否存在差异。

大约 3-4 个额外的周期通常可以忽略不计,但既然你说的是“数十亿次”,它当然很可能会产生可衡量的差异。在 3GHz 机器上,3 个周期乘以 10 亿是 1 秒。

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

编辑:
由于您不打算修改它,并且由于第一个分布是用文字值初始化的,因此您实际上可能将其设为常量(例如 aconstexpr或 namespace level static const)。无论其他两个如何,这都应该允许编译器在任何情况下为那个生成最有效的代码。

于 2013-10-31T13:40:02.897 回答