- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我有以下任务来演示虚假共享并编写了一个简单的程序:
#include <sys/times.h>
#include <time.h>
#include <stdio.h>
#include <pthread.h>
long long int tmsBegin1,tmsEnd1,tmsBegin2,tmsEnd2,tmsBegin3,tmsEnd3;
int array[100];
void *heavy_loop(void *param) {
int index = *((int*)param);
int i;
for (i = 0; i < 100000000; i++)
array[index]+=3;
}
int main(int argc, char *argv[]) {
int first_elem = 0;
int bad_elem = 1;
int good_elem = 32;
long long time1;
long long time2;
long long time3;
pthread_t thread_1;
pthread_t thread_2;
tmsBegin3 = clock();
heavy_loop((void*)&first_elem);
heavy_loop((void*)&bad_elem);
tmsEnd3 = clock();
tmsBegin1 = clock();
pthread_create(&thread_1, NULL, heavy_loop, (void*)&first_elem);
pthread_create(&thread_2, NULL, heavy_loop, (void*)&bad_elem);
pthread_join(thread_1, NULL);
pthread_join(thread_2, NULL);
tmsEnd1 = clock();
tmsBegin2 = clock();
pthread_create(&thread_1, NULL, heavy_loop, (void*)&first_elem);
pthread_create(&thread_2, NULL, heavy_loop, (void*)&good_elem);
pthread_join(thread_1, NULL);
pthread_join(thread_2, NULL);
tmsEnd2 = clock();
printf("%d %d %d\n", array[first_elem],array[bad_elem],array[good_elem]);
time1 = (tmsEnd1-tmsBegin1)*1000/CLOCKS_PER_SEC;
time2 = (tmsEnd2-tmsBegin2)*1000/CLOCKS_PER_SEC;
time3 = (tmsEnd3-tmsBegin3)*1000/CLOCKS_PER_SEC;
printf("%lld ms\n", time1);
printf("%lld ms\n", time2);
printf("%lld ms\n", time3);
return 0;
}
当我看到结果时,我感到非常惊讶(我在我的 i5-430M 处理器上运行它)。
请告诉我我的错误或解释为什么会这样。
最佳答案
错误共享是具有独立缓存的多个内核访问同一物理内存区域的结果(尽管不是同一个地址——那将是真正的共享)。
要了解虚假共享,您需要了解缓存。在大多数处理器中,每个内核都有自己的 L1 缓存,用于保存最近访问的数据。缓存按“行”组织,这些行是对齐的数据 block ,通常长度为 32 或 64 字节(取决于您的处理器)。当您从不在缓存中的地址读取时,整行从主内存(或 L2 缓存)读取到 L1。当您写入缓存中的地址时,包含该地址的行被标记为“脏”。
这就是共享方面的用武之地。如果多个核心正在从同一行读取,则它们每个都可以在 L1 中拥有该行的副本。但是,如果一个副本被标记为脏,它会使其他缓存中的行无效。如果这没有发生,那么在一个核心上进行的写入可能要等到很久以后才会对其他核心可见。所以下一次另一个核心去读取该行时,缓存未命中,它必须再次获取该行。
False 共享发生在核心读取和写入同一行上的不同地址时。尽管它们不共享数据,但缓存的行为就像它们一样,因为它们非常接近。
此效果在很大程度上取决于处理器的架构。如果你有一个单核处理器,你根本看不到效果,因为没有共享。如果您的缓存行更长,您会在“坏”和“好”情况下看到效果,因为它们仍然靠得很近。如果您的核心不共享 L2 缓存(我猜他们这样做),您可能会看到 300-400% 的差异,因为它们必须在缓存未命中时一直转到主内存。
您可能还想知道每个线程都在进行读写操作(+= 而不是 =)这一点很重要。某些处理器具有直写 高速缓存,这意味着如果内核写入不在高速缓存中的地址,它不会错过并从内存中获取该行。将此与回写 缓存进行对比,后者确实会在写入时丢失。
关于c - 虚假共享和 pthreads,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8331255/
我试图弄清楚接受 OpenID 登录的网站如何无法通过简单的主机文件更新来指向伪造的 OpenID 提供商。 假设我想侵入 Joe Smith 的帐户,在这个例子中,假设他的 OpenID 提供商是
#include #include #include #include #include #include #include #include #include #include #define P
根据此讨论 - "RESTful API - Correct behavior when spurious/not requested parameters are passed in the req
如果编译为 Cand C++ 源代码,这个简单的代码片段会使用 g++ 4.7.0 生成“函数调用中缺少标记”警告。我相信这是编译器的错误,因为最终的 NULL值(value)就在那里。 #inclu
我读到,有时 && 运算符用于“短路”JavaScript,使其相信返回值 0 是 0 而不是 NaN,因为 0 在 JavaScript 中是一个虚假数字。我一直在四处寻找,想弄清楚这一切意味着什么
我正在使用 Borland(又名“Embarcodegearland”)C++Builder 2007 编译器,它有一个小错误,系统头文件中的某些 static const 项可能导致虚假的 "xyz
我是一名优秀的程序员,十分优秀!