Algorithm 最大单次销售利润-并行化版本
我正在尝试使用OpenMP API(或pthreads)来并行化以下代码。其时间复杂度为O(n)。 我想知道是否有可能将条目数组划分为Algorithm 最大单次销售利润-并行化版本,algorithm,parallel-processing,pthreads,openmp,Algorithm,Parallel Processing,Pthreads,Openmp,我正在尝试使用OpenMP API(或pthreads)来并行化以下代码。其时间复杂度为O(n)。 我想知道是否有可能将条目数组划分为X块(X=线程数),并为每个线程并行执行该过程 这是一个非常经典的算法问题,到目前为止,我还没有看到任何人试图实现一个并行版本 重要提示:简单的简化并不能解决这个问题,因为我只是从左到右读取数组。所以并行化不是很明显 #include<stdio.h> /* The function assumes that there are at least
X
块(X
=线程数),并为每个线程并行执行该过程
这是一个非常经典的算法问题,到目前为止,我还没有看到任何人试图实现一个并行版本
重要提示:简单的简化并不能解决这个问题,因为我只是从左到右读取数组。所以并行化不是很明显
#include<stdio.h>
/* The function assumes that there are at least two
elements in array.
The function returns a negative value if the array is
sorted in decreasing order.
Returns 0 if elements are equal */
int maxDiff(int arr[], int arr_size)
{
int max_diff = arr[1] - arr[0];
int min_element = arr[0];
int i;
for(i = 1; i < arr_size; i++)
{
if(arr[i] - min_element > max_diff)
max_diff = arr[i] - min_element;
if(arr[i] < min_element)
min_element = arr[i];
}
return max_diff;
}
#包括
/*该函数假定至少有两个
数组中的元素。
如果数组为空,则函数返回负值
按降序排序。
如果元素相等,则返回0*/
int-maxDiff(int-arr[],int-arr\u-size)
{
int max_diff=arr[1]-arr[0];
int min_元素=arr[0];
int i;
对于(i=1;i最大差异)
max_diff=arr[i]-min_元素;
if(arr[i]
我对OpenMP尤其不确定,但这里有一个关联运算符,用于解决并行性问题
struct intermediate {
int min_elem;
int max_elem;
int max_diff;
};
使用此函数准备单例列表
struct intermediate singleton(int x) {
return (struct intermediate){x, x, INT_MIN};
}
struct intermediate combine(struct intermediate a, struct intermediate b) {
return (struct intermediate){min(a.min_elem, b.min_elem),
max(a.max_elem, b.max_elem),
max(max(a.max_diff, b.max_diff),
b.max_elem - a.min_elem)};
}
使用此功能组合两个相邻的中间体
struct intermediate singleton(int x) {
return (struct intermediate){x, x, INT_MIN};
}
struct intermediate combine(struct intermediate a, struct intermediate b) {
return (struct intermediate){min(a.min_elem, b.min_elem),
max(a.max_elem, b.max_elem),
max(max(a.max_diff, b.max_diff),
b.max_elem - a.min_elem)};
}
可以这样画出一个可能的评估策略
C
/ \
C \
/ \ \
/ \ \
/ \ \
C C \
/ \ / \ \
S S S S S
| | | | |
0 1 2 3 4
这里C
表示联合,而S
表示单件。因为联合收割机是关联的,所以任何二叉树都可以。这是另一个策略
C
/ \
/ \
/ \
/ C
/ / \
C / C
/ \ / / \
S S S S S
| | | | |
0 1 2 3 4
由于数据依赖性和较低的计算要求,这在多核中不太可能给您带来很大的加速-但是,您可以通过在数组的每个块中计算局部最小值、最大值和局部区域最佳值,然后跨块进行比较。由于最后一步的原因,这在O(N)+O(P2)时间内运行,进一步限制了可伸缩性
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include <limits.h>
#include <omp.h>
void tick(struct timeval *t);
double tock(const struct timeval * const t);
unsigned int maxDiff(const int * const arr, const int arr_size)
{
int max_diff = arr[1] - arr[0];
int min_element = arr[0];
int i;
for(i = 1; i < arr_size; i++)
{
if(arr[i] - min_element > max_diff)
max_diff = arr[i] - min_element;
if(arr[i] < min_element)
min_element = arr[i];
}
return max_diff;
}
unsigned int ompMaxDiff(const int * const arr, const int arr_size)
{
int nthreads=omp_get_max_threads();
int maxes[nthreads];
int mins [nthreads];
unsigned int best = 0;
for (int i=0; i<nthreads; i++) {
mins [i] = INT_MAX;
maxes[i] = INT_MIN;
}
#pragma omp parallel num_threads(nthreads) default(none) shared(mins, maxes) reduction(max:best)
{
int idx = omp_get_thread_num();
int min = INT_MAX, max = INT_MIN;
#pragma omp for schedule(static)
for(int i=0; i<arr_size; i++) {
if (arr[i] < min) min=arr[i];
if (arr[i] > max) max=arr[i];
if ((arr[i] - min) > best) best = arr[i] - min;
}
mins [idx] = min;
maxes[idx] = max;
}
for (int i=0; i<nthreads-1; i++)
for (int j=i+1; j<nthreads; j++)
if ((maxes[j] - mins[i]) > best) best = maxes[j]-mins[i];
return best;
}
int main(int argc, char **argv) {
const int nitems=1000000;
int *data = malloc(nitems*sizeof(int));
srand(time(NULL));
for (int i=0; i<nitems; i++)
data[i] = rand() % 500; /* numbers between 0 and 500 */
data[(nitems/2)+1] = -700;
data[(nitems/2)] = 700; /* a trick! shouldn't get 1400, */
/* should get <= 1200 */
struct timeval start;
tick(&start);
unsigned int res = maxDiff(data, nitems);
double restime = tock(&start);
printf("Serial: answer = %u, time = %lf\n", res, restime);
tick(&start);
res = ompMaxDiff(data, nitems);
restime = tock(&start);
printf("OpenMP: answer = %u, time = %lf\n", res, restime);
free(data);
return 0;
}
void tick(struct timeval *t) {
gettimeofday(t, NULL);
}
double tock(const struct timeval * const t) {
struct timeval now;
gettimeofday(&now, NULL);
return (double)(now.tv_sec - t->tv_sec) + ((double)(now.tv_usec - t->tv_usec)/1000000.);
}
@Jeb11对四元素数组计算类似于
combine(组合(单体(a[0])、单体(a[1])、combine(单体(a[2])、单体(a[3]))
。由于combine
是关联的,因此组合顺序有很大的灵活性。我只是从左到右读取数组,所以您的解决方案不起作用。@Jeb11 Jonathan使用的不是还原。你试过了吗?当我们读取数组时,你必须注意方向。我从左到右阅读。好吧-这比最大差异要微妙一点,你是在寻找一个项目和后续项目之间的最大差异,对吗?@jonathan Yeah确实更新的版本似乎做了正确的事情,但问题的附加限制将进一步限制缩放。@$$$$$$$$$$$$@!我正要给你一个固定正确的解决方案!此外,我想说的是,OpenMP中的缩减被假定为关联的和交换的。但是,OPs函数不是可交换的,但它仍然是关联的(例如,类似于矩阵乘法)。在这种情况下,您必须存储每个线程的结果(正如您所做的那样),然后以串行方式进行操作。对于不可交换的操作,不可能并行化它们。哎哟-对不起,@Zboson:)。另一方面,这个版本可能有显著的改进。例如,结果表明(google“最大和子序列并行”,这是一个可以转化为的问题),通过在每个并行区域进行更多计算,可以将组合时间从O(P2)提高到O(P)。但是对于这里可能有用的小范围的P,我强烈怀疑额外的工作不值得。与其硬编码线程数量,我会询问并行团队中有多少线程,然后使用一条语句在并行部分内分配数组。请看Hristo Iliev在这里的回答@Zboson:是的-我真的应该解决这个问题,再加上手动分解for循环(不依赖于调度(静态)来分解循环,它可能会这样,但不能保证)。我不确定我是否理解你关于静态调度的论点。您是否担心它可能不会在增加线程数时分配块?我问了一个问题