MPI对道路车辆情况的Nagel-Schreckenberg 模型进行蒙特卡洛模拟
平臺Ubuntu 16.04,Linux下MPI環境的安裝見鏈接:https://blog.csdn.net/lusongno1/article/details/61709460
據 Nagel-Schreckenberg 模型,車輛的運動滿足以下規則:
1.???假設當前速度是 v ,和前一輛車的距離為d。
2.? ?如果 d > v,它在下一秒的速度會提高到 v + 1 ,直到達到規定的最高限速。
3.???如果 d <= v,那么它在下一秒的速度會降低到 d - 1 。
4.???前三條完成后,司機還會以概率 p 隨機減速1個單位,速度不會為負值。
5.???基于以上幾點,車輛向前移動v(這里的v已經被更新)個單位。
實驗規模:
實驗設計:
初始化條件:
所有車輛初速度為4,車輛間距為8,最大速度為8,最小速度為1,最后一輛車的位置是0。減速概率為0.3
車輛結構體:
typedef struct car{int v;int d;int p; }car;所有車輛為一個數組car_list
核心循環:
//更新距離 car_list[i].d=car_list[i+1].p-car_list[i].p;//依賴于前一輛車 //更新速度 if(car_list[i].d>car_list[i].v&& car_list[i].v<vmax)car_list[i].v++;//加速 if(car_list[i].d<=car_list[i].v)car_list[i].v=car_list[i].d-1;//減速 if(car_list[i].v>1 && rand()%10< p )car_list[i].v--;//隨機減速 //更新位置 car_list[i].p+=car_list[i].v;對這段程序進行周期次數的循環
并行設計:
在每個周期中,按照核心數(0…N-1)將車輛分為N個連續的區間,每個核心計算各自的部分。
i=(num_car/numprocs*myid);//指向這個集合的第一個元素除第一個核心外其他的進程K都需要在更新自己的車輛部分之前發送第一輛車的位置到進程K-1,除最后一個進程外的其他進程K都要在計算自己部分的最后一輛車時接受進程K+1發送的位置數據以更新最后一輛車。
為避免標準通信方式導致后面的進程要等待前面的進程執行到最后開始接受的時候才執行完發送,采用緩存通信方式(MPI_Bsend)。
在周期結束之前要同步所有進程。
MPI_Barrier(MPI_COMM_WORLD);輸出結果:
只在四線程執行時輸出到文件,將每個進程自己部分的車輛數據發送到第一個進程,第一個進程接收其它進程的數據整合后輸出,并進行統計。
車輛的信息輸出到result.txt中,格式為 進行輸出的線程號 第幾輛車:速度 位置 和前一輛車的距離
統計輸出到statistic.txt中,前面輸出的是速度統計,對應的速度有幾輛車 。后面是位置統計,在位置范圍有幾輛車
結果分析:
可見不管哪種規模都會有大量的車處于速度為1的狀態,隨機減速會導致堵車情況。
運行時間分析:
10000
rank=0time:12.911823
rank=1time:12.937384
rank=2time:12.968312
rank=3time:12.837026
100000
rank=0time:127.054991
rank=1time:127.597898
rank=2time:127.428670
rank=3time:127.302298
?
rank=0time:169.043639
rank=1time:169.092520
rank=2time:169.038982
?
rank=0time:137.758731
rank=1time:137.782351
?
rank=0time:255.181992
500000
rank=0 time:158.620365
rank=1time:159.041463
rank=2time:158.827125
rank=3time:158.519957
?
rank=0time:211.242267
rank=1time:211.114535
rank=2time:211.117872
?
rank=0time:171.935731
rank=1time:171.970319
?
rank=0time:319.567614
?
1000000
rank=0 time:190.271859
rank=1time:190.056185
rank=2time:189.846401
rank=3time:190.314395
?
rank=0time:211.273677
rank=1time:211.328568
rank=2time:211.304030
?
rank=0time:172.247044
rank=1time:172.281147
?
rank=0time:318.965036
規模 | 時間/s | |||
100k*2k | 255 | 137 | 169 | 127 |
500k*0.5k | 319 | 171 | 211 | 158 |
1000K*0.3k | 318 | 172 | 211 | 190 |
加速比 | ||||
100k*2k | 1 | 1.861314 | 1.508876 | 2.007874 |
500k*0.5k | 1 | 1.865497 | 1.511848 | 2.018987 |
1000K*0.3k | 1 | 1.848837 | 1.507109 | 1.673684 |
實驗遇到的問題:
1.如何串行執行
MPI的并行是進程的并行,所以MPI_Finalize()只是將資源釋放了,并不是之后的程序就串行執行了,要想串行,可以指定一個進程執行。
2.結果輸出
在用進程0輸出所有車輛數據時發現只有線程0處理的部分數據有變更,其余數據維持在初始化時的狀態。那么應該是每個進程是將處理了自己部分的數據的備份,而不是在原有數據的基礎上處理的。所以要一個進程進行輸出。
如果每個進程都自己輸出數據就會產生文件寫沖突而導致結果的不可預知,所以要將其它進程的數據發送到一個進程,用一個進程進行輸出。
3.進程同步
如果進程間通信時設置的tag可以區分所有周期(比如設置為j*10+myid),那么在每個周期結束時就沒有必要同步所有進程。
但是在這樣修改之后運行時間沒有什么區別,應該是核心的處理能力類似,同步產生的開銷并不明顯。
源程序:#include <stdio.h> #include <stdlib.h> #include <time.h> #include <mpi.h>int num_car=100000; int num_cycle[]={2000,500,300};const int v0=4,vmax=8,p=5;typedef struct car {int v;int d;int p; }car; car car_list[1000000];int count[10]={0,0,0,0,0, 0,0,0,0,0}; int pos_count[20*8+10000*8]={0};//count per 100int main(int argc,char *argv[]) {//四線程寫結果的時候打開文件FILE*fp = fopen("result_100000.txt","w");FILE*fp2 = fopen("statistic_100000.txt","w");//初始條件int i=0;for(i=0;i<num_car;i++){car_list[i].v=v0;car_list[i].p=vmax*i;car_list[i].d=vmax;}int myid, numprocs;clock_t starttime,endtime;int namelen;char processor_name[MPI_MAX_PROCESSOR_NAME];MPI_Init(&argc,&argv);MPI_Comm_size(MPI_COMM_WORLD,&numprocs);MPI_Comm_rank(MPI_COMM_WORLD,&myid);int* mpi_buffer=malloc(sizeof(int)*1000000);MPI_Buffer_attach(mpi_buffer,sizeof(int)*1000000);//模擬過程開始starttime=clock();int j=0;for(j=0;j<2000;j++){i=(num_car/numprocs*myid);//指向這個集合的第一個元素if(myid!=0)//如果不是第一個線程就要向前發送數據{ MPI_Bsend(&(car_list[i].p),1,MPI_INT,myid-1,j*10+myid,MPI_COMM_WORLD);}for(;i<num_car/numprocs*(myid+1)-1;i++){//更新距離car_list[i].d=car_list[i+1].p-car_list[i].p;//更新速度if(car_list[i].d>car_list[i].v && car_list[i].v<vmax)car_list[i].v++;if(car_list[i].d<=car_list[i].v)car_list[i].v=car_list[i].d-1;srand(i*num_car+j);if( car_list[i].v>1 ){int r=rand()%10;if(r<p){car_list[i].v--;//printf("#");}}//更新位置car_list[i].p+=car_list[i].v;}if(myid!=numprocs-1)//不是最后一個進程{int temp;MPI_Status status;MPI_Recv(&(temp),1,MPI_INT,myid+1,j*10+myid+1,MPI_COMM_WORLD,&status);//更新距離car_list[i].d=temp-car_list[i].p; //printf("%d temp %d %d\n",myid,temp,car_list[i].d);}//更新速度if(car_list[i].v<vmax)car_list[i].v++;if(car_list[i].d<=car_list[i].v)car_list[i].v=car_list[i].d-1;srand((unsigned) time(NULL));if( car_list[i].v>1 && rand()%10< p ){car_list[i].v--;}//更新位置car_list[i].p+=car_list[i].v;//MPI_Barrier(MPI_COMM_WORLD);}//for cycle//模擬過程結束endtime=clock();printf("rank=%d time:%lf\n",myid,(double)(endtime-starttime)/CLOCKS_PER_SEC);//四線程的時候向文件寫結果,別的線程時注釋掉好了MPI_Barrier(MPI_COMM_WORLD);if(myid==0){MPI_Send((car_list),sizeof(car)*num_car/4,MPI_BYTE,3,myid,MPI_COMM_WORLD); }if(myid==1){MPI_Send((car_list+num_car/4),sizeof(car)*num_car/4,MPI_BYTE,3,myid,MPI_COMM_WORLD); }if(myid==2){MPI_Send((car_list+2*num_car/4),sizeof(car)*num_car/4,MPI_BYTE,3,myid,MPI_COMM_WORLD); }if(myid==numprocs-1){MPI_Status status;MPI_Recv((car_list),sizeof(car)*num_car/4,MPI_BYTE,0,0,MPI_COMM_WORLD,&status);MPI_Recv((car_list+num_car/4),sizeof(car)*num_car/4,MPI_BYTE,1,1,MPI_COMM_WORLD,&status);MPI_Recv((car_list+2*num_car/4),sizeof(car)*num_car/4,MPI_BYTE,2,2,MPI_COMM_WORLD,&status); int a;for(a=0;a<num_car;a++){fprintf(fp,"%d %d:%d %d %d\n",myid,a,car_list[a].v,car_list[a].p,car_list[a].d);}for(i=0;i<num_car;i++){count[car_list[i].v]++;pos_count[car_list[i].p/1000]++;}int k;for(k=0;k<10;k++){fprintf(fp2,"%d\t:%d\n",k,count[k]);}for(k=0;k<2*8+num_car*8/1000;k++){fprintf(fp2,"%d\t%d\n",k,pos_count[k]);} }MPI_Barrier(MPI_COMM_WORLD);fclose(fp);fclose(fp2);MPI_Finalize();return 0; }轉載于:https://www.cnblogs.com/biaoJM/p/10186706.html
總結
以上是生活随笔為你收集整理的MPI对道路车辆情况的Nagel-Schreckenberg 模型进行蒙特卡洛模拟的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 对Linux命令od -tc -tx1的
- 下一篇: 关于__str__和__repr__的用