2011-06-07 77 views
0

來自CUDA我感興趣的是如何從線程讀取共享內存,並與CUDA的讀取對齊要求進行比較。我將使用下面的代碼作爲示例:pthread從共享內存讀取

#include <sys/unistd.h> 
#include <pthread.h> 
#include <stdlib.h> 
#include <stdio.h> 
#define THREADS 2 

void * threadFun(void * args); 

typedef struct { 
    float * dataPtr; 
    int tIdx, 
    dSize; 
} t_data; 

int main(int argc, char * argv[]) 
{ 
    int i, 
    sizeData=5; 
    void * status; 

    float *data; 

    t_data * d; 

    pthread_t * threads; 
    pthread_attr_t attr; 

    data=(float *) malloc(sizeof(float) * sizeData); 
    threads=(pthread_t *)malloc(sizeof(pthread_t)*THREADS); 
    d = (t_data *) malloc (sizeof(t_data)*THREADS); 

    data[0]=0.0; 
    data[1]=0.1; 
    data[2]=0.2; 
    data[3]=0.3; 
    data[4]=0.4; 

    pthread_attr_init(&attr); 
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 

    for (i=0; i<THREADS;i++) 
    { 
     d[i].tIdx=i; 
     d[i].dataPtr=data; 
     d[i].dSize=sizeData; 
     pthread_create(&threads[i],NULL,threadFun,(void *)(d+i)); 
    } 

    for (i=0; i<THREADS; i++) 
    { 
     pthread_join(threads[i],&status); 
     if(status); 
      //Error; 
    } 
    return 0; 
} 

void * threadFun(void * args) 
{ 
    int i; 

    t_data * d= (t_data *) args; 

    float sumVal=0.0; 

    for (i=0; i<d->dSize; i++) 
     sumVal+=d->dataPtr[i]*(d->tIdx+1); 

    printf("Thread %d calculated the value as %-11.11f\n",d->tIdx,sumVal); 

    return(NULL); 
} 

在threadFun中,整個指針d指向共享內存空間(我相信)。從我在文檔中遇到的讀取多個線程都可以。在CUDA中,讀取需要進行合併 - pthread中是否存在類似的對齊限制?即如果我有兩個線程從相同的共享地址讀取,我假設某個調度程序必須將一個線程放在另一個線程之前。在CUDA中,這可能是一個代價高昂的操作,應該避免。是否存在對共享內存「同時」讀取的懲罰 - 如果是這樣的話,它是如此之小以至於可以忽略不計?即兩個線程可能需要同時讀取d-> datPtr [0] - 我假設內存讀取不能同時發生 - 這種假設是否錯誤?

另外我讀了article from intel,它說多線程時使用數組結構 - 這與cuda一致。如果我這樣做,這幾乎是不可避免的,我將需要線程ID - 我相信這將需要我使用互斥鎖鎖定線程ID直到它被讀入線程的範圍,這是真的還是會有其他方式識別線程?

關於多線程程序的內存管理的文章也將受到讚賞。

回答

2

雖然你的線程數據指針d指向到一個共享的內存空間,除非您增加該指針以嘗試讀取或寫入共享內存空間數組中的相鄰線程數據元素,基本上就是處理本地化的線程數據。此外,args的值對於每個線程都是本地的,所以在這兩種情況下,如果您沒有增加數據指針本身(即,您從未調用類似d++等的內容,以便指向另一個線程的內存),則不需要互斥來保護「屬於」你的線程的內存。

再次爲你的線程ID,因爲你只是從產卵線程寫這個值,然後在實際產生的線程中讀取該值,不需要互斥或同步機制......你只有一個單一的生產者/消費者的數據。互斥鎖和其他同步機制僅在有多個線程將讀取和寫入相同的數據位置時才需要。

+0

,但dataPtr在d中指向的數據是共享地址。在我的示例中,兩個線程都從共享內存地址中讀取數據以計算總和,並可能同時讀取。我對這種情況的發生感興趣 - 兩個線程可以同時讀取,還是有一些調度程序實際上將兩個讀取分開?如果兩個線程*可以同時讀取,這是如何工作的? – Marm0t 2011-06-07 19:09:42

+0

這取決於。首先,讀取序列將是非確定性的,這意味着除非添加同步機制,否則您將無法確定性地確定哪個線程將首先讀取。這就是說,線程本地指針「d」不*指向共享內存。指針'd-> dataPtr' * *指向共享內存。在單處理器系統上,通過軟件調度程序對'd-> dataPtr'進行仲裁。但是,在多處理器系統上,仲裁將在硬件內存控制器級完成。 – Jason 2011-06-07 19:43:22

+1

之所以這樣,是因爲在單處理器系統上,基本上是在線程之間對處理器執行時間進行時間分割,軟件調度程序處理在任何給定時間運行的線程。多處理器系統雖然可以同時執行兩個不同的線程,但是如果兩個處理器都試圖讀取完全相同的內存位置,那麼仲裁不能在軟件級完成,而是發生在硬件級別通過預取數據到本地緩存,發出緩存一致性調用等。 – Jason 2011-06-07 19:48:05

0
  1. 存儲器讀取到在不同的線程中對同一存儲器的相同區域不共享存儲器系統(寫則是另一回事了問題,相關面積爲高速緩衝存儲器線:視64-256字節系統)

  2. 我看不出有什麼理由讓thread_id應該是一個同步操作。 (你可以使用任何ID有意義的你給你的線程,它可以比從一個抽象的ID得到一個有意義的值更簡單)

+0

說我正在使用數組的結構,標識我的線程與變量'我'定義在第一for循環在主。爲了確定一個線程,我會說'd.Idx = i'(然後d.dataPtr =(double_ptr)等等......)。在這種情況下,我必須在d.Idx上使用互斥鎖,因爲在調用下一個線程時該值將發生變化。我目前的實現是一系列結構(這是英特爾論文所說的較慢的結構) - 雖然結構中的所有大元素指向相同的數組。它可以索引d [i] .stuff(intel說慢)而不是d.stuff [i](intel說得更快)。 – Marm0t 2011-06-07 16:16:13

+0

在threadFun中,d是一個本地(即基於堆棧的)變量,所以d.Idx的值對於每個線程都是唯一的,而不使用互斥鎖或任何東西。 – 2011-06-07 16:40:15

0

來自CUDA可能讓你覺得複雜。 POSIX線程要簡單得多。基本上你在做什麼應該工作,只要你只在共享數組中讀取。另外,不要忘記CUDA是C++的肢解而不是C,所以有些東西可能看起來與這方面有所不同。例如,在你的代碼中,從malloc轉換回來的習慣通常被真正的C程序員所詬病,因爲它可能是微妙的錯誤的來源。

1

CPU有高速緩存。讀取來自緩存,因此只要相應的緩存行是SHARED,每個CPU /內核都可以從它自己的緩存中讀取。將強制高速緩存線寫入EXCLUSIVE狀態,使其他CPU上的相應高速緩存線無效。

如果每個線程有一個成員,每個線程都有成員,並且同時存在對該數組的讀寫操作,則可能需要將每個成員與一個緩存行對齊,以避免錯誤共享。