递归深度截止策略:并行快速排序

Posted

技术标签:

【中文标题】递归深度截止策略:并行快速排序【英文标题】:Recursion Depth Cut Off Strategy: Parallel QuickSort 【发布时间】:2013-03-25 19:00:43 【问题描述】:

我实现了一个并行快速排序算法。为了避免过多并行线程的开销,我有一个截止策略,当向量大小小于特定阈值时,将并行算法转换为顺序算法。但是,现在我正在尝试根据递归深度设置截止策略。即我希望我的算法在达到某个递归深度时变为顺序。我使用了以下代码,但它不起作用。我不确定如何进行。有什么想法吗?

template <class T>
void ParallelSort::sortHelper(typename vector<T>::iterator start, typename vector<T>::iterator end, int level =0) //THIS IS THE QUICKSoRT INTERFACE

  static int depth =0;

  const int insertThreshold = 20;
  const int threshold = 1000;
  if(start<end)
  
    if(end-start < insertThreshold) //thresholf for insert sort
    
      insertSort<T>(start, end);
    
    else if((end-start) >= insertThreshold && depth<threshold) //threshhold for non parallel quicksort
    
      int part = partition<T>(start,end);
      depth++;
      sortHelper<T>(start, start + (part - 1), level+1);
      depth--;
      depth++;
      sortHelper<T>(start + (part + 1), end, level+1);
      depth--;
    
    else
    
      int part = partition<T>(start,end);
      #pragma omp task
      
        depth++;
        sortHelper<T>(start, start + (part - 1), level+1);
        depth--;
      
      depth++;
      sortHelper<T>(start + (part + 1), end, level+1);
      depth--;
    
  

我尝试了静态变量depth 和非静态变量level,但它们都不起作用。 注意:以上截图仅取决于depth。包含level 以显示尝试的两种方法

【问题讨论】:

“它不起作用”是什么意思?它与预期的行为有何不同? 我的意思是它没有给我加速。但我已经想通了(答案如下)。无论如何,感谢您的关注。 假设单线程操作。那么在对sortHelper&lt;T&gt; 的调用中,depth 不等于level?递归调用次数为level。摆脱static depth,您编写的静态变量在并行代码中几乎没有位置。 深度为 20 的递归二叉树有 100 万个条目。在您的情况下,您将拥有未定义的深度(因为 static depth 的值在您的代码中未定义,因为多个线程正在写入它而没有任何锁定保护)。 谁知道通过启动一群线程会在冷缓存影响上损失多少,以及当两个内核开始争夺包含它们的阵列部分的缓存行时,您会得到多少无用的缓存反弹正在发呆…… 【参考方案1】:

static depth 从两个线程写入会使您的代码执行未指定的行为,因为未指定这些写入的作用。

碰巧的是,您正在传递level,这是您的递归深度。在每个级别,您将线程数加倍——因此等于 6 的级别限制(例如)最多对应于 2^6 个线程。您的代码只有一半并行,因为partition 代码出现在主线程中,因此您可能会少于理论上同时运行的最大线程数。

template <class T>
void ParallelSort::sortHelper(typename vector<T>::iterator start, typename vector<T>::iterator end, int level =0) //THIS IS THE QUICKSoRT INTERFACE


  const int insertThreshold = 20;
  const int treeDepth = 6; // at most 2^6 = 64 tasks
  if(start<end)
  
    if(end-start < insertThreshold) //thresholf for insert sort
    
      insertSort<T>(start, end);
    
    else if(level>=treeDepth) // only 2^treeDepth threads, after which we run in sequence
    
      int part = partition<T>(start,end);
      sortHelper<T>(start, start + (part - 1), level+1);
      sortHelper<T>(start + (part + 1), end, level+1);
    
    else // launch two tasks, creating an exponential number of threads:
    
      int part = partition<T>(start,end);
      #pragma omp task
      
        sortHelper<T>(start, start + (part - 1), level+1);
      
      sortHelper<T>(start + (part + 1), end, level+1);
    
  

【讨论】:

【参考方案2】:

好吧,我想通了。这是我的一个愚蠢的错误。

当堆栈大小大于某个阈值(而不是更小)时,算法应该回退到顺序代码。这样做可以解决问题,并加快速度。

【讨论】:

不,它不起作用。你观察到的是一个小故障。您不能以尝试使用它的方式跨线程使用静态变量 (depth)。你得到的行为完全是虚假的。 你是对的。静态 depth 不是故障安全的。将level 与上述答案一起使用是可行的

以上是关于递归深度截止策略:并行快速排序的主要内容,如果未能解决你的问题,请参考以下文章

快速排序复杂度分析

快速排序

java八种排序算法---快速排序

音视频开发之旅(24) 算法系列-快速排序

归并排序和快速排序

排序算法快速排序