Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/64.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
C OpenMP程序(k-means+;+;)不可缩放_C_Parallel Processing_Openmp - Fatal编程技术网

C OpenMP程序(k-means+;+;)不可缩放

C OpenMP程序(k-means+;+;)不可缩放,c,parallel-processing,openmp,C,Parallel Processing,Openmp,目前,我正在使用OpenMP和C编程一个并行版本的k-means++。到目前为止,我正在实现质心的初始化。如果您不熟悉此过程,其工作原理大致相同。给定具有n点的dataset(矩阵),k质心使用“概率函数”(也称为轮盘选择)初始化 假设您有n=4个点和以下到某些质心的距离数组: distances = [2, 4, 6, 8] dist_sum = 20 通过将距离的每个条目除以距离和,并添加之前的结果,定义一个累积概率数组,如下所示: probs = [0.1, 0.2, 0.3,

目前,我正在使用OpenMP和C编程一个并行版本的k-means++。到目前为止,我正在实现质心的初始化。如果您不熟悉此过程,其工作原理大致相同。给定具有
n
点的
dataset
(矩阵),
k
质心使用“概率函数”(也称为轮盘选择)初始化

假设您有
n=4
个点和以下到某些质心的距离数组:

distances = [2, 4, 6, 8]
dist_sum  = 20
通过将
距离
的每个条目除以
距离和
,并添加之前的结果,定义一个累积概率数组,如下所示:

probs     = [0.1, 0.2, 0.3, 0.4] = [2/20, 4/20, 6/20, 8/20]
acc_probs = [0.1, 0.3, 0.6, 1.0]
然后,执行轮盘赌选择。给定一个随机数,比如说
r=0.5
,使用
r
acc_probs
选择下一个点,在
acc_probs
上迭代,直到
r
。在本例中,所选点为
i=2
,因为
r

问题 在本例中,我使用的是非常大的矩阵(大约
n=1600000
个点)。尽管该程序给出了正确的答案(即质心的良好初始化),但其伸缩性不如预期。此函数根据此算法计算初始质心

    double **parallel_init_centroids (double **dataset, int n, int d, int k, RngStream randomizer, long int *total_ops) {

    double dist=0, error=0, dist_sum=0, r=0, partial_sum=0, mindist=0;
    int cn=0, cd=0, ck = 0, cck = 0, idx = 0;
    ck = 0;

    double probs_sum = 0; // debug

    int  mink=0, id=0, cp=0;

    for (ck = 0; ck < k; ck++) {

        if ( ck == 0 ) {

            // 1. choose an initial centroid c_0 from dataset randomly
            idx = RngStream_RandInt (randomizer, 0, n-1);

        }  
        else {

            // 2. choose a successive centroid c_{ck} using roulette selection
            r = RngStream_RandU01 (randomizer);
            idx = 0;
            partial_sum = 0;
            for (cn=0; cn<n; cn++) {

                partial_sum = partial_sum + distances[cn]/dist_sum;

                if (r < partial_sum) {
                    idx = cn;
                    break;
                }
            }
        }

        // 3. copy centroid from dataset
        for (cd=0; cd<d; cd++)
            centroids[ck][cd] = dataset[idx][cd];

        // reset before parallel region
        dist_sum = 0;

        // -- parallel region --
        # pragma omp parallel shared(distances, clusters, centroids, dataset, chunk, dist_sum_threads, total_ops_threads) private(id, cn, cck, cd, cp, error, dist, mindist, mink)
        {
            id = omp_get_thread_num();
            dist_sum_threads[id] = 0;               // each thread reset its entry

            // parallel loop
            // 4. recompute distances against centroids
            # pragma omp for schedule(static,chunk)
            for (cn=0; cn<n; cn++) {

                mindist = DMAX;
                mink = 0;

                for (cck=0; cck<=ck; cck++) {

                    dist = 0;

                    for (cd=0; cd<d; cd++) {

                        error = dataset[cn][cd] - centroids[ck][cd];
                        dist = dist + (error * error);                  total_ops_threads[id]++;
                    }

                    if (dist < mindist) {
                        mindist = dist;
                        mink = ck;
                    }
                }

                distances[cn]           = mindist;
                clusters[cn]            = mink;
                dist_sum_threads[id]    += mindist;      // each thread contributes before reduction
            }
        }
        // -- parallel region --

        // 5. sequential reduction
        dist_sum = 0;
        for (cp=0; cp<p; cp++)
            dist_sum += dist_sum_threads[cp];
    }


    // stats
    *(total_ops) = 0;
    for (cp=0; cp<p; cp++)
        *(total_ops) += total_ops_threads[cp];

    // free it later
    return centroids;
}
dist\u sum
通过添加
dist\u sum\u线程的每个条目来定义。此函数按预期工作,但当线程数增加时,执行时间会增加。这显示了一些性能指标

我的实现有什么问题,特别是openmp?总之,只使用了两个pragma:

# pragma omp parallel ...
{
    get thread id

    # pragma omp for schedule(static,chunk)
    {
        compute distances ...
    }

    fill distances and dist_sum_threads[id]
}
换句话说,我消除了障碍、互斥访问和其他可能导致额外开销的因素。但是,随着线程数的增加,执行时间最差

更新

  • 先前的代码已更改为。与我以前的代码类似。在这种情况下,计算
    n=100000
    点和
    k=16
    质心之间的距离
  • 在并行区域之前和之后,使用
    omp\u get\u wtime
    测量执行时间。总时间存储在
    wtime\u explored
  • 我包括一个计算距离和的缩减。然而,它并没有像预期的那样工作(下面被评论为糟糕的并行缩减)。
    dist_sum
    的正确值是
    999857108020.0
    ,但是,当使用
    p
    线程计算它时,结果是
    999857108020.0*p
    ,这是错误的
  • 表演情节是
  • 这是主要的并行功能,完整代码位于:

    double**并行计算距离(double**数据集,整数n,整数d,整数k,长整数*总运算){
    双距离=0,错误=0,心智主义=0;
    int cn、cd、ck、水貂、id、cp;
    //平行区域前复位
    距离和=0;
    //--开始时间--
    wtime_start=omp_get_wtime();
    //并联回路
    #pragma omp parallel shared(距离、簇、质心、数据集、块、距离和、距离和线程)private(id、cn、ck、cd、cp、error、dist、mindsist、mink)
    {
    id=omp_get_thread_num();
    dist_sum_线程[id]=0;//重置
    //2.根据质心重新计算距离
    #计划的pragma omp(静态、块)
    对于(cn=0;cn您的代码不是一个假设,我只能在此提出假设。然而,以下是我认为(可能)发生的事情(没有具体的重要性顺序):

    • 当您更新
      dist\u sum\u线程
      total\u ops\u线程
      时,您会遇到错误共享。只需声明
      reduce(+:dist\u sum)即可完全避免前者
      并在
      并行
      区域内直接使用
      dist\u-sum
      。您也可以对
      total\u-ops\u-threads
      使用本地
      total\u-ops
      声明的
      reducement(+)
      执行类似操作,并在最后将其累加到
      *total\u-ops
      (顺便说一句,
      dist-sum
      已计算但从未使用过…)

    • 无论如何,代码看起来都是内存受限的,因为您有大量的内存访问,几乎不需要计算。因此,预期的速度提高主要取决于您的内存带宽和并行代码时可以访问的内存控制器的数量。有关更多详细信息,请参阅

    • 鉴于您的问题可能存在上述内存绑定特征,请尝试使用内存放置(
      numactl
      可能和/或线程关联
      proc\u bind
      )。您还可以尝试使用线程调度策略和/或尝试查看是否无法对将数据阻塞到缓存中的问题应用某些循环平铺

    • 您没有详细说明您测量时间的方式,但请注意,只有在挂钟时间的情况下,而不是CPU时间的情况下,速度才有意义。请使用
      omp\u get\u wtime()
      进行任何此类测量

    试着解决这些问题,并根据你的内存结构评估你的实际潜在速度。如果你仍然觉得你没有达到你应该达到的,那么就更新你的问题


    编辑

    因为您提供了一个完整的示例,所以我设法对您的代码进行了一些实验,并实现了我想到的修改(主要是为了减少错误共享)

    下面是函数no的外观:

    double **parallel_compute_distances( double **dataset, int n, int d,
                                         int k, long int *total_ops ) {
        // reset before parallel region
        dist_sum = 0;            
    
        // -- start time --
        wtime_start = omp_get_wtime ();
    
        long int tot_ops = 0;
    
        // parallel loop
        # pragma omp parallel for reduction( +: dist_sum, tot_ops )
        for ( int cn = 0; cn < n; cn++ ) {
            double mindist = DMAX;
            int mink = 0;
            for ( int ck = 0; ck < k; ck++ ) {
                double dist = 0;
                for ( int cd = 0; cd < d; cd++ ) {
                    double error = dataset[cn][cd] - centroids[ck][cd];
                    dist += error * error;
                    tot_ops++;
                }
                if ( dist < mindist ) {
                    mindist = dist;
                    mink = ck;
                }
            }
            distances[cn] = mindist;
            clusters[cn] = mink;
            dist_sum += mindist;
        }
    
        // -- end time --
        wtime_end = omp_get_wtime ();
    
        // -- total wall time --
        wtime_spent = wtime_end - wtime_start;
    
        // stats
        *(total_ops) = tot_ops;
    
        return centroids;
    }
    
    double**并行计算距离(double**数据集,int n,int d,
    整数k,长整数*总运算){
    //平行区域前复位
    距离和=0;
    //--开始时间--
    wtime_start=omp_get_wtime();
    长整数tot_ops=0;
    //并联回路
    #pragma omp并行用于缩减(+:距离和,tot_
    
    double **parallel_compute_distances (double **dataset, int n, int d, int k, long int *total_ops) {
    
        double dist=0, error=0, mindist=0;
    
        int cn, cd, ck, mink, id, cp;
    
        // reset before parallel region
        dist_sum = 0;            
    
        // -- start time --
        wtime_start = omp_get_wtime ();
    
        // parallel loop
        # pragma omp parallel shared(distances, clusters, centroids, dataset, chunk, dist_sum, dist_sum_threads) private(id, cn, ck, cd, cp, error, dist, mindist, mink)
        {
            id = omp_get_thread_num();
            dist_sum_threads[id] = 0;               // reset
    
            // 2. recompute distances against centroids
            # pragma omp for schedule(static,chunk)
            for (cn=0; cn<n; cn++) {
    
                mindist = DMAX;
                mink = 0;
    
                for (ck=0; ck<k; ck++) {
    
                    dist = 0;
    
                    for (cd=0; cd<d; cd++) {
    
                        error = dataset[cn][cd] - centroids[ck][cd];
                        dist = dist + (error * error);                               total_ops_threads[id]++;
                    }
    
                    if (dist < mindist) {
                        mindist = dist;
                        mink = ck;
                    }
                }
    
                distances[cn]           = mindist;
                clusters[cn]            = mink;
                dist_sum_threads[id]    += mindist;
            }
    
    
            // bad parallel reduction
            //#pragma omp parallel for reduction(+:dist_sum)
            //for (cp=0; cp<p; cp++){             
            //    dist_sum += dist_sum_threads[cp];
            //}
    
        }
    
    
        // -- end time --
        wtime_end = omp_get_wtime ();
    
        // -- total wall time --
        wtime_spent = wtime_end - wtime_start;
    
        // sequential reduction
        for (cp=0; cp<p; cp++)       
            dist_sum += dist_sum_threads[cp];
    
    
        // stats
        *(total_ops) = 0;
        for (cp=0; cp<p; cp++)
            *(total_ops) += total_ops_threads[cp];
    
        return centroids;
    }
    
    double **parallel_compute_distances( double **dataset, int n, int d,
                                         int k, long int *total_ops ) {
        // reset before parallel region
        dist_sum = 0;            
    
        // -- start time --
        wtime_start = omp_get_wtime ();
    
        long int tot_ops = 0;
    
        // parallel loop
        # pragma omp parallel for reduction( +: dist_sum, tot_ops )
        for ( int cn = 0; cn < n; cn++ ) {
            double mindist = DMAX;
            int mink = 0;
            for ( int ck = 0; ck < k; ck++ ) {
                double dist = 0;
                for ( int cd = 0; cd < d; cd++ ) {
                    double error = dataset[cn][cd] - centroids[ck][cd];
                    dist += error * error;
                    tot_ops++;
                }
                if ( dist < mindist ) {
                    mindist = dist;
                    mink = ck;
                }
            }
            distances[cn] = mindist;
            clusters[cn] = mink;
            dist_sum += mindist;
        }
    
        // -- end time --
        wtime_end = omp_get_wtime ();
    
        // -- total wall time --
        wtime_spent = wtime_end - wtime_start;
    
        // stats
        *(total_ops) = tot_ops;
    
        return centroids;
    }