[英]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]++
。 基本上,您仍然按順序執行函數,但是會產生創建和銷毀線程的額外開銷。
我可以為您想到幾種選擇(可能還有更多選擇):
BinSizes
一部分。 根據您用於計算ii
的計算的屬性,這可能不可行。 創建代表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和任何范圍:可能每個數組元素都有一個不同的互斥量。 當然,那么您將需要承擔創建每個互斥鎖的開銷,並且如果有人試圖一次鎖定其中幾個互鎖,則可能導致死鎖等。
最后,您可以完全放棄並行處理此塊的想法:正如其他用戶所提到的那樣,使用線程會受到一定程度的收益遞減的影響。 除非您的BinSizes
數組很大,否則即使您“正確執行”,也可能看不到並行化的巨大好處。
tl; dr-對於大多數問題,添加線程並不是一個簡單的解決方案。 您的代碼不可並行化,並且此代碼幾乎沒有任何實際的並發性。
您可以對BinSizes
上的每個(便宜的)整數操作旋轉互斥BinSizes
。 這將破壞任何並行性,因為所有線程都已在此序列化。
少數指令可以同時運行(for循環和莫頓碼陣列上的幾個操作)比(UN)鎖定一個互斥體便宜得多 :即使使用原子增量(如果有的話)會比非更貴同步部分。
一種解決方法是為每個線程提供自己的輸出數組,並在完成所有任務后將它們組合在一起。
此外,您將為每個調用創建並加入多個線程。 與計算相比,創建線程的成本相對較高,因此通常建議創建一個長期存在的線程池以分散該成本。
即使執行此操作,也需要根據您擁有多少個(空閑)內核來調整線程數。 如果在遞歸函數中執行此操作,則同時存在幾個線程? 創建的線程多於您要安排內核的線程,這是沒有意義的。
哦,您正在泄漏內存。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.