C 基准测试,顺序x并行程序。次线性加速比?
更新2。解决了的!这是内存问题。这里有一些关于它的长凳: 更新。我的目标是实现最佳吞吐量。我所有的结果都在这里 顺序结果: 平行结果*: multsoma_par1_vN,N确定每个线程访问数据的方式。 N:1-N位移,2-L1位移,3-L2位移,4-TAM/N位移 我很难弄明白为什么我的并行代码比顺序代码运行得稍微快一点 我基本上是通过一个类型(int/float/double)的大数组(10^8个元素)循环并应用计算:a=a*常量+B。其中a和B是相同大小的数组 顺序代码只执行单个函数调用。 并行版本创建pthreads并使用与启动函数相同的函数 我正在使用gettimeofday()、RDTSC()和最近的getrusage()来测量计时。我的主要结果用每个元素的时钟数(CPE)表示 我的处理器是i5-3570K。4核,无超线程 问题是,在顺序代码下,我可以得到2.00CPE,而在并行时,我的最佳性能是1.84CPE。我知道通过创建pthread和调用更多的计时例程会增加开销,但我不认为这是没有得到更好计时的原因。 我测量了每个线程的CPE,并用1、2、3和4个线程执行程序。当只创建一个线程时,我得到的预期结果CPE约为2.00(+一些开销以毫秒表示,但总体CPE完全不受影响)。 当运行2个或更多线程时,主CPE减小,但每个线程CPE增大。 2个线程我的主CPE大约是1.9,每个线程都是3.8(为什么这不是2.0?!) 同样的情况也发生在3和4个线程上。 4个线程的主CPE大约为1.85(我的最佳计时),每个线程的CPE为7.0~7.5 使用比可用内核更多的线程(4),我仍然获得2.0以下的CPE,但不超过1.85(由于开销,大多数情况下会更高) 我怀疑上下文转换可能是这里的限制因素。当使用2个线程运行时,我可以从每个线程数5到10个线程。。。 但我对此不太确定。这些似乎很少的上下文切换是否足以使我的CPE几乎翻倍?我希望至少能用我所有的CPU核心获得1.00左右的CPE 我进一步分析了这个函数的汇编代码。它们是相同的,除了一些额外的移位和在函数的最开始处添加(4条指令)之外,它们是循环外的 如果您想查看一些代码:C 基准测试,顺序x并行程序。次线性加速比?,c,multithreading,performance,parallel-processing,benchmarking,C,Multithreading,Performance,Parallel Processing,Benchmarking,更新2。解决了的!这是内存问题。这里有一些关于它的长凳: 更新。我的目标是实现最佳吞吐量。我所有的结果都在这里 顺序结果: 平行结果*: multsoma_par1_vN,N确定每个线程访问数据的方式。 N:1-N位移,2-L1位移,3-L2位移,4-TAM/N位移 我很难弄明白为什么我的并行代码比顺序代码运行得稍微快一点 我基本上是通过一个类型(int/float/double)的大数组(10^8个元素)循环并应用计算:a=a*常量+B。其中a和B是相同大小的数组 顺序代码只执行单个函数
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/time.h>
#include <cpuid.h>
typedef union{
unsigned long long int64;
struct {unsigned int lo, hi;} int32;
} tsc_counter;
#define RDTSC(cpu_c) \
__asm__ __volatile__ ("rdtsc" : \
"=a" ((cpu_c).int32.lo), \
"=d" ((cpu_c).int32.hi) )
#define CNST 5
#define NTHREADS 4
#define L1_SIZE 8096
#define L2_SIZE 72512
typedef int data_t;
data_t * A;
data_t * B;
int tam;
double avg_thread_CPE;
tsc_counter thread_t0[NTHREADS], thread_t1[NTHREADS];
struct timeval thread_sec0[NTHREADS], thread_sec1[NTHREADS];
void fillA_B(int tam){
int i;
for (i=0;i<tam;i++){
A[i]=2; B[i]=2;
}
return;
}
void* multsoma_par4_v4(void *arg){
int w;
int i,j;
int *id = (int *) arg;
int limit = tam-14;
int size = tam/NTHREADS;
int tam2 = ((*id+1)*size);
int limit2 = tam2-14;
gettimeofday(&thread_sec0[*id],NULL);
RDTSC(thread_t0[*id]);
//Mult e Soma
for (i=(*id)*size;i<limit2 && i<limit;i+=15){
A[i] = A[i] * CNST + B[i];
A[i+1] = A[i+1] * CNST + B[i+1];
A[i+2] = A[i+2] * CNST + B[i+2];
A[i+3] = A[i+3] * CNST + B[i+3];
A[i+4] = A[i+4] * CNST + B[i+4];
A[i+5] = A[i+5] * CNST + B[i+5];
A[i+6] = A[i+6] * CNST + B[i+6];
A[i+7] = A[i+7] * CNST + B[i+7];
A[i+8] = A[i+8] * CNST + B[i+8];
A[i+9] = A[i+9] * CNST + B[i+9];
A[i+10] = A[i+10] * CNST + B[i+10];
A[i+11] = A[i+11] * CNST + B[i+11];
A[i+12] = A[i+12] * CNST + B[i+12];
A[i+13] = A[i+13] * CNST + B[i+13];
A[i+14] = A[i+14] * CNST + B[i+14];
}
for (; i<tam2 && i<tam; i++)
A[i] = A[i] * CNST + B[i];
RDTSC(thread_t1[*id]);
gettimeofday(&thread_sec1[*id],NULL);
double CPE, elapsed_time;
CPE = ((double)(thread_t1[*id].int64-thread_t0[*id].int64))/((double)(size));
elapsed_time = (double)(thread_sec1[*id].tv_sec-thread_sec0[*id].tv_sec)*1000;
elapsed_time+= (double)(thread_sec1[*id].tv_usec - thread_sec0[*id].tv_usec)/1000;
//printf("Thread %d workset - %d\n",*id,size);
//printf("CPE Thread %d - %lf\n",*id, CPE);
//printf("Time Thread %d - %lf\n",*id, elapsed_time/1000);
avg_thread_CPE+=CPE;
free(arg);
pthread_exit(NULL);
}
void imprime(int tam){
int i;
int ans = 12;
for (i=0;i<tam;i++){
//printf("%d ",A[i]);
//checking...
if (A[i]!=ans) printf("WA!!\n");
}
printf("\n");
return;
}
int main(int argc, char *argv[]){
tsc_counter t0,t1;
struct timeval sec0,sec1;
pthread_t thread[NTHREADS];
double CPE;
double elapsed_time;
int i;
int* id;
tam = atoi(argv[1]);
A = (data_t*) malloc (tam*sizeof(data_t));
B = (data_t*) malloc (tam*sizeof(data_t));
fillA_B(tam);
avg_thread_CPE = 0;
//Start Computing...
gettimeofday(&sec0,NULL);
RDTSC(t0); //Time Stamp 0
for (i=0;i<NTHREADS;i++){
id = (int*) malloc(sizeof(int));
*id = i;
if (pthread_create(&thread[i], NULL, multsoma_par4_v4, (void*)id)) {
printf("--ERRO: pthread_create()\n"); exit(-1);
}
}
for (i=0; i<NTHREADS; i++) {
if (pthread_join(thread[i], NULL)) {
printf("--ERRO: pthread_join() \n"); exit(-1);
}
}
RDTSC(t1); //Time Stamp 1
gettimeofday(&sec1,NULL);
//End Computing...
imprime(tam);
CPE = ((double)(t1.int64-t0.int64))/((double)(tam)); //diferenca entre Time_Stamps/repeticoes
elapsed_time = (double)(sec1.tv_sec-sec0.tv_sec)*1000;
elapsed_time+= (double)(sec1.tv_usec - sec0.tv_usec)/1000;
printf("Main CPE: %lf\n",CPE);
printf("Avg Thread CPE: %lf\n",avg_thread_CPE/NTHREADS);
printf("Time: %lf\n",elapsed_time/1000);
free(A); free(B);
return 0;
}
#包括
#包括
#包括
#包括
#包括
typedef联合{
无符号长int64;
结构{unsigned int lo,hi;}int32;
}tsc_计数器;
#定义RDTSC(cpu\U c)\
__asm\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu\
“=a”((cpu_c).int32.lo)\
“=d”((cpu_c.int32.hi))
#定义CNST 5
#定义第4行
#定义L1_大小8096
#定义L2_大小72512
typedef int data_t;
数据_t*A;
数据_t*B;
INTTAM;
双平均螺纹;
tsc_计数器线程_t0[n线程]、线程_t1[n线程];
struct timeval thread_sec0[NTHREADS],thread_sec1[NTHREADS];
无效填充物(内部tam){
int i;
对于(i=0;i在看到完整的代码后,我非常同意注释中对@nosid的猜测:因为计算操作与内存负载的比率很低,数据(如果我没有弄错的话,大约800M)不适合缓存,内存带宽可能是限制因素。到主内存的链接共享给处理器中的所有内核,因此当其带宽饱和时,所有内存操作开始暂停并花费更长的时间;因此CPE增加
此外,代码中的以下位置是数据竞争:
avg_thread_CPE+=CPE;
当您将在不同线程上计算的CPE值汇总到单个全局变量时,无需任何同步
下面我留下了我最初回答的部分,包括评论中提到的“第一句话”。我仍然认为CPE的定义是一个元素上的操作所占用的时钟数是正确的
您不应该期望每个元素的时钟(CPE)指标减少
由于使用了多个线程。根据定义,它是一个线程的速度
平均而言,只处理一个数据项。线程有助于更快地处理所有数据(通过在不同数据库上同时处理)
内核),因此经过的挂钟时间,即执行
整个计划,预计将减少
int*id=(int*)arg;-这看起来很可疑。通常,线程索引是通过值传递的,而不是通过指针传递的。如何创建线程?@Alexey Kukanov这不是问题,它可以工作。这个函数是pthread_create调用使用的函数,因此我必须将参数作为void*。我怀疑我见过几次的错误:线程是在循环中创建的,一个d循环索引的地址作为参数传递给所有线程。它会导致多个数据争用和未定义的行为。如果您确定这不是问题,并且每个线程都会收到一个不同变量的地址,很好。int size=tam/NTHREADS;
-如果tam
是您的元素总数,那么该除法m我可以休息一下。你正在做大量的内存操作,基本上没有计算。我想,内存带宽是这种情况下的限制因素。也许可以给你更多的见解。我不同意你的第一句话。每个线程CPE也不应该减少。你是对的,avg_thread_CPE变量在任何时候都不同步随时(我必须解决这个问题)。但不要认为这是问题所在,因为我得到的这个值是所有线程CPE的总和,并且avg CPE是可靠的。这个平均值是=avg_thread_CPE/NTHREADS。我的主要CPE是通过另一个单独调用RDT来计算的,该调用包含所有pthread创建。我只通过一个线程的一个RDTSC调用获得3.8 CPE。它们的总和是7.6(2 threa)