pthread 从共享内存中读取
Posted
技术标签:
【中文标题】pthread 从共享内存中读取【英文标题】:pthread reading from shared memory 【发布时间】:2011-06-07 15:19:09 【问题描述】:来自 CUDA,我对如何从线程读取共享内存感兴趣,并与 CUDA 的读取对齐要求进行比较。我将使用以下代码作为示例:
#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都指向共享内存空间(我相信)。从我在文档中遇到的情况来看,reading from multiple threads 是可以的。在 CUDA 中需要合并读取 - pthread 中是否有类似的对齐限制? IE。如果我有两个线程从同一个共享地址读取,我假设调度程序必须将一个线程放在另一个线程之前。在 CUDA 中,这可能是一项昂贵的操作,应该避免。从共享内存中“同时”读取是否会受到惩罚 - 如果是这样,它是否太小以至于可以忽略不计?即两个线程可能需要同时读取 d->datPtr[0] - 我假设内存读取不能同时发生 - 这个假设是错误的吗?
我还阅读了article from intel,它说在多线程时使用数组结构 - 这与 cuda 一致。如果我这样做,几乎不可避免的是我将需要线程 ID - 我相信这将需要我使用互斥锁锁定线程 ID,直到它被读入线程的范围,这是真的还是会有其他方式识别线程?
一篇关于多线程程序的内存管理的文章也将不胜感激。
【问题讨论】:
【参考方案1】:虽然您的线程数据指针 d
指向共享内存空间,除非您增加该指针以尝试读取或写入共享内存空间数组中的相邻线程数据元素,否则您基本上是在处理本地化线程数据。 args
的值也是每个线程的本地值,所以在这两种情况下,如果你不增加数据指针本身(即,你永远不会调用像 d++
之类的东西,这样你就指向另一个线程的内存),不需要互斥锁来保护“属于”线程的内存。
同样对于您的线程 ID,因为您只是从生成线程中写入该值,然后在实际生成的线程中读取该值,因此不需要互斥锁或同步机制……您只有数据的单个生产者/消费者。仅当有多个线程将读取和写入相同的数据位置时才需要互斥锁和其他同步机制。
【讨论】:
但是dataPtr指向的d中的数据是共享地址。在我的示例中,两个线程都从共享内存地址读取以计算总和,并且可能同时读取。我对这是如何发生的感兴趣 - 两个线程可以“同时”读取还是有一些调度程序可以实际划分两个读取?如果两个线程可以同时读取,这是如何工作的? 视情况而定。首先是读取的顺序是不确定的,这意味着除非您添加同步机制,否则您将无法确定地判断哪个线程将首先读取。话虽如此,线程本地指针d
not 指向共享内存。指针d->dataPtr
正在指向共享内存。在单处理器系统上,对d->dataPtr
的仲裁将通过软件调度程序完成。但在多处理器系统上,仲裁将在硬件内存控制器级别完成。
这样做的原因是因为在单处理器系统上,您基本上是在线程之间对处理器执行时间进行时间切片,并且软件调度程序处理在任何给定时间运行的线程.多处理器系统虽然实际上可以同时执行两个不同的线程,所以如果两个处理器都试图读取完全相同的内存位置,那么仲裁不能在软件级别完成,而是在硬件级别进行,要么通过预取数据到本地缓存,发出缓存一致性调用等。【参考方案2】:
CPU 有缓存。读取来自缓存,因此每个 CPU/核心都可以从自己的缓存中读取,只要相应的缓存行是共享的。写入强制缓存线进入 EXCLUSIVE 状态,使其他 CPU 上的相应缓存线无效。
如果您有一个数组,每个线程都有一个成员,并且该数组同时存在读取和写入,您可能希望将每个成员与缓存线对齐,以避免错误共享。
【讨论】:
【参考方案3】:内存读取到不同线程中的同一区域到同一内存在共享内存系统中不是问题(写入是另一回事,相关区域是缓存行:64-256 字节取决于系统)
我看不出获取 thread_id 应该是同步操作的任何原因。 (而且你可以为你的线程提供任何对你有意义的 id,这比从抽象 id 中获取有意义的值更简单)
【讨论】:
假设我正在使用数组结构,用 main 中第一个 for 循环中定义的变量“i”标识我的线程。为了识别一个线程,我会说'd.Idx = i'(然后是d.dataPtr =(double_ptr)等......)。在这种情况下,我必须在 d.Idx 上使用互斥锁,因为在调用下一个线程时值会改变。我目前的实现是一个结构数组(英特尔论文所说的速度较慢) - 尽管结构的所有大元素都指向相同的数组。可以索引 d[i].stuff(intel 说慢)而不是 d.stuff[i](intel 说快)。 在 threadFun 中,d 是一个本地(即基于堆栈的)变量,因此 d.Idx 的值对于每个线程都是唯一的,无需使用互斥锁或任何东西。【参考方案4】:来自 CUDA 可能会让您觉得复杂。 POSIX 线程要简单得多。基本上你正在做的事情应该可以工作,只要你只在共享数组中读取。
另外,不要忘记 CUDA 是 C++ 的肢解,而不是 C,所以在这方面有些事情可能看起来也不同。例如,在您的代码中,从malloc
转换返回值的习惯通常会被真正的 C 程序员所反对,因为它可能是细微错误的根源。
【讨论】:
以上是关于pthread 从共享内存中读取的主要内容,如果未能解决你的问题,请参考以下文章