c++ 如何优化多个兰德()调用?[已关闭]

v1uwarro  于 2023-10-20  发布在  其他
关注(0)|答案(3)|浏览(101)

已关闭,此问题为opinion-based。它目前不接受回答。
**想改善这个问题吗?**更新问题,以便editing this post可以用事实和引用来回答。

21小时前关门了。
社区在19小时前审查了是否重新打开这个问题,并关闭了它:
原始关闭原因未解决
Improve this question
我有一个粒子模拟器,它需要很多随机数的调用,主要是在0.0-1.0之间。它对每个产生的粒子都这样做,所以你可以看到它加起来了。
我在每次迭代中计算了不少于60个:(double)(rand() % RAND_MAX) / (RAND_MAX)
它们用于许多特征,从颜色随机性到大小,到速度,再到力等。
我的问题是双重的:
1.兰德()函数是否会对性能造成很大的影响,或者每个粒子有60个这样的函数,不值得担心?我可以很容易地有1000个粒子,所以这将等于60,000个随机调用!
1.我知道我可以计算5个随机浮点数,并在60个调用中重用它们,但我担心这是一个坏主意,我会开始看到由于重用而导致的不一致。只计算一个并重用它可能是一个糟糕的主意,对吧?
有没有更好的方法来优化它?

ktecyv1j

ktecyv1j1#

这不是对你的问题的回答,而是一个评论:即使是有经验的程序员也不能告诉他们的代码的瓶颈在哪里,除非他们实际上分析代码。这同样适用于你的问题:您想知道代码的某一部分是否存在性能问题,但您没有提供任何证据表明您已经测量了这一部分,因此您很可能浪费时间来思考这个问题。
所以建议是:如果你的代码太慢,用一个分析器运行它,看看生成随机数的调用是否显示在靠近顶部的任何地方。如果他们这样做,那么你可以开始思考解决方案。如果他们不这样做,那么你只是获得了几天做别的事情!

sg24os4d

sg24os4d2#

正如在一些优秀的评论和回答中提到的,在效率问题上,直觉和(甚至是有教养的)猜测往往是糟糕的指南。
但是,如果你碰巧知道生成的随机数的总数,你可以编写一个小的测试程序,只生成相同数量的随机数,加上一些廉价的统计时刻,用于健全性检查。
接下来,您可以将这个小程序所花费的时间与实际物理应用程序所花费的总时间进行比较。
为了获得良好的可移植的统计保证,您可以使用标准C++ <random>头文件提供的现代工具,而不是使用 legacyrand()/srand()函数。
你的小测试程序的C++代码可能是这样的:

//-- https://isocpp.org/files/papers/n3551.pdf
//-- https://en.cppreference.com/w/cpp/numeric/random/uniform_real_distribution

#include  <iostream>
#include  <random>

int main()
{
    std::random_device  rd;  // Will be used to obtain a seed
                             // on Linux, could be:  rd{"/dev/urandom"};
    std::mt19937        gen(rd()); // Standard mersenne_twister_engine seeded with rd()
    std::uniform_real_distribution<double>  dis(0.0, 1.0);

    int     count = 60*1000*1000;
    double  sum1  = 0.0;
    double  sum2  = 0.0;
    double  r     = 0.0;

    for (int n = 0; n < count; ++n) {
        // Use dis to transform the random unsigned int generated by gen into a 
        // double in [0, 1). Each call to dis(gen) generates a new random double.
        r = dis(gen);
        sum1 += r;
        sum2 += r*r;
    }

    double avg   = sum1 / (double)count;
    double stdev = std::sqrt ((sum2 / (double)count) - avg*avg);

    std::cout << "count = " << count << '\n';
    std::cout << "average = " << avg << '\n';
    std::cout << "standard deviation = " << stdev << '\n';

    return EXIT_SUCCESS;
}

事实证明,在老式Intel x86-64 PC上,使用此代码生成0.0和1.0之间的6000万个随机数的时间略低于一秒,使用选项-O2和编译器:

g++ (GCC) 12.3.1 20230508 (Red Hat 12.3.1-1)

因此,我们可以预期在现代CPU上生成60,000个随机数所需的时间低于1毫秒。

x0fgdtte

x0fgdtte3#

这里有一个主要的考虑因素,似乎没有人提到(这个问题本身也不足以确定它是否是一个问题)。
如果从多个执行线程调用rand(),则会出现潜在问题。rand()使用(一个,奇异)种子值。每次调用它时,种子值都会更新,因此下一次调用可以/将产生新的/不同的值。
为了让它在多个执行线程中工作,rand()通常需要用互斥(或类似的东西)来保护该种子。如果从多个线程调用rand(),代码最终会被序列化,因此一次只能运行一个对rand()的调用。
因此,即使有许多线程,并且有足够的内核来并发运行,大量使用rand()通常会导致仅有效利用一个或两个内核,而不是所有可用的内核(尽管确切的内核使用情况会因使用rand()的频率而异)。
C++ <random>头文件中的随机数生成器没有这个缺点。每个随机数生成器(使用种子)将种子存储在随机数生成器对象本身中。所以你可以有16个线程使用16个独立的随机数生成器对象,它们之间根本没有同步。但是,请注意,std::random_device通常会从某些随机数生成硬件中检索数字(至少是间接地),因此它的频繁使用或多或少是序列化的,因此您通常只想使用它来检索某些伪随机生成器的初始种子。该库还直接支持生成范围内的数字,因此(与您当前的代码不同)您在0.0. 1.0范围不会有偏差。

相关问题