蒙特卡洛模拟运行速度明显慢于顺序模拟

2023-12-01

一般来说,我对并发和并行编程的概念很陌生。我正在尝试使用计算 Pi蒙特卡罗法这是我的源代码:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>

int main(void)
{
    long points;
    long m = 0;
    double coordinates[2];
    double distance;
    printf("Enter the number of points: ");
    scanf("%ld", &points);

    srand((unsigned long) time(NULL));
    for(long i = 0; i < points; i++)
    {
        coordinates[0] = ((double) rand() / (RAND_MAX));
        coordinates[1] = ((double) rand() / (RAND_MAX));
        distance = sqrt(pow(coordinates[0], 2) + pow(coordinates[1], 2));
        if(distance <= 1)
            m++;
    }

    printf("Pi is roughly %lf\n", (double) 4*m / (double) points);
}

当我尝试使用 openmp api 使该程序并行时,它的运行速度几乎慢了 4 倍。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <omp.h>
#include <sys/sysinfo.h>

int main(void)
{

    long total_points;              // Total number of random points which is given by the user
    volatile long total_m = 0;      // Total number of random points which are inside of the circle
    int threads = get_nprocs();     // This is needed so each thred knows how amny random point it should generate
    printf("Enter the number of points: ");
    scanf("%ld", &total_points);
    omp_set_num_threads(threads);   

    #pragma omp parallel
    {
       double coordinates[2];          // Contains the x and y of each random point
       long m = 0;                     // Number of points that are in the circle for any particular thread
       long points = total_points / threads;   // Number of random points that each thread should generate
       double distance;                // Distance of the random point from the center of the circle, if greater than 1 then the point is outside of the circle
       srand((unsigned long) time(NULL));

        for(long i = 0; i < points; i++)
        {
           coordinates[0] = ((double) rand() / (RAND_MAX));    // Random x
           coordinates[1] = ((double) rand() / (RAND_MAX));    // Random y
           distance = sqrt(pow(coordinates[0], 2) + pow(coordinates[1], 2));   // Calculate the distance
          if(distance <= 1)
              m++;
       }

       #pragma omp critical
       {
           total_m += m;
       }
    }

    printf("Pi is roughly %lf\n", (double) 4*total_m / (double) total_points);
}

我尝试查找原因,但不同的算法有不同的答案。


代码中有两个开销来源,即critical region,并调用rand()。代替rand() use rand_r:

我认为您正在寻找 rand_r(),它明确采用 当前 RNG 状态作为参数。那么每个线程应该有它的 自己的种子数据副本(是否希望每个线程以 相同的种子或不同的种子取决于你在做什么,在这里你 希望它们不同,否则您会一次又一次地得到同一行)。

可以使用 OpenMP 子句删除关键区域reduction。此外,您也不需要调用sqrt也不手动将点除以线程(i.e., long points = total_points / threads;), 您可以使用#pragma omp for为了那个原因。所以你的代码将如下所示:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <omp.h>
#include <sys/sysinfo.h>

int main(void)
{
    long total_points; 
    long total_m = 0;
    int threads = get_nprocs();   
    printf("Enter the number of points: ");
    scanf("%ld", &total_points);
    omp_set_num_threads(threads);   

    #pragma omp parallel 
    {                  
        unsigned int myseed = omp_get_thread_num();
        #pragma omp for reduction (+: total_m)
        for(long i = 0; i < total_points; i++){
            if(pow((double) rand_r(&myseed) / (RAND_MAX), 2) + pow((double) rand_r(&myseed) / (RAND_MAX), 2) <= 1)
               total_m++;
         }
     }
    printf("Pi is roughly %lf\n", (double) 4*total_m / (double) total_points);

}

在我的机器上快速测试输入 1000000000:

sequential : 16.282835 seconds 
2 threads  :  8.206498 seconds  (1.98x faster)
4 threads  :  4.107366 seconds  (3.96x faster)
8 threads  :  2.728513 seconds  (5.96x faster)

请记住,我的机器只有 4 个核心。尽管如此,为了进行更有意义的比较,应该尝试尽可能地优化顺序代码,然后将其与并行版本进行比较。当然,如果顺序版本尽可能优化,并行版本的加速可能会下降。例如,根据提供的代码的顺序版本测试当前的并行版本而不进行修改@用户3666197,产生以下结果:

sequential :  9.343118 seconds 
2 threads  :  8.206498 seconds  (1.13x faster)
4 threads  :  4.107366 seconds  (2.27x faster)
8 threads  :  2.728513 seconds  (3.42x faster)

然而,我们还可以改进并行版本以及等等,第四。例如,如果一个人采取@用户3666197版本,修复更新的竞争条件coordinates(在线程之间共享),并添加 OpenMP #pragma omp for,我们有以下代码:

int main(void)
{
    double start = omp_get_wtime();
    long points = 1000000000; //....................................... INPUT AVOIDED
    long m = 0;
    unsigned long HAUSNUMERO = 1;
    double DIV1byMAXbyMAX = 1. / RAND_MAX / RAND_MAX;

    int threads = get_nprocs();
    omp_set_num_threads(threads);
    #pragma omp parallel reduction (+: m )
    {
        unsigned int aThreadSpecificSEED_x = HAUSNUMERO + 1 + omp_get_thread_num();
        unsigned int aThreadSpecificSEED_y = HAUSNUMERO - 1 + omp_get_thread_num();
        #pragma omp for nowait
        for(long i = 0; i < points; i++)
        {
            double x = rand_r( &aThreadSpecificSEED_x );
            double y = rand_r( &aThreadSpecificSEED_y );
            m += (1  >= ( x * x + y * y ) * DIV1byMAXbyMAX);
        }
    }
    double end = omp_get_wtime();
    printf("%f\n",end-start);
    printf("Pi is roughly %lf\n", (double) 4*m / (double) points);
}

产生以下结果:

sequential :  9.160571 seconds 
2 threads  :  4.769141 seconds  (1.92 x faster)
4 threads  :  2.456783 seconds  (3.72 x faster)
8 threads  :  2.203758 seconds  (4.15 x faster)

我正在使用标志进行编译-O3 -std=c99 -fopenmp,并使用 gcc 版本4.9.3 (MacPorts gcc49 4.9.3_0).

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

蒙特卡洛模拟运行速度明显慢于顺序模拟 的相关文章

  • 如何在C编程中获取当前时间(以毫秒为单位)[重复]

    这个问题在这里已经有答案了 可能的重复 如何使用 ANSI C 测量以毫秒为单位的时间 https stackoverflow com questions 361363 how to measure time in milliseconds
  • 静态成员函数与C语言绑定?

    以下 C 代码可使用 Visual C 和 g 进行编译 struct S static void foo extern C void S foo struct T static void foo extern C void T foo a
  • 无法在更新面板中找到上传的文件

    aspx
  • NUnit 测试运行顺序

    默认情况下 nunit 测试按字母顺序运行 有谁知道有什么方法可以设置执行顺序吗 是否存在这样的属性 我只是想指出 虽然大多数受访者认为这些是单元测试 但问题并没有具体说明它们是 nUnit 是一个很棒的工具 可用于各种测试情况 我可以看到
  • 错误 C2064:术语不计算为采用 1 个参数的函数

    class Student bool Graduate return m bGraduate class School vector
  • 设置外部应用程序焦点

    在 VB NET 中 您可以使用以下命令将焦点设置到外部应用程序 AppActivate Windows Name or AppActivate processID As Integer 现在 如果您这样做 则效果很好 Dim intNot
  • 最小对的总和

    Given 2N点 in a 2D plane 你必须将它们分组为N pairs使得所有对的点之间的距离的总和是最小可能值 所需的输出只是总和 换句话说 如果a1 a2 an分别是第一对 第二对 和第 n 对点之间的距离 则 a1 a2 a
  • 在标准库中静态链接时如何支持动态插件?

    假设一个应用程序myapp exe是使用构建的g 它使用标志 static libstdc 这样就可以安装在没有环境的情况下libstdc so myapp exe还添加了对某些功能的插件支持plugf可以通过动态加载dlopen来自共享库
  • 锁定文件的一个块

    我有一个大小为 192k 的文件 我想锁定文件的中间部分 例如 我想用 c 锁定文件的 64k 128k 知道如何锁定文件的那部分吗 你需要使用锁定文件Ex http msdn microsoft com en us library win
  • 带有 Unicode 字符的主机名在 Windows 8 中有效

    Uri CheckHostName 回报UriHostNameType Unknown到处都是 但在 Windows 8 上 它又回来了UriHostNameType Dns 为什么突然间带有 Unicode 西里尔字符的主机名在 Wind
  • 一些涉及类析构函数和删除运算符的内存管理问题?

    在阅读了一些教程后 我仍然不清楚 C 中内存管理的一些观点 1 当使用 new 运算符声明的类超出范围时 是否会调用其析构函数并释放内存 是否有必要调用删除运算符来释放类的内存并调用其析构函数 class Test void newTest
  • 在发送传出请求之前将新的 SoapClient 绑定到特定 IP 地址

    假设应用程序所在的计算机具有 SoapClient 具体来说 我正在使用 Microsoft Web Service3 Messaging SoapClient 它通过发送传出请求并获取 SoapEnvelope 作为回报 完善的流程 与远
  • 检测用户是否正在滚动 dataGridView 滚动条

    我正在更新一个dataGridView与一个新的数据表使用 dataGridView1 DataSource table 但是 我不想在用户滚动 dataGridView 时执行此操作 如何检查滚动条是否正在滚动或已完成滚动 即拖动而不是单
  • 序列化时如何跳过 xml 声明?

    我正在尝试输出一个没有 xml 头的 xml 文件 例如 我试过 Type t obj GetType XmlSerializer xs new XmlSerializer t XmlWriter xw XmlWriter Create c
  • C# 记录类型:记录子类之间的相等比较

    给定父记录类型 public record Foo string Value 和两个记录子类Bar and Bee我想知道是否可以实施Equals在基类中 因此 Foo Bar 或 Bee 的实例都被考虑equal基于Value 两者都与E
  • RabbitMQ + Windows + LDAP 无需发送密码

    我正在尝试在 Windows 7 上使用 RabbitMQ 3 6 2 进行 LDAP 身份验证 授权 我已经在应用程序发送用户名 密码的情况下进行了基本身份验证 但密码位于我需要弄清楚如何进行的代码中避免 有没有人在不提供密码的情况下成功
  • Azure Function App Azure 服务总线触发器触发两次

    我使用带有服务总线触发器的 Azure Function Apps 来读取服务总线并对服务总线消息的内容执行操作 服务总线接收 JSON 序列化对象 然后将 JSON 消息反序列化回 Function App 中的对象 然而 由于某种原因
  • MonoGame 中的 ContentLoadException

    我一直在尝试使用 Xamarin Studio 在 MonoGame 中加载纹理 我的代码设置如下 region Using Statements using System using Microsoft Xna Framework usi
  • 字符串常量之前应有非限定 ID

    我目前正在编写一个 C 应用程序 它与 math h 结合实现了振荡器 我拥有的代码应该可以很好地用于该应用程序 尝试编译目标文件 但是我遇到编译器错误 很可能与语法 等有关 我认为这与命名空间有关 错误 终端输出 User Name Ma
  • 为什么 32 位 .NET 进程的引用类型的最小大小为 12 字节

    我正在读专业 Net 性能 https rads stackoverflow com amzn click com 1430244585本书有关参考类型内部结构的部分 它提到 对于 32 位 net 进程 引用类型具有 4 字节的对象头和

随机推荐