簡體   English   中英

在C中使用pthread的遞歸函數

[英]Recursive function using pthreads in C

我有以下代碼

    #include "stdio.h"
#include "stdlib.h"
#include <string.h>

#define MAXBINS 8


void swap_long(unsigned long int **x, unsigned long int **y){

  unsigned long int *tmp;
  tmp = x[0];
  x[0] = y[0];
  y[0] = tmp;

}

void swap(unsigned int **x, unsigned int **y){

  unsigned int *tmp;
  tmp = x[0];
  x[0] = y[0];
  y[0] = tmp;

}

void truncated_radix_sort(unsigned long int *morton_codes, 
              unsigned long int *sorted_morton_codes, 
              unsigned int *permutation_vector,
              unsigned int *index,
              int *level_record,
              int N, 
              int population_threshold,
              int sft, int lv){

  int BinSizes[MAXBINS] = {0};
  unsigned int *tmp_ptr;
  unsigned long int *tmp_code;

  level_record[0] = lv; // record the level of the node

  if(N<=population_threshold || sft < 0) { // Base case. The node is a leaf
    memcpy(permutation_vector, index, N*sizeof(unsigned int)); // Copy the pernutation vector
    memcpy(sorted_morton_codes, morton_codes, N*sizeof(unsigned long int)); // Copy the Morton codes 

    return;
  }
  else{

    // Find which child each point belongs to 
    int j = 0;
    for(j=0; j<N; j++){
      unsigned int ii = (morton_codes[j]>>sft) & 0x07;
      BinSizes[ii]++;
    }


    // scan prefix 
    int offset = 0, i = 0;
    for(i=0; i<MAXBINS; i++){
      int ss = BinSizes[i];
      BinSizes[i] = offset;
      offset += ss;
    }

    for(j=0; j<N; j++){
      unsigned int ii = (morton_codes[j]>>sft) & 0x07;
      permutation_vector[BinSizes[ii]] = index[j];
      sorted_morton_codes[BinSizes[ii]] = morton_codes[j];
      BinSizes[ii]++;
    }

    //swap the index pointers  
    swap(&index, &permutation_vector);

    //swap the code pointers 
    swap_long(&morton_codes, &sorted_morton_codes);

    /* Call the function recursively to split the lower levels */
    offset = 0; 
    for(i=0; i<MAXBINS; i++){

      int size = BinSizes[i] - offset;

      truncated_radix_sort(&morton_codes[offset], 
               &sorted_morton_codes[offset], 
               &permutation_vector[offset], 
               &index[offset], &level_record[offset], 
               size, 
               population_threshold,
               sft-3, lv+1);
      offset += size;  
    }


  } 
}

我試圖做這個塊

int j = 0;
    for(j=0; j<N; j++){
      unsigned int ii = (morton_codes[j]>>sft) & 0x07;
      BinSizes[ii]++;
    }

通過用以下內容代替

    int rc,j;
    pthread_t *thread = (pthread_t *)malloc(NTHREADS*sizeof(pthread_t));
    belong *belongs = (belong *)malloc(NTHREADS*sizeof(belong));
    pthread_mutex_init(&bin_mtx, NULL);
    for (j = 0; j < NTHREADS; j++){
        belongs[j].n = NTHREADS;
        belongs[j].N = N;
        belongs[j].tid = j;
        belongs[j].sft = sft;
        belongs[j].BinSizes = BinSizes;
        belongs[j].mcodes = morton_codes;
        rc = pthread_create(&thread[j], NULL, belong_wrapper, (void *)&belongs[j]);
    }

    for (j = 0; j < NTHREADS; j++){
        rc = pthread_join(thread[j], NULL);
    }

並在遞歸函數之外定義這些

typedef struct{
    int n, N, tid, sft;
    int *BinSizes;
    unsigned long int *mcodes;
}belong;

pthread_mutex_t bin_mtx;

void * belong_wrapper(void *arg){
    int n, N, tid, sft, j;
    int *BinSizes;
    unsigned int ii;
    unsigned long int *mcodes;
    n = ((belong *)arg)->n;
    N = ((belong *)arg)->N;
    tid = ((belong *)arg)->tid;
    sft = ((belong *)arg)->sft;
    BinSizes = ((belong *)arg)->BinSizes;
    mcodes = ((belong *)arg)->mcodes;
    for (j = tid; j<N; j+=n){
        ii = (mcodes[j] >> sft) & 0x07;
        pthread_mutex_lock(&bin_mtx);
        BinSizes[ii]++;
        pthread_mutex_unlock(&bin_mtx);
    }

}

但是,執行所需的時間比串行的要多得多。為什么會這樣? 我應該改變什么?

由於您使用單個互斥量來保護對BinSizes數組的更新,因此您最終仍將繼續對該數組進行所有更新:在任何給定時間,只有一個線程可以調用BinSizes[ii]++ 基本上,您仍然按順序執行函數,但是會產生創建和銷毀線程的額外開銷。

我可以為您想到幾種選擇(可能還有更多選擇):

  1. 按照@Chris的建議進行操作,並使每個線程更新BinSizes一部分。 根據您用於計算ii的計算的屬性,這可能不可行。
  2. 創建代表BinSizes不同分區的多個互斥鎖。 例如,如果BinSizes有10個元素,則可以為元素0-4創建一個互斥體,為元素5-9創建另一個互斥體,然后在線程中使用它們,如下所示:

     if (ii < 5) { mtx_index = 0; } else { mtx_index = 1; } pthread_mutex_lock(&bin_mtx[mtx_index]); BinSizes[ii]++; pthread_mutex_unlock(&bin_mtx[mtx_index]); 

    您可以將這個想法推廣到任何大小的BinSizes和任何范圍:可能每個數組元素都有一個不同的互斥量。 當然,那么您將需要承擔創建每個互斥鎖的開銷,並且如果有人試圖一次鎖定其中幾個互鎖,則可能導致死鎖等。

  3. 最后,您可以完全放棄並行處理此塊的想法:正如其他用戶所提到的那樣,使用線程會受到一定程度的收益遞減的影響。 除非您的BinSizes數組很大,否則即使您“正確執行”,也可能看不到並行化的巨大好處。

tl; dr-對於大多數問題,添加線程並不是一個簡單的解決方案。 您的代碼不可並行化,並且此代碼幾乎沒有任何實際的並發性。


您可以對BinSizes上的每個(便宜的)整數操作旋轉互斥BinSizes 這將破壞任何並行性,因為所有線程都已在此序列化。

少數指令可以同時運行(for循環和莫頓碼陣列上的幾個操作)比(UN)鎖定一個互斥體便宜得多 :即使使用原子增量(如果有的話)會比非更貴同步部分。

一種解決方法是為每個線程提供自己的輸出數組,並在完成所有任務后將它們組合在一起。

此外,您將為每個調用創建並加入多個線程。 與計算相比,創建線程的成本相對較高,因此通常建議創建一個長期存在的線程池以分散該成本。

即使執行此操作,也需要根據您擁有多少個(空閑)內核來調整線程數。 如果在遞歸函數中執行此操作,則同時存在幾個線程? 創建的線程多於您要安排內核的線程,這是沒有意義的。

哦,您正在泄漏內存。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM