2011-06-07 47 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上的相应高速缓存线无效。

如果每个线程有一个成员,每个线程都有成员,并且同时存在对该数组的读写操作,则可能需要将每个成员与一个缓存行对齐,以避免错误共享。