OpenMP通用qsort低速加速

时间:2015-05-13 20:23:08

标签: c generics parallel-processing openmp qsort

因此,即使基本的串行qsort大约有10亿个元素,我的实现似乎也会中断。在线大多数并行qsort算法用于排序整数数组和东西,但我希望能够使用自定义比较器(如内置qsort)对任何内容进行排序,因为我想对某些结构进行排序。我使用了12个线程,并且可以验证它们是否正在正确地生成顶部。也许我会产生太多的东西,应该根据递归的深度停止产生新的线程?我知道我的qsort实现是相当基础的,显然内置的qsort已经有很多工作和优化,但我不明白为什么我没有通过并行化获得良好的加速。任何输入都将非常受欢迎,因为我可以在很多方面使用此代码,如果我可以保持它的通用性。谢谢!

void test ( void* data, uint64_t startIdx, uint64_t endIdx, size_t dataSize, int (*cmp)(const void *, const void *) )
{
    #pragma omp parallel
    {
        #pragma omp single nowait
        {
            p_qsort( data, 0, MAX_INTS - 1, sizeof (testint), cmp );
        }
    }
}

void p_qsort ( void* data, uint64_t startIdx, uint64_t endIdx, size_t dataSize, int (*cmp)(const void *, const void *) )
{
    uint64_t idx = p_qsort_partition( data, startIdx, endIdx, dataSize, cmp );

    //Left array
    if ( startIdx < idx - 1 )
    {
        #pragma omp task
        p_qsort( data, startIdx, idx - 1, dataSize, cmp );
    }
    //Right array
    if ( endIdx > idx )
    {
        #pragma omp task
        p_qsort( data, idx, endIdx, dataSize, cmp );
    }
}

void swapVoidElements ( void* el1, void* el2, size_t size )
{
    if ( el1 == el2 )
        return;

    void* temp = malloc( size );

    //temp = el1
    memcpy( temp, el1, size );
    //el1 = el2
    memcpy( el1, el2, size );
    //el2 = temp
    memcpy( el2, temp, size );

    free( temp );
}

uint64_t p_qsort_partition ( void* data, uint64_t left, uint64_t right, size_t dataSize, int (*cmp)(const void *, const void *) )
{
    void* pivotP = getVoidPtr( data, left, dataSize );
    void* pivotCmp = malloc( dataSize );
    memcpy( pivotCmp, pivotP, dataSize );

    while ( left <= right )
    {
        while ( cmp( getVoidPtr( data, left, dataSize ), pivotCmp ) < 0 )
            left++;
        //while ( array[right] > pivot )
        while ( cmp( getVoidPtr( data, right, dataSize ), pivotCmp ) > 0 )
            right--;
        //Swap
        if ( left <= right )
        {
            void* leftP = getVoidPtr( data, left, dataSize );
            void* rightP = getVoidPtr( data, right, dataSize );

            swapVoidElements( leftP, rightP, dataSize );

            left++;
            right--;
        }
    }
    free( pivotCmp );

    return left;
}

void* getVoidPtr ( void* data, uint64_t idx, size_t dataSize )
{
    uint64_t idxNum = idx * dataSize;
    char* test = ((char*) data) + idxNum;

    return (void *) test;
}

1 个答案:

答案 0 :(得分:3)

您为每个创建的OMP任务带来了一些开销,并且您的特定任务变得越来越小。随着每项任务的工作量变小,开销也会成比例地增加。串行QuickSort的一些常见优化技术不仅可以帮助您使用基本算法,还可以解决您的开销问题。

通过切换小子阵列的策略,您可以大大减少所涉及的任务总数,从而减少相关的开销。对于小型子阵列切换到插入排序的常见Quicksort优化,这很好地排列。 &#34; small&#34;的定义是一个可调参数,它的最佳值在某种程度上取决于你的排序,但可能在5到30范围内的某些东西对你来说是一个很好的截止点。进行此类切换时,请在一个任务中执行整个子阵列插入排序。

您可能还会受益于仅针对每对子阵列中较小的子阵列进行递归,而是循环处理较大的子阵列。这将最大递归深度限制为O(log n),否则在最坏的情况下为O(n)。由于每次递归都涉及自己的任务,这也会将所需的任务总数减少至少两倍。

选择好的枢轴是QuickSort性能的核心问题之一,但枢轴选择算法的相对影响是高度依赖数据的。我建议至少一点 little 比总是选择最左边的元素更复杂 - 无论是三分之一中心还是随机枢轴选择都可能在平均情况下产生更好的性能。因为枢轴的选择会影响子阵列的大小,这与创建的任务数量和他们的大小有关,这可能是您并行版本的额外胜利。