Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Multithreading 并行前缀算法的调度_Multithreading_Algorithm_Parallel Processing - Fatal编程技术网

Multithreading 并行前缀算法的调度

Multithreading 并行前缀算法的调度,multithreading,algorithm,parallel-processing,Multithreading,Algorithm,Parallel Processing,我想实现并行前缀算法(使用POSIX线程或OpenMP)。我有并行算法的伪代码,但我不知道如何实现它 parallelPrefix(array) if array.length = 1 result[1] = array[1] return result[] for 1 <= i <= floor(n / 2) pardo temp[i] = array[2 * i - 1] + array[2 * i] pre

我想实现并行前缀算法(使用POSIX线程或OpenMP)。我有并行算法的伪代码,但我不知道如何实现它

parallelPrefix(array)
    if array.length = 1
        result[1] = array[1]
        return result[]
    for 1 <= i <= floor(n / 2) pardo
        temp[i] = array[2 * i - 1] + array[2 * i]
    prefixSums[] = parallelPrefix(temp[])
    for 1 <= i <= n pardo
        if i is even result[i] = prefixSums[i / 2]
        if i = 1     result[i] = array[1]
        if i is odd  result[i] = prefixSums[(i - 1) / 2] + temp[i]
     return result[]
并行前缀(数组) 如果array.length=1 结果[1]=数组[1] 返回结果[] 对于算法的1个并行化潜能 您所介绍的算法对于并行化来说是非常具有挑战性的:对于循环实例,有很多新的
,而每次迭代都很少做什么。我不得不使用一个巨大的阵列来实现任何加速,这个阵列太大了,以至于我几乎耗尽了内存(进程占用了约13GB)。但您提到要实现一个不同的就地算法。这将大大减少内存占用,并且您将能够针对足够大的问题执行该算法,从而显著提高速度

错误 我认为,你的算法有一些错误。首先,
n
从未定义过。我假设,它指的是
array.length
,这样算法才有意义。第二,线路

if i is odd  result[i] = prefixSums[(i - 1) / 2] + temp[i]
应该是

if i is odd  result[i] = prefixSums[(i - 1) / 2] + array[i]
OpenMP 线程创建 在OpenMP中,您不必自己负责线程的创建和调度,因此您的并行循环

for 1 <= i <= n pardo
在Fortran中(您尚未指定要使用的编程语言)。转换另一个并行循环的方式相同

这将创建OpenMP认为足够多的线程,通常是操作系统看到的处理器数量(包括完整的处理器、多核处理器中的内核和虚拟超线程内核)。然而,这可以通过不同的方式来覆盖

选择时间表 迭代将由OpenMP分发到线程。默认情况下,这是使用静态计划实现的,该计划将整个循环范围划分为每个线程的块。如果您的迭代在计算工作中非常不平衡,并且您的迭代太少,无法平衡,那么您可以使用OpenMP指令中的
schedule(dynamic)
子句请求动态计划。但我认为你的情况没有必要这么做

开销 因此,正如您所提到的,每个迭代不会有一个线程,而是在特定系统上有意义的线程数。然而,即使在这种情况下,创建和销毁线程也可能会有一些开销,这种开销可能会消耗您从并行化中获得的东西,特别是对于短循环/廉价循环

在您的示例中,小型
n
就是这种情况。假设
n
是指
array.length
(否则您的算法就没有意义了),当您接近递归中的基本情况时,可能会出现这种情况

我不会太担心这个问题,因为在你的算法中没有太多这样的情况。在数组上的其他类型的递归算法(例如分治算法)中,对原始数组()的每个元素执行基本情况。与这些算法不同,您的递归是线性的,因此您将很少执行这些关键的执行

此外,一些OpenMP实现决定保持一组线程处于活动状态,以防万一,并在需要时使用它们。在这种情况下,您根本不必担心开销

如果您决定了开销问题,并且您的OpenMP实现本身无法使线程保持活动状态,那么您有2个选项:

停用小型
n的线程

您可以通过使用
num_threads
指令在迭代次数较少的情况下覆盖线程数来停用这些简单执行的并行执行,例如

procs = n > 100000 ? omp_get_num_procs() : 1;
#pragma omp parallel for num_threads(procs)
for (i = 1; i <= n; i++) {
    /* loop body */
}
通过将easy案例中的线程数设置为1,您可以告诉OpenMP不要在这些案例中创建任何额外的线程。所有工作都将在(已经存在的)主线程中执行。这几乎消除了这些情况下的所有开销

您必须设置哪一个限制,以及这是否会提高性能,这在很大程度上取决于您的机器和OpenMP实现。我用来测试性能的GCC4.9.2无论如何都会重用OpenMP线程。在这个设置中,我无法观察到这个技巧有任何改进。但这对于其他OpenMP实现可能有所不同

使线程保持活动状态

如果您的初始
n
非常小,并且您仍然希望尝试从并行化中获得一些加速,那么您可以强制OpenMP使线程在并行部分之间保持活动状态。这可以通过一个
omp parallel
部分来完成,该部分包含所有
omp for
循环执行

然而,所有这些是否值得付出努力,在很大程度上取决于您使用的OpenMP实现。同样,我用来测试性能的GCC4.9.2重用了它的OpenMP线程。在这个设置中,拉出
omp parallel
指令是没有必要的,因此没有任何帮助,甚至会稍微降低性能。但这对于其他OpenMP实现可能有所不同

C++中的示例代码:

#include <vector>

std::vector<int> parallelPrefix_outsideParallel(const std::vector<int> &array) {
    int n = array.size();
    std::vector<int> result(n);
    if (n == 1) {
        result[0] = array[0];
        return result;
    }
    std::vector<int> temp(n / 2);
    std::vector<int> prefixSums;
    #pragma omp parallel shared(array, result, prefixSums) firstprivate(n)
    {
        #pragma omp for
        for (int i = 0; i < n / 2; i++) {
            temp[i] = array[2 * i] + array[2 * i + 1];
        }
        #pragma omp single
        {
            prefixSums = parallelPrefix_outsideParallel(temp);
        }
        #pragma omp for
        for (int i = 0; i < n; i++) {
            if (i % 2 == 1) {
                result[i] = prefixSums[i / 2];
            } else if (i == 0) {
                result[i] = array[0];
            } else if (i % 2 == 0) {
                result[i] = prefixSums[i / 2 - 1] + array[i];
            }
        }
    } // end parallel
    return result;
}
这将执行从索引1到
n
的整个循环,为每个线程划分为块。分布将尽可能均匀,每个线程的迭代次数为
n/numThreads
。如果无法使其完全均衡,则第一个
n%numThreads
线程将比其余线程多接收1次迭代(这就是
min(threadId,n%numThreads)
表达式的目的)

动态调度 动态计划可以通过维护由中心实例管理的迭代队列(例如,实现为列表)来实现。每个线程都有一个
while
循环,该循环要求新的迭代(或更高效的迭代:小的迭代块)并执行它们。当中央队列为空时,除主thr之外的所有线程
!$omp parallel do private(i)
do i=1,n
    ! loop body
end do
!$omp end parallel do 
procs = n > 100000 ? omp_get_num_procs() : 1;
#pragma omp parallel for num_threads(procs)
for (i = 1; i <= n; i++) {
    /* loop body */
}
std::vector<int> parallelPrefix_withNumThreads(const std::vector<int> &array) {
    int n = array.size();
    int procs = n > 100 ? omp_get_num_procs() : 1;
    std::vector<int> result(n);
    if (n == 1) {
        result[0] = array[0];
        return result;
    }
    std::vector<int> temp(n / 2);
    #pragma omp parallel for shared(temp, array) num_threads(procs)
    for (int i = 0; i < n / 2; i++) {
        temp[i] = array[2 * i] + array[2 * i + 1];
    }
    std::vector<int> prefixSums = parallelPrefix_withNumThreads(temp);
    #pragma omp parallel for shared(temp, array) num_threads(procs)
    for (int i = 0; i < n; i++) {
        if (i % 2 == 1) {
            result[i] = prefixSums[i / 2];
        } else if (i == 0) {
            result[i] = array[0];
        } else if (i % 2 == 0) {
            result[i] = prefixSums[i / 2 - 1] + array[i];
        }
    }
    return result;
}
#include <vector>

std::vector<int> parallelPrefix_outsideParallel(const std::vector<int> &array) {
    int n = array.size();
    std::vector<int> result(n);
    if (n == 1) {
        result[0] = array[0];
        return result;
    }
    std::vector<int> temp(n / 2);
    std::vector<int> prefixSums;
    #pragma omp parallel shared(array, result, prefixSums) firstprivate(n)
    {
        #pragma omp for
        for (int i = 0; i < n / 2; i++) {
            temp[i] = array[2 * i] + array[2 * i + 1];
        }
        #pragma omp single
        {
            prefixSums = parallelPrefix_outsideParallel(temp);
        }
        #pragma omp for
        for (int i = 0; i < n; i++) {
            if (i % 2 == 1) {
                result[i] = prefixSums[i / 2];
            } else if (i == 0) {
                result[i] = array[0];
            } else if (i % 2 == 0) {
                result[i] = prefixSums[i / 2 - 1] + array[i];
            }
        }
    } // end parallel
    return result;
}
int getStartIndex(int threadId, int numThreads, int n) {
    return 1 + threadId * n / numThreads + min(threadId, n % numThreads);
}

int startIndex = getStartIndex(threadId, numThreads, n);
int nextThreadStartIndex = getStartIndex(threadId + 1, numThreads, n);

for (int i = startIndex; i < nextThreadStartIndex; i++) {
    // loop body
}