C OpenMP 并行快速排序

2023-11-27

在 C++ 中使用 openMP 时,我再次陷入困境。这次我尝试实现并行快速排序。

Code:

#include <iostream>
#include <vector>
#include <stack>
#include <utility>
#include <omp.h>
#include <stdio.h>

#define SWITCH_LIMIT 1000

using namespace std;

template <typename T>
void insertionSort(std::vector<T> &v, int q, int r)
{
    int key, i;
    for(int j = q + 1; j <= r; ++j)
    {
        key = v[j];
        i = j - 1;
        while( i >= q && v[i] > key )
        {
            v[i+1] = v[i];
            --i;
        }
        v[i+1] = key;
    }
}

stack<pair<int,int> > s;

template <typename T>
void qs(vector<T> &v, int q, int r)
{
    T pivot;
    int i = q - 1, j = r;
    //switch to insertion sort for small data
    if(r - q < SWITCH_LIMIT) 
    {
        insertionSort(v, q, r);
        return;
    }

    pivot = v[r];
    while(true)
    {
        while(v[++i] < pivot);
        while(v[--j] > pivot);
        if(i >= j) break;
        std::swap(v[i], v[j]); 
    }
    std::swap(v[i], v[r]);

    #pragma omp critical
    {
        s.push(make_pair(q, i - 1));
        s.push(make_pair(i + 1, r));        
    }
}

int main()
{
    int n, x;
    int numThreads = 4, numBusyThreads = 0;
    bool *idle = new bool[numThreads];
    for(int i = 0; i < numThreads; ++i)
        idle[i] = true;
    pair<int, int> p;
    vector<int> v;
    cin >> n;
    for(int i = 0; i < n; ++i)
    {
        cin >> x;
        v.push_back(x);
    }
    cout << v.size() << endl;
    s.push(make_pair(0, v.size()));

    #pragma omp parallel shared(s, v, idle, numThreads, numBusyThreads, p) 
    {
        bool done = false;
        while(!done) 
        {
            int id = omp_get_thread_num();
            #pragma omp critical
            {
                if(s.empty() == false && numBusyThreads < numThreads) 
                {
                    ++numBusyThreads;
                    //the current thread is not idle anymore
                    //it will get the interval [q, r] from stack
                    //and run qs on it
                    idle[id] = false;
                    p = s.top();                    
                    s.pop();
                }
                if(numBusyThreads == 0)
                {
                    done = true;
                }
            }
            if(idle[id] == false)
            {

                qs(v, p.first, p.second);
                idle[id] = true;
                #pragma omp critical 
                --numBusyThreads;
            }

        }
    }
    return 0;
}

算法:

为了将 openMP 用于递归函数,我使用堆栈来跟踪 qs 函数应运行的下一个间隔。我手动添加第一个间隔 [0, size],然后在堆栈中添加新间隔时让线程开始工作。

问题:

程序结束得太早,如果您查看代码,则在创建第一组间隔([q,i - 1],[i + 1,r])后不对数组进行排序。我的猜测是获得工作的线程,考虑默认情况下共享的快速排序函数的局部变量(代码中的 qs ),因此它们将它们搞乱并且在堆栈中不添加间隔。

我如何编译:

g++ -o qs qs.cc -Wall -fopenmp

我如何跑步:

./qs < in_100000 > out_100000

其中 in_100000 是一个文件,第一行包含 100000,下一行包含 100k 个整数,并以空格分隔。

我在 Linux 上使用 gcc 4.5.2

感谢您的帮助,

Dan


我实际上并没有运行你的代码,但我立即看到一个错误p,这应该是private not shared。并行调用qs: qs(v, p.first, p.second);将举行比赛p,导致不可预测的行为。局部变量位于qs应该没问题,因为所有线程都有自己的堆栈。然而,总体方法是好的。你走在正确的轨道上。


以下是我对并行快速排序的实现的一般评论。快速排序本身就是尴尬地平行,这意味着不需要同步。的递归调用qs在分区数组上是令人尴尬的并行。

然而,并行性暴露在递归的形式。如果您只是使用nestedOpenMP 中的并行性,最终会在一秒钟内拥有数千个线程。不会获得加速。因此,大多数情况下您需要将递归算法转变为交互式算法。然后,您需要实现一种工作队列。这就是你的方法。而且,这并不容易。

对于您的方法,有一个很好的基准:OmpSCR。您可以在以下位置下载http://sourceforge.net/projects/ompscr/

在基准测试中,有多个版本的基于 OpenMP 的快速排序。其中大多数与您的类似。然而,为了增加并行性,必须最小化全局队列上的争用(在您的代码中,它是s)。因此,可能有一些优化,例如拥有本地队列。尽管算法本身是纯粹并行的,但实现可能需要同步工件。而且,最重要的是,很难获得加速。


但是,您仍然可以通过两种方式直接在 OpenMP 中使用递归并行性:(1) 限制线程总数,以及 (2) 使用 OpenMP 3.0 的task.

这是第一种方法的伪代码(这仅基于 OmpSCR 的基准):

void qsort_omp_recursive(int* begin, int* end)
{
  if (begin != end) {
    // Partition ...

    // Throttling
    if (...)  {
      qsort_omp_recursive(begin, middle);
      qsort_omp_recursive(++middle, ++end);
    } else {

#pragma omp parallel sections nowait
      {
#pragma omp section
        qsort_omp_recursive(begin, middle);
#pragma omp section
        qsort_omp_recursive(++middle, ++end);
      }
    }
  }
}

为了运行此代码,您需要调用omp_set_nested(1) and omp_set_num_threads(2)。代码非常简单。我们只是在工作划分上产生两个线程。然而,我们插入了一个简单的限制逻辑来防止过多的线程。请注意,我的实验显示这种方法的速度相当不错。


最后,您可以使用 OpenMP 3.0task,其中任务是逻辑上并发的工作。在上述所有 OpenMP 方法中,每个并行构造都会产生两个physical线程。您可能会说任务与工作线程之间存在严格的一对一映射。然而,task将逻辑任务和工作人员分开。

因为OpenMP 3.0还没有流行,所以我会使用西尔克加,这非常适合表达这种嵌套和递归并行性。在 Cilk Plus 中,并行化非常简单:

void qsort(int* begin, int* end)
{
  if (begin != end) {
    --end;
    int* middle = std::partition(begin, end,
      std::bind2nd(std::less<int>(), *end));
    std::swap(*end, *middle);

    cilk_spawn qsort(begin, middle);
    qsort(++middle, ++end);
    // cilk_sync; Only necessay at the final stage.
  }
}

我从 Cilk Plus 的示例代码中复制了此代码。您将看到一个关键字cilk_spawn是并行快速排序的一切。我将跳过 Cilk Plus 和 spawn 关键字的解释。然而,它很容易理解:两个递归调用被声明为逻辑上并发的任务。每当递归发生时,就会创建逻辑任务。但是,Cilk Plus 运行时(它实现了高效的工作窃取调度程序)将处理各种脏作业。它以最佳方式对并行任务进行排队并映射到工作线程。

请注意,OpenMP 3.0 的task本质上与 Cilk Plus 的方法类似。我的实验表明,相当不错的加速是可行的。我在 8 核机器上获得了 3~4 倍的加速。而且,加速是规模化的。 Cilk Plus 的绝对加速比 OpenMP 3.0 更高。

Cilk Plus(和 OpenMP 3.0)的方法和您的方法本质上是相同的:并行任务和工作负载分配的分离。然而,有效实施却非常困难。例如,您必须减少争用并使用无锁数据结构。

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

C OpenMP 并行快速排序 的相关文章

  • 结构化绑定中缺少类型信息

    我刚刚了解了 C 中的结构化绑定 但有一件事我不喜欢 auto x y some func is that auto正在隐藏类型x and y 我得抬头看看some func的声明来了解类型x and y 或者 我可以写 T1 x T2 y
  • 在模板类中声明模板友元类时出现编译器错误

    我一直在尝试实现我自己的链表类以用于教学目的 我在迭代器声明中指定了 List 类作为友元 但它似乎无法编译 这些是我使用过的 3 个类的接口 Node h define null Node
  • STL 迭代器:前缀增量更快? [复制]

    这个问题在这里已经有答案了 可能的重复 C 中的预增量比后增量快 正确吗 如果是 为什么呢 https stackoverflow com questions 2020184 preincrement faster than postinc
  • 通过引用传递 [C++]、[Qt]

    我写了这样的东西 class Storage public Storage QString key const int value const void add item QString int private QMap
  • 机器Epsilon精度差异

    我正在尝试计算 C 中双精度数和浮点数的机器 epsilon 值 作为学校作业的一部分 我在 Windows 7 64 位中使用 Cygwin 代码如下 include
  • 随着时间的推移,添加到 List 变得非常慢

    我正在解析一个大约有 1000 行的 html 表 我从一个字符串中添加 10 个字符串 td 每行到一个list td
  • 如何连接重叠的圆圈?

    我想在视觉上连接两个重叠的圆圈 以便 becomes 我已经有部分圆的方法 但现在我需要知道每个圆的重叠角度有多大 但我不知道该怎么做 有人有主意吗 Phi ArcTan Sqrt 4 R 2 d 2 d HTH Edit 对于两个不同的半
  • 用于 FTP 的文件系统观察器

    我怎样才能实现FileSystemWatcherFTP 位置 在 C 中 这个想法是 每当 FTP 位置添加任何内容时 我都希望将其复制到我的本地计算机 任何想法都会有所帮助 这是我之前问题的后续使用 NET 进行选择性 FTP 下载 ht
  • WPF 数据绑定到复合类模式?

    我是第一次尝试 WPF 并且正在努力解决如何将控件绑定到使用其他对象的组合构建的类 例如 如果我有一个由两个单独的类组成的类 Comp 为了清楚起见 请注意省略的各种元素 class One int first int second cla
  • 重载 (c)begin/(c)end

    我试图超载 c begin c end类的函数 以便能够调用 C 11 基于范围的 for 循环 它在大多数情况下都有效 但我无法理解和解决其中一个问题 for auto const point fProjectData gt getPoi
  • WcfSvcHost 的跨域异常

    对于另一个跨域问题 我深表歉意 我一整天都在与这个问题作斗争 现在已经到了沸腾的地步 我有一个 Silverlight 应用程序项目 SLApp1 一个用于托管 Silverlight SLApp1 Web 的 Web 项目和 WCF 项目
  • 为什么这个字符串用AesCryptoServiceProvider第二次解密时不相等?

    我在 C VS2012 NET 4 5 中的文本加密和解密方面遇到问题 具体来说 当我加密并随后解密字符串时 输出与输入不同 然而 奇怪的是 如果我复制加密的输出并将其硬编码为字符串文字 解密就会起作用 以下代码示例说明了该问题 我究竟做错
  • 如何定义一个可结构化绑定的对象的概念?

    我想定义一个concept可以检测类型是否T can be 结构化绑定 or not template
  • x:将 ViewModel 方法绑定到 DataTemplate 内的事件

    我基本上问同样的问题这个人 https stackoverflow com questions 10752448 binding to viewmodels property from a template 但在较新的背景下x Bind V
  • C# xml序列化必填字段

    我需要将一些字段标记为需要写入 XML 文件 但没有成功 我有一个包含约 30 个属性的配置类 这就是为什么我不能像这样封装所有属性 public string SomeProp get return someProp set if som
  • 实例化类时重写虚拟方法

    我有一个带有一些虚函数的类 让我们假设这是其中之一 public class AClassWhatever protected virtual string DoAThingToAString string inputString retu
  • C++ 中的参考文献

    我偶尔会在 StackOverflow 上看到代码 询问一些涉及函数的重载歧义 例如 void foo int param 我的问题是 为什么会出现这种情况 或者更确切地说 你什么时候会有 对参考的参考 这与普通的旧参考有何不同 我从未在现
  • 类型或命名空间“MyNamespace”不存在等

    我有通常的类型或命名空间名称不存在错误 除了我引用了程序集 using 语句没有显示为不正确 并且我引用的类是公共的 事实上 我在不同的解决方案中引用并使用相同的程序集来执行相同的操作 并且效果很好 顺便说一句 这是VS2010 有人有什么
  • Mono 应用程序在非阻塞套接字发送时冻结

    我在 debian 9 上的 mono 下运行一个服务器应用程序 大约有 1000 2000 个客户端连接 并且应用程序经常冻结 CPU 使用率达到 100 我执行 kill QUIT pid 来获取线程堆栈转储 但它总是卡在这个位置
  • 如何确定 CultureInfo 实例是否支持拉丁字符

    是否可以确定是否CultureInfo http msdn microsoft com en us library system globalization cultureinfo aspx我正在使用的实例是否基于拉丁字符集 我相信你可以使

随机推荐

  • 如何以最小的影响重新启动 CSS 动画

    有没有一种方法可以重新启动 CSS 动画 而无需克隆元素 回流 DOM 等待 setTimeout onAnimationEnd EDIT 无需 jQuery 或检查 我基本上只是在下一个绘制的帧处重新启动动画 此方法不会克隆任何元素 重排
  • 处理带有未知 IPv6 扩展标头的数据包

    Question 是否应该丢弃带有未知 IPv6 扩展标头的数据包 Details 我无法通过检查找到这个问题的答案RFC 这本书IPv6 要点第 22 页指出 如果节点需要下一个标头但无法识别下一个标头字段中的值 则需要丢弃该数据包并向数
  • 在C#中,如何可靠地杀死进程树[重复]

    这个问题在这里已经有答案了 在 C 中 我们使用以下代码来终止进程树 有时有效 有时无效 可能与 Windows 7 和 或 64 位有关 它找到给定进程的子进程的方法是调用GetProcesses获取系统中的所有进程 然后调用NtQuer
  • Airplay 按钮未显示在 AVPlayer 的播放器控件中

    我使用 AVPlayer 和 AVPlayerViewController 创建了一个视频播放器 我已经设定 allowsExternalPlayback 财产至真也 usesExternalPlaybackWhileExternalScr
  • apk (.apk) 和应用程序包 (.aab) 之间的区别

    最近谷歌推出了一个新功能app bundle这与 APK 的概念非常相似 除了灵活性和架构差异之外 我阅读了很多博客 文章来了解应用程序包与 APK 文件相比如何在设备中工作 App Bundle 的实际内部工作流程是什么 它如何在从 Go
  • 将标记替换为 html 内容

    我一直在搜索 Google Map API V3 文档 但找不到任何方法来使用我自己的 html 内容而不是图像在地图上创建自定义图标 我想显示一个动态标记 可以显示文本或我想要的任何内容 例如 div class marker Dynam
  • Java打印包含多个整数的字符串

    今天刚开始学习java 似乎无法弄清楚这一点 我正在关注 learnjavaonline org 上的教程 该教程会教您一些内容 然后要求您编写代码来执行特定操作 然后检查输出以查看其是否正确 问题是 如果它不正确 它不会说明原因 也不会为
  • Anaconda 安装后运行 pyinstaller 会导致 ImportError: no Module named 'pefile'

    I did conda install c acellera pyinstaller 3 2 3 as per Anaconda 的网站看起来它安装正确 但如果我尝试通过 cmd 运行它 我会得到以下信息 C Users Cornelis
  • 增加该值(如果存在),否则在 DynamoDB 中添加新条目

    我有一个 DynamoDB 表 其列和主键为ipAddress IP地址 visits 我从我的 React 网站获取用户的 IP 地址 并通过 Lambda 函数和 API Gateway POST 请求将其插入到 DynamoDB 如果
  • UIImages 的图像尺寸是 1024 x 1024?

    苹果文档指出 您应该避免创建大于 1024 x 的 UIImage 对象 大小为 1024 除了大量的内存之外 这样的图像还会 消耗 使用图像作为纹理时可能会遇到问题 在 OpenGL ES 中或将图像绘制到视图或图层时 这个尺寸 如果您正
  • Boost.Test 检查指针是否为空

    我有以下测试 BOOST CHECK NE pointer nullptr 编译失败的原因是 xxx include boost test tools detail print helper hpp 50 14 错误 operator 出了
  • 如果Java中的反射会减慢命令的执行速度,为什么还有这么多框架使用它?

    根据我的理解 使用 Java 反射 API 会按命令减慢代码执行速度 但后来我看到它在 Java 世界的很多地方都在使用 仅举几例 注释 Spring框架 AOP 休眠 MyBatis 这意味着我错过了一些关于 java 反射 又名优化技术
  • 使用 PostGIS 配置 Amazon Elastic Beanstalk

    有谁有使用 PostGIS 设置 Amazon Elastic Beanstalk 的经验 以便我可以利用 Geodjango 默认设置 RDS 以 MySQL 为特色 当前不支持开箱即用的许多功能 1 PostgreSQL PostGIS
  • 如何在 django 中验证 json 对象

    我正在使用 AJAX 向 django 视图提交 JSON JSON 如下所示 code 9910203040 required name Abc required payments amount 300 required name efg
  • winform 友好的类名

    我有一个 C winform 应用程序 当使用间谍 时 它给出 WindowsForms10 Window 8 app 0 33c0d9d 作为类名 有没有办法将其更改为更友好的内容 没有 最后一个十六进制数字是拥有该窗口的 AppDoma
  • 如何在 vim 中删除下一个字符(不是当前字符!)?

    我经常发现自己需要删除光标后面的字符 而不是当前字符 在 vim 的正常模式下执行此操作的最短方法是什么 lx会成功 或者lxh如果您想将光标返回到原始位置 它只是向前移动光标并删除其下方的字符 如果这还不够短 您可以将其映射到单个按键 m
  • Linux集群,如何“仅仅”锁定一个文件?

    在 Bash 中 我试图使函数 getLock 与不同的锁名称一起使用 function getLock getLock FILE 1 getLock OP 2 case getLock OP in LOCK UN flock u getL
  • 如何表示当前英国时间?

    我在服务器和客户端之间转换日期时遇到问题 两者都在德国运行 客户端计算机上的区域设置可以设置为英国或德国 我从服务器收到一个 CET 格式的日期 我需要在 UI 上将此时间表示为英国时间 例如 从服务器收到的时间 如 01 07 2010
  • Jenkins/fastlane - 没有找到本地代码签名身份

    我在使用 Jenkins 的 fastlane 时遇到问题 在终端中运行此命令有效fastlane provide crashlytics build testing false check xcode false env xxx 但是詹金
  • C OpenMP 并行快速排序

    在 C 中使用 openMP 时 我再次陷入困境 这次我尝试实现并行快速排序 Code include