Algorithm 最大单次销售利润-并行化版本

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

我正在尝试使用OpenMP API(或pthreads)来并行化以下代码。其时间复杂度为O(n)。 我想知道是否有可能将条目数组划分为
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循环(不依赖于调度(静态)来分解循环,它可能会这样,但不能保证)。我不确定我是否理解你关于静态调度的论点。您是否担心它可能不会在增加线程数时分配块?我问了一个问题