将程序拆分为 4 个线程比单个线程慢

2023-11-23

过去一周我一直在编写一个光线追踪器,并且已经达到了足够多线程的程度。我尝试过使用 OpenMP 来并行化它,但是使用更多线程运行它实际上比使用一个线程运行它要慢。

阅读其他类似的问题,尤其是有关 OpenMP 的问题,一个建议是 gcc 可以更好地优化串行代码。但是运行下面的编译代码export OMP_NUM_THREADS=1速度是原来的两倍export OMP_NUM_THREADS=4。 IE。两次运行时编译的代码相同。

运行程序time:

> export OMP_NUM_THREADS=1; time ./raytracer
real    0m34.344s
user    0m34.310s
sys     0m0.008s


> export OMP_NUM_THREADS=4; time ./raytracer
real    0m53.189s
user    0m20.677s
sys     0m0.096s

用户时间比实际时间少很多,这在使用多核时是不常见的 -user应该大于real因为多个核心同时运行。

我使用 OpenMP 并行化的代码

void Raytracer::render( Camera& cam ) {

    // let the camera know to use this raytracer for probing the scene
    cam.setSamplingFunc(getSamplingFunction());

    int i, j;

    #pragma omp parallel private(i, j)
    {

        // Construct a ray for each pixel.
        #pragma omp for schedule(dynamic, 4)
        for (i = 0; i < cam.height(); ++i) {
            for (j = 0; j < cam.width(); ++j) {
                cam.computePixel(i, j);
            }
        }
    }
}

读书时这个问题我以为我已经找到答案了。它讨论了 gclib rand() 的实现,同步调用自身以保留线程之间随机数生成的状态。我经常使用 rand() 进行蒙特卡罗采样,所以我认为这就是问题所在。我摆脱了对 rand 的调用,用单个值替换它们,但使用多个线程仍然较慢。编辑:哎呀事实证明我没有正确测试这个,这是随机值!

现在这些都已经解决了,我将概述每次调用时所做的事情computePixel,所以希望能找到解决方案。

在我的光线追踪器中,我基本上有一个场景树,其中包含所有对象。这棵树在这段时间内被遍历了很多次computePixel然而,当测试对象的交集时,不会对此树或任何对象进行写入。computePixel本质上是多次读取场景,调用对象上的方法(所有这些都是 const 方法),并在最后将单个值写入其自己的像素数组。这是我所知道的唯一部分,其中多个线程将尝试写入同一成员变量。任何地方都没有同步,因为没有两个线程可以写入像素阵列中的同一单元。

谁能建议可能发生某种争论的地方?值得尝试的事情?

先感谢您。

EDIT:抱歉,我很愚蠢,没有提供有关我的系统的更多信息。

  • 编译器 gcc 4.6(带 -O2 优化)
  • 乌班图 Linux 11.10
  • OpenMP 3
  • Intel i3-2310M 四核 2.1Ghz(目前在我的笔记本电脑上)

计算像素的代码:

class Camera {

    // constructors destructors
    private:
        // this is the array that is being written to, but not read from.
        Colour* _sensor; // allocated using new at construction.
}

void Camera::computePixel(int i, int j) const {

    Colour col;

    // simple code to construct appropriate ray for the pixel
    Ray3D ray(/* params */);
    col += _sceneSamplingFunc(ray); // calls a const method that traverses scene. 

    _sensor[i*_scrWidth+j] += col;
}

从建议来看,可能是树遍历导致速度减慢。其他一些方面:一旦调用采样函数,就会涉及相当多的递归(光线的递归弹跳)——这会导致这些问题吗?


感谢大家的建议,但经过进一步分析并消除其他影响因素后,随机数生成did原来是罪魁祸首。

正如上面问题中所述,rand() 需要跟踪其从一次调用到下一次调用的状态。如果多个线程试图修改此状态,则会导致竞争条件,因此 glibc 中的默认实现是锁定每个通话,使函数成为线程安全的。这对于性能来说是很糟糕的。

不幸的是,我在 stackoverflow 上看到的这个问题的解决方案都是本地的,即处理问题在调用 rand() 的范围内。相反,我提出了一种“快速而肮脏”的解决方案,任何人都可以在他们的程序中使用它来为每个线程实现独立的随机数生成,而不需要同步。

我已经测试了代码,它有效 - 没有锁定,并且调用 threadrand 没有明显的减慢。欢迎指出任何明显的错误。

线程兰德.h

#ifndef _THREAD_RAND_H_
#define _THREAD_RAND_H_

// max number of thread states to store
const int maxThreadNum = 100;

void init_threadrand();

// requires openmp, for thread number
int threadrand();

#endif // _THREAD_RAND_H_

threadrand.cpp

#include "threadrand.h"
#include <cstdlib>
#include <boost/scoped_ptr.hpp>
#include <omp.h>

// can be replaced with array of ordinary pointers, but need to
// explicitly delete previous pointer allocations, and do null checks.
//
// Importantly, the double indirection tries to avoid putting all the
// thread states on the same cache line, which would cause cache invalidations
// to occur on other cores every time rand_r would modify the state.
// (i.e. false sharing)
// A better implementation would be to store each state in a structure
// that is the size of a cache line
static boost::scoped_ptr<unsigned int> randThreadStates[maxThreadNum];

// reinitialize the array of thread state pointers, with random
// seed values.
void init_threadrand() {
    for (int i = 0; i < maxThreadNum; ++i) {
        randThreadStates[i].reset(new unsigned int(std::rand()));
    }
}

// requires openmp, for thread number, to index into array of states.
int threadrand() {
    int i = omp_get_thread_num();
    return rand_r(randThreadStates[i].get());
}

现在您可以初始化线程的随机状态main using init_threadrand(),然后使用得到一个随机数threadrand()在 OpenMP 中使用多个线程时。

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

将程序拆分为 4 个线程比单个线程慢 的相关文章

随机推荐

  • 如何在 MS SQL Server 2008 上设置日期格式

    我想根据模式格式化日期 例如 22 01 2015 或 2016 12 15 在 NET Framework 中 我们有 DateTime gt ToString 方法 它接受格式作为参数 甚至接受 string Format 它的作用相同
  • 在 Web 应用程序中处理时区

    在我们的网络应用程序中 我们需要显示并输入 不同时区不同国家的日期时间信息 目前 我们正在为每个国家 地区维护单独的 Web 服务器和单独的数据库 oracle 11g 我们计划将所有内容合并到一个具有单一数据库 Oracle 11g 的门
  • 如何在此 SSRS 表达式中“指定数据集聚合”?

    我的 SSRS 报告中需要一个行值 该值是根据报告中已使用的几个字段计算得出的 我希望它显示在名为 textboxPercentageValue 的文本框中 用半简单的英语来说 表达式 公式是 If the value of the Wee
  • Android 自定义 ArrayAdapter 在过滤后不刷新

    所以我有一个习惯ArrayAdapter所以我可以使用标题 副标题视图ListView 我有一个EditText它接受一个字符串并过滤适配器 过滤器的工作原理是过滤正确的对象 我可以通过单击它来判断 它以正确的 附加 开始意图 但是 即使过
  • Javascript:关于如何定义新数据类型有哪些指导原则?

    假设您正在创建数据类型并公开其行为 您能否举例说明何时使用 一个功能和新功能 define new data type var CustomDataType function this a whatever this doX functio
  • 设置内联元素的宽度

    您可以设置内联元素的宽度 例如 span em and strong 但在放置它们之前您不会注意到任何效果 a 我以为内联元素的宽度不能设置 b 假设可以设置宽度 在我们定位内联元素之前 我们不会注意到任何效果 因此我们指定的宽度 位置如何
  • “撤消”功能的最佳设计模式[重复]

    这个问题在这里已经有答案了 可能的重复 撤消引擎的设计模式 一般来说 您如何处理应用程序中支持 撤消 功能的问题 我曾经开发过网络应用程序和桌面应用程序 但我从来没有真正对我制作的任何 撤消 系统感到满意 我相信应该是Command设计模式
  • Angular2.js 与 Angular2.dev.js

    我想知道之间的差异angular2 js and angular2 dev js 当然还有更多文件 例如 router dev js and router js还有 我的问题是为什么有两个版本 它们之间有什么区别 angular2 dev
  • Ruby on Rails 使用外键删除固定装置

    我在使用使用外键的装置设置测试时遇到问题 如果有人能帮助我理解这一点 我将不胜感激 比方说 user type模型有一个参考 role模型 当测试执行时 测试数据库中的所有数据都被删除并再次重新插入 Rails 首先从角色模型中删除数据 而
  • 在 JS 中访问 Asp.Net Session 变量

    我无法访问 js 文件中的变量 我在页面顶部的代码是 然后我想访问我的 js 文件中的权限 我现在只想提醒您这一点 我能做到吗 thanks 您必须将会话值存储在隐藏字段中 之后您可以在 JS 中访问隐藏的 FieldValue
  • php中的应用范围

    我需要在所有请求之间共享相同的数组对象 无论来自同一浏览器 用户的请求如何 php 中是否有任何应用程序范围可以存储该数组对象 我正在使用 php 5 x 如果您想在每个用户的所有请求中共享它 使用会话可能是要走的路 如果您想在所有用户的所
  • 从 dict 创建 ORM 对象并添加到会话中

    假设我有一个User具有属性的模型id name email和一段关系languages 是否有可能创建一个User来自现有数据的实例 其行为就像我查询它一样dbsession query User get 42 我的意思特别是我希望能够访
  • 在 Qt MainWindow 上设置 WA_DeleteOnClose 属性时,删除 ui 指针时程序崩溃

    我已经设置了WA DeleteOnClose主窗口中的小部件属性 setAttribute Qt WA DeleteOnClose 但是 每当我关闭该主窗口时 我都会在其析构函数中遇到段错误 该析构函数只包含delete ui 简而言之 在
  • Pandas:将日期范围解压缩为单个日期

    Dataset 我有一个 1GB 的股票数据集 其中包含日期范围内的值 日期范围没有重叠 数据集按 股票代码 开始日期 排序 gt gt gt df head start date end date val ticker AAPL 2014
  • SonarQube 重构此方法以降低其认知复杂性

    我有以下实用方法 并且我正在使用多个 if 语句并遇到认知复杂性问题 我浏览了一些链接 但我无法理解应该如何更改代码而不影响此方法的用户 public static boolean isWrapperValid WrapperClass w
  • 如何在gtk3-python中执行后台任务?

    我有这个主线 Gui py from gi repository import Gtk Gdk import Process import gobject class gui def init self self window Gtk Wi
  • JavaScript 日期差异

    我在使用 DateDiff 函数时遇到问题 我试图找出两个日期 时间之间的差异 我读过这篇文章 在Javascript中计算日期差异的最佳方法是什么 我还看了这个教程 http www javascriptkit com javatutor
  • 如何查找当前页面使用了哪些CSS文件[重复]

    这个问题在这里已经有答案了 我的页面上有很多 CSS 文件 但其中很多文件并没有被样式使用 是否可以确定哪些文件被页面使用 哪些文件不被页面使用 Use http getfirebug com 来调试页面 当查看 css 时 它将引用使用的
  • ng 服务无法在 Docker 容器中工作

    我有这个Docker Compose 配置我只需创建一个 NodeJS 容器并在其中安装 Angular CLI After a docker compose up d 我可以在容器内通过 SSH 连接docker compose run
  • 将程序拆分为 4 个线程比单个线程慢

    过去一周我一直在编写一个光线追踪器 并且已经达到了足够多线程的程度 我尝试过使用 OpenMP 来并行化它 但是使用更多线程运行它实际上比使用一个线程运行它要慢 阅读其他类似的问题 尤其是有关 OpenMP 的问题 一个建议是 gcc 可以