순차적 실행을 위해 필자의 기능만큼 최적화했습니다. openMP를 사용할 때 성능이 향상되지 않습니다. 1 개의 코어가 있고 8 개의 코어가있는 머신에서 프로그램을 시험해 보았는데 성능은 같습니다.
연도를 20으로 설정하면
코어 1 개가 있습니다.
코어 8 개 : 1 초.순차 실행을 위해 프로그램 최적화시 openMP를 사용한 후 성능이 향상되지 않습니다.
올해를 25로 설정하면
코어 1 개 : 40 초입니다.
코어 8 개 : 40 초.
1 핵심 기계 : 내 노트북의 인텔 코어 2 듀오 1.8 GHz의, 우분투는 리눅스
8 코어 기계 : 3.25 GHz의, 우분투는 리눅스
각 경로. 따라서 루프 크기가 기하 급수적으로 증가하고 openMP 스레드의 공간이 제로가 될 것으로 예상됩니다. 내 루프에서는 하나의 변수 만 축소합니다. 다른 모든 변수는 읽기 전용입니다. 나는 쓰는 함수 만 사용하고 스레드 안전하다고 생각한다.
또한 내 프로그램에서 Valgrind cachegrind를 실행합니다. 나는 출력을 완전히 이해하지 못하지만 캐시 미스 나 잘못된 공유가없는 것처럼 보입니다.
나는 나의 완전한 프로그램은 다음과 같습니다
gcc -O3 -g3 -Wall -c -fmessage-length=0 -lm -fopenmp -ffast-math
로 컴파일합니다. 코드를 많이 게시하여 죄송합니다. OpenMP 나 C에 익숙하지 않아 주 작업을 잃지 않고 코드를 더 이상 재개 할 수 없습니다.
OpenMP를 사용할 때 성능을 어떻게 향상시킬 수 있습니까?
프로그램을 더 빨리 실행할 수있는 컴파일러 플래그 또는 C 트릭이 있습니까?
TEST.C
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <omp.h>
#include "test.h"
int main(){
printf("starting\n");
int year=20;
int tradingdate0=1;
globalinit(year,tradingdate0);
int i;
float v=0;
long n=pow(tradingdate0+1,year);
#pragma omp parallel for reduction(+:v)
for(i=0;i<n;i++)
v+=pathvalue(i);
globaldel();
printf("finished\n");
return 0;
}
//***function on which openMP is applied
float pathvalue(long pathindex) {
float value = -ctx.firstpremium;
float personalaccount = ctx.personalaccountat0;
float account = ctx.firstpremium;
int i;
for (i = 0; i < ctx.year-1; i++) {
value *= ctx.accumulationfactor;
double index = getindex(i,pathindex);
account = account * index;
double death = fmaxf(account,ctx.guarantee[i]);
value += qx(i) * death;
if (haswithdraw(i)){
double withdraw = personalaccount*ctx.allowed;
value += px(i) * withdraw;
personalaccount = fmaxf(personalaccount-withdraw,0);
account = fmaxf(account-withdraw,0);
}
}
//last year
double index = getindex(ctx.year-1,pathindex);
account = account * index;
value+=fmaxf(account,ctx.guarantee[ctx.year-1]);
return value * ctx.discountfactor;
}
int haswithdraw(int period){
return 1;
}
float getindex(int period, long pathindex){
int ndx = (pathindex/ctx.chunksize[period])%ctx.tradingdate;
return ctx.stock[ndx];
}
float qx(int period){
return 0;
}
float px(int period){
return 1;
}
//****global
struct context ctx;
void globalinit(int year, int tradingdate0){
ctx.year = year;
ctx.tradingdate0 = tradingdate0;
ctx.firstpremium = 1;
ctx.riskfreerate = 0.06;
ctx.volatility=0.25;
ctx.personalaccountat0 = 1;
ctx.allowed = 0.07;
ctx.guaranteerate = 0.03;
ctx.alpha=1;
ctx.beta = 1;
ctx.tradingdate=tradingdate0+1;
ctx.discountfactor = exp(-ctx.riskfreerate * ctx.year);
ctx.accumulationfactor = exp(ctx.riskfreerate);
ctx.guaranteefactor = 1+ctx.guaranteerate;
ctx.upmove=exp(ctx.volatility/sqrt(ctx.tradingdate0));
ctx.downmove=1/ctx.upmove;
ctx.stock=(float*)malloc(sizeof(float)*ctx.tradingdate);
int i;
for(i=0;i<ctx.tradingdate;i++)
ctx.stock[i]=pow(ctx.upmove,ctx.tradingdate0-i)*pow(ctx.downmove,i);
ctx.chunksize=(long*)malloc(sizeof(long)*ctx.year);
for(i=0;i<year;i++)
ctx.chunksize[i]=pow(ctx.tradingdate,ctx.year-i-1);
ctx.guarantee=(float*)malloc(sizeof(float)*ctx.year);
for(i=0;i<ctx.year;i++)
ctx.guarantee[i]=ctx.beta*pow(ctx.guaranteefactor,i+1);
}
void globaldel(){
free(ctx.stock);
free(ctx.chunksize);
free(ctx.guarantee);
}
test.h
float pathvalue(long pathindex);
int haswithdraw(int period);
float getindex(int period, long pathindex);
float qx(int period);
float px(int period);
//***global
struct context{
int year;
int tradingdate0;
float firstpremium;
float riskfreerate;
float volatility;
float personalaccountat0;
float allowed;
float guaranteerate;
float alpha;
float beta;
int tradingdate;
float discountfactor;
float accumulationfactor;
float guaranteefactor;
float upmove;
float downmove;
float* stock;
long* chunksize;
float* guarantee;
};
struct context ctx;
void globalinit();
void globaldel();
편집 나는 상수로 모든 글로벌 변수를 단순화합니다. 20 년 동안이 프로그램은 두 번 더 빨리 실행됩니다. 예를 들어 스레드 수를 OMP_NUM_THREADS=4 ./test
으로 설정하려고했습니다. 그러나 그것은 나에게 어떤 성능 이득도주지 않았다.
gcc에 문제가 있습니까?
TEST.C
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <omp.h>
#include "test.h"
int main(){
starttimer();
printf("starting\n");
int i;
float v=0;
#pragma omp parallel for reduction(+:v)
for(i=0;i<numberofpath;i++)
v+=pathvalue(i);
printf("v:%f\nfinished\n",v);
endtimer();
return 0;
}
//function on which openMP is applied
float pathvalue(long pathindex) {
float value = -firstpremium;
float personalaccount = personalaccountat0;
float account = firstpremium;
int i;
for (i = 0; i < year-1; i++) {
value *= accumulationfactor;
double index = getindex(i,pathindex);
account = account * index;
double death = fmaxf(account,guarantee[i]);
value += death;
double withdraw = personalaccount*allowed;
value += withdraw;
personalaccount = fmaxf(personalaccount-withdraw,0);
account = fmaxf(account-withdraw,0);
}
//last year
double index = getindex(year-1,pathindex);
account = account * index;
value+=fmaxf(account,guarantee[year-1]);
return value * discountfactor;
}
float getindex(int period, long pathindex){
int ndx = (pathindex/chunksize[period])%tradingdate;
return stock[ndx];
}
//timing
clock_t begin;
void starttimer(){
begin = clock();
}
void endtimer(){
clock_t end = clock();
double elapsed = (double)(end - begin)/CLOCKS_PER_SEC;
printf("\nelapsed: %f\n",elapsed);
}
test.h
float pathvalue(long pathindex);
int haswithdraw(int period);
float getindex(int period, long pathindex);
float qx(int period);
float px(int period);
//timing
void starttimer();
void endtimer();
//***constant
const int year= 20 ;
const int tradingdate0= 1 ;
const float firstpremium= 1 ;
const float riskfreerate= 0.06 ;
const float volatility= 0.25 ;
const float personalaccountat0= 1 ;
const float allowed= 0.07 ;
const float guaranteerate= 0.03 ;
const float alpha= 1 ;
const float beta= 1 ;
const int tradingdate= 2 ;
const int numberofpath= 1048576 ;
const float discountfactor= 0.301194211912 ;
const float accumulationfactor= 1.06183654655 ;
const float guaranteefactor= 1.03 ;
const float upmove= 1.28402541669 ;
const float downmove= 0.778800783071 ;
const float stock[2]={1.2840254166877414, 0.7788007830714049};
const long chunksize[20]={524288, 262144, 131072, 65536, 32768, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1};
const float guarantee[20]={1.03, 1.0609, 1.092727, 1.1255088100000001, 1.1592740743, 1.1940522965290001, 1.2298738654248702, 1.2667700813876164, 1.304773183829245, 1.3439163793441222, 1.384233870724446, 1.4257608868461793, 1.4685337134515648, 1.512589724855112, 1.557967416600765, 1.6047064390987882, 1.6528476322717518, 1.7024330612399046, 1.7535060530771016, 1.8061112346694148};
이미 순차 코드에 대한 성능이 향상되었으므로 항상 시작해야합니다. 매개 변수가있는 전역 구조는 기본적으로 컴파일러가 최적화 할 수있는 모든 가능성을 없애줍니다. 규칙은 모든 상수를 상수 (정수의 경우는 "enum"또는 부동 소수점의 경우 #define)로하고 모든 런타임 매개 변수를 함수의 인수로 전달하는 간단한 규칙입니다. 컴파일러가하는 방식으로 프로그램의 다른 부분이'struct'의 특정 값을 변경하지 않으므로 상수 전파를 할 수 없다는 것을 확신 할 수 없습니다. 청소는 병렬 편집에도 도움이됩니다. –
@JensGustedt 글로벌 변수를 관리하는 올바른 방법을 알려 주셔서 감사합니다. 내 코드를 2 배 더 빠르게 만들었습니다 (제 질문에 편집을 참조하십시오). 그래도 병렬화에서 이득이 없다. –
Nicolas, 당신은 직접 따라 가지 않았습니다. 여러 가지 .o 파일이있는 프로그램을 작성하자 마자 다중 정의 된 기호로 어려움을 겪게됩니다. gcc에 문제가있는 경우 우리는 말할 수 없습니다. 사용하는 버전을 알려주지 않았습니다. OpenMP가 차이를 만드는 지 확인하려면 (-O3 -S'를 사용하여) 프로그램을 어셈블리로 컴파일하고 결과 코드와'-fopenmp'를 비교합니다. –