UPDATE2。解决了!这是内存问题。有人在这里讨论它: http://dontpad.com/bench_mem
更新。我的目标是实现最佳吞吐量。我的所有结果都在这里。
顺序结果: https://docs.google.com/spreadsheet/ccc?key=0AjKHxPB2qgJXdE8yQVNHRkRiQ2VzeElIRWwxMWtRcVE&usp=sharing
平行结果*: https://docs.google.com/spreadsheet/ccc?key=0AjKHxPB2qgJXdEhTb2plT09PNEs3ajBvWUlVaWt0ZUE&usp=sharing
multsoma_par1_vN,N确定每个线程如何访问数据。 N:1 - NTHREADS位移,2 - L1位移,3 - L2位移,4 - TAM / NTHREADS
我很难弄清楚为什么我的并行代码运行速度比顺序代码快一点。
我基本上做的是循环一个类型的大数组(10 ^ 8个元素)(int / float / double)并应用计算:A = A * CONSTANT + B.其中A和B是数组相同的大小。
顺序代码只执行单个函数调用。 并行版本创建pthreads并使用与启动函数相同的功能。
我使用gettimeofday(),RDTSC()以及最近的getrusage()来测量时间。我的主要结果由每个元素的时钟(CPE)表示。
我的处理器是i5-3570K。 4个核心,没有超线程。
问题在于我可以在顺序代码下达到2.00 CPE,并且在并行时我的最佳性能是1.84 CPE。我知道我通过创建pthreads并调用更多的计时例程来获得开销,但我不认为这是没有获得更好的时间的原因。 我测量了每个线程的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(我的最佳时机)和每个线程7.0~7.5 CPE。
使用多个线程而不是可用核心(4)我仍然将CPE低于2.0但不高于1.85(由于开销而大多数时候更高)。
我怀疑上下文切换可能是限制因素。当使用2个线程运行时,我可以计算从每个线程切换5到10个非自愿的上下文... 但我对此不太确定。那些似乎很少的上下文切换足以使我的CPE几乎翻倍吗?我期待使用我所有的CPU核心至少获得1.00 CPE。
我进一步研究了这个并分析了这个函数的汇编代码。它们是相同的,除了一些额外的移位并在函数的最开始添加(4条指令)并且它们没有循环。
如果你想看一些代码:
#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;
}
我感谢任何帮助。
答案 0 :(得分:0)
在看完完整的代码之后,我更赞同评论中对@nosid的猜测:因为计算操作与内存负载的比率很低,而且数据(如果我没有记错的话大约800M)不要适合缓存,内存带宽可能是限制因素。主存储器的链接共享给处理器中的所有内核,因此当其带宽饱和时,所有内存操作都会停止并占用更长的时间;因此CPE增加。
此外,代码中的以下位置是数据竞争:
avg_thread_CPE+=CPE;
将不同线程上计算的CPE值汇总到单个全局变量而不进行任何同步。
下面我留下了我最初答案的部分内容,包括&#34;第一个陈述&#34;在评论中提到。我仍然觉得它是正确的,因为CPE的定义是操作在单个元素上占用的时钟数。
您不应期望每个元素的时钟(CPE)指标会降低 由于使用了多个线程。根据定义,它的速度有多快 平均处理单个数据项。线程有助于更快地处理所有数据(通过同时处理不同的数据) 核心),所以经过的挂钟时间,即执行时间 整个计划,应该会减少。