C pthreads和drand48并发性能
根据规范,C中的函数rand()使用互斥锁来锁定上下文()。所以,如果我使用多个线程调用它,我的程序会很慢,因为所有线程都会尝试访问这个锁定区域 因此,我找到了drand48(),另一个随机数生成器函数,它没有锁()。但是,不知何故,我的并行程序仍然比串行程序慢!代码粘贴在下面: 序列版本:C pthreads和drand48并发性能,c,pthreads,C,Pthreads,根据规范,C中的函数rand()使用互斥锁来锁定上下文()。所以,如果我使用多个线程调用它,我的程序会很慢,因为所有线程都会尝试访问这个锁定区域 因此,我找到了drand48(),另一个随机数生成器函数,它没有锁()。但是,不知何故,我的并行程序仍然比串行程序慢!代码粘贴在下面: 序列版本: #include <cstdlib> #define M 100000000 int main() { for (int i = 0; i < M; ++i)
#include <cstdlib>
#define M 100000000
int main()
{
for (int i = 0; i < M; ++i)
drand48();
return 0;
}
#include <pthread.h>
#include <cstdlib>
#define M 100000000
#define N 4
pthread_t threads[N];
void* f(void* p)
{
for (int i = 0; i < M/N; ++i)
drand48();
}
int main()
{
for (int i = 0; i < N; ++i)
pthread_create(&threads[i], NULL, f, NULL);
for (int i = 0; i < N; ++i)
pthread_join(threads[i], NULL);
return 0;
}
#包括
#定义M 100000000
int main()
{
对于(int i=0;i
并行版本:
#include <cstdlib>
#define M 100000000
int main()
{
for (int i = 0; i < M; ++i)
drand48();
return 0;
}
#include <pthread.h>
#include <cstdlib>
#define M 100000000
#define N 4
pthread_t threads[N];
void* f(void* p)
{
for (int i = 0; i < M/N; ++i)
drand48();
}
int main()
{
for (int i = 0; i < N; ++i)
pthread_create(&threads[i], NULL, f, NULL);
for (int i = 0; i < N; ++i)
pthread_join(threads[i], NULL);
return 0;
}
#包括
#包括
#定义M 100000000
#定义n4
pthread_t线程[N];
void*f(void*p)
{
对于(int i=0;i
我执行了两种代码。串行运行约0.6秒,并行运行约2.1秒
谁能解释一下为什么会发生这种情况
一些附加信息:我的电脑上有4个内核。我使用 g++serial.cpp-o serial 并行使用 g++parallel.cpp-lpthread-o并行
编辑: 显然,每当我更新线程中的全局变量时,就会发生性能损失。在下面的示例中,x变量是全局变量(注意,在并行示例中,操作将是非线程安全的): 序列号:
#include <cstdlib>
#define M 1000000000
int x = 0;
int main()
{
for (int i = 0; i < M; ++i)
x = x + 10 - 10;
return 0;
}
#包括
#定义百万
int x=0;
int main()
{
对于(int i=0;i
平行:
#include <pthread.h>
#include <cstdlib>
#define M 1000000000
#define N 4
pthread_t threads[N];
int x;
void* f(void* p)
{
for (int i = 0; i < M/N; ++i)
x = x + 10 - 10;
}
int main()
{
for (int i = 0; i < N; ++i)
pthread_create(&threads[i], NULL, f, NULL);
for (int i = 0; i < N; ++i)
pthread_join(threads[i], NULL);
return 0;
}
#包括
#包括
#定义百万
#定义n4
pthread_t线程[N];
int x;
void*f(void*p)
{
对于(int i=0;i
请注意,drand48()使用全局结构变量\u libc\u drand48\u datadrand48()
使用全局结构变量\u libc\u drand48\u data
,它将状态保持在那里(写入),因此是缓存线争用的根源,而缓存线争用很可能是性能下降的根源。这不是我最初怀疑并在评论中写道的虚假共享
,而是真正的共享。drand48()的实现中没有锁定的原因有两个:
xsubi[0] = result & 0xffff;
xsubi[1] = (result >> 16) & 0xffff;
xsubi[2] = (result >> 32) & 0xffff;
源代码
您提供了我在下面提供的链接以供参考。问题是状态更新时缓存线争用
#include <stdlib.h>
/* Global state for non-reentrant functions. Defined in drand48-iter.c. */
extern struct drand48_data __libc_drand48_data;
double drand48(void)
{
double result;
erand48_r (__libc_drand48_data.__x, &__libc_drand48_data, &result);
return result;
}
以及\uu drand48\u iterate
的实现,在这里它会写回全局
int
__drand48_iterate (unsigned short int xsubi[3], struct drand48_data *buffer)
{
uint64_t X;
uint64_t result;
/* Initialize buffer, if not yet done. */
if (unlikely(!buffer->__init))
{
buffer->__a = 0x5deece66dull;
buffer->__c = 0xb;
buffer->__init = 1;
}
/* Do the real work. We choose a data type which contains at least
48 bits. Because we compute the modulus it does not care how
many bits really are computed. */
X = (uint64_t) xsubi[2] << 32 | (uint32_t) xsubi[1] << 16 | xsubi[0];
result = X * buffer->__a + buffer->__c;
xsubi[0] = result & 0xffff;
xsubi[1] = (result >> 16) & 0xffff;
xsubi[2] = (result >> 32) & 0xffff;
return 0;
}
int
__drand48_迭代(无符号短整型xsubi[3],结构drand48_数据*缓冲区)
{
uint64_t X;
uint64_t结果;
/*初始化缓冲区(如果尚未完成)*/
如果(不太可能(!缓冲区->初始化))
{
缓冲区->\uuuu a=0x5deec66dull;
缓冲区->\uuu c=0xb;
缓冲区->初始化=1;
}
/*做真正的工作。我们选择一个至少包含
48位。因为我们计算模,所以它不关心如何计算
许多位实际上是经过计算的*/
X=(uint64_t)xsubi[2]u c;
xsubi[0]=结果&0xffff;
xsubi[1]=(结果>>16)&0xffff;
xsubi[2]=(结果>>32)&0xffff;
返回0;
}
线程创建的开销很大,并行性带来的改进必须足够大才能保证开销。对于M=100000000
您刚刚发现M
太小(或者线程创建太慢)。事实并非如此。如果我将函数drand48()改为一个简单的算术函数,并行程序的运行速度会比串行程序快。。。那么有可能drand48()正在进行错误共享。。。也就是说,当两个线程共享一条缓存线时,尽管它们访问的数据不在同一地址,但至少有一个线程是写入程序-缓存线在内核之间来回乒乓。。如果缓存线的高争用性会影响性能(有时会非常严重),这是有道理的。但是,我怎样才能真正证明虚假分享真的发生了呢?