Tải bản đầy đủ - 0 (trang)
IX.7 Giải phương trình vi phân.

IX.7 Giải phương trình vi phân.

Tải bản đầy đủ - 0trang

for (int j=1; j<4; j++)

{

#pragma omp parallel

#pragma omp sections nowait

{

#pragma omp section

u1=y+h*(a11*f(x+c1*h,u)+a12*f(x+c2*h,v));

#pragma omp section

v1=y+h*(a21*f(x+c1*h,u)+a22*f(x+c2*h,v));

}

u=u1; v=v1;

}



được dùng để tìm điểm bất động

u0  y,



v0  y



uk 1  y  (a11 f (x  c1, uk )  a12 f (x  c 2 , vk ))



k 0,1,2,3



vk 1  y  (a 21f (x  c1, uk )  a 22f (x  c 2 , vk ))



k 0,1,2,3



Trong đó, các lệnh

#pragma omp parallel

#pragma omp sections nowait

{

#pragma omp section

....

// thread 0

#pragma omp section

....

// thread 1

}



tạo ra 2 thread chạy song song với nhau (trên máy có 2 vi-xử-lý thì mỗi thread sử dụng 1

vi-xử-lý). Thread thứ nhất tính uk 1  y  (a11f (x  c1, uk )  a12 f (x  c 2 , vk )) , và

thread thứ hai tính vk 1  y  (a 21f (x  c1, uk )  a 22 f (x  c 2 , vk )) .

Khi ra khỏi vùng song song này, trình sẽ chạy tuần tự, và vì thế mà lệnh

u=u1; v=v1;



chỉ do thread master thực hiện (hiện tượng race sẽ không xảy ra).

Để thấy được ý nghĩa của các lệnh gán gián tiếp trên (tránh được hiện tượng race),

chúng ta sử dụng các lệnh gán trực tiếp (sẽ gây ra hiện tượng race!).

for (int j=1; j<4; j++)

{

#pragma omp parallel

#pragma omp sections nowait

{



#pragma omp section

u = y+h*(a11*f(x+c1*h,u)+a12*f(x+c2*h,v));

#pragma omp section

v = y+h*(a21*f(x+c1*h,u)+a22*f(x+c2*h,v));

}

}



sẽ có thể gây ra hiện tượng “race”, và vì vậy mà kết quả tính sẽ khơng còn đúng nữa.

Cơng thức y(x  )  y  (b1f (x  c1, u4 )  b 2 f (x  c 2 , v 4 )) do các lệnh

y+=h*(b1*f(x+c1*h,u)+b2*f(x+c2*h,v));

x+=h;



thực hiện73.

IX.7.2. Giải phương trình



 2u

t 2



 2



 2u

2x



.



Hệ số courant được lấy là C=0.9. Đoạn [0.1] được chia ra làm 80 phần bằng nhau.

#include

#include

#define n 80

#define C 0.9

#define f(x) (x<0.7) ? umax/0.7*x : umax/0.3*(1.0-x)

void main(int argc, char *argv[])

{ double u[n+2], um[n+2], up[n+2];

double h, t=0, dt, V=1.0, tstop=1.0, umax=0.05, UL=0,UR=0;

h = 1.0/(n+1); for (int i=0; i<=n; i++) u[i] = f(i*h);

u[0] = UL; u[n+1] = UR;

for (i=1; i<=n; i++) um[i] = u[i]+0.5*C*C*(u[i+1]-2*u[i]+u[i-1]);

um[0] = UL; um[n+1] = UR;

dt = C/V*h;

while (t < tstop){

t += dt;

#pragma omp parallel for

for (i=1; i<=n; i++) up[i] = 2*u[i]-um[i]+C*C*(u[i+1]-2*u[i]+u[i-1]);

up[0]=UL; up[n+1] = UR;

#pragma omp parallel for

for (i=0; i<=n; i++) um[i]=u[i]; u[i]=up[i];

}

for (i=0; i<= n; i++) {cout << u[i] << " ";} cout << "\n";

}



73



Chạy trình trên máy 2 vi-xử-lý Pentium 3, do việc tạo lập song song cũng tốn thời gian, mà thời gian tính theo

thuật toán song song lại chậm hơn là tuần tự (bỏ đi tất cả các chỉ thị song song)



IX.7.3. Chỉ thị đồng bộ “#pragma omp barrier”.

Đồng bộ hóa các q trình là việc buộc chúng, tuy hoạt động song song, nhưng lại phải

tuân thủ theo thời gian thực. Thông thường việc này xảy ra là vì các q trình có sử dụng

các kết quả của nhau. Một trong số các ví dụ điển hình cần đến đồng bộ hóa q trình

tinh tốn là việc giải phương trình, hay hệ phương trình vi phân bằng phương pháp

Runge-Kutta.



Q trình giải là một vòng lặp, trong mỗi lần lặp một số nào đó các vi xử lý chạy song

song, cùng thực hiện một số công việc tương tự như nhau, rồi trao đổi kết quả cho nhau

trước khi bước vào vòng lặp tiếp theo. Như vậy các thread chỉ có thể cùng bước vào

vòng lặp mới cùng một lúc.

Sơ đồ chung của việc đồng bộ hóa 2 q trình như sau



Các ngăn “barrier” làm nhiệm vụ chặn các thread lại, và chỉ khi nào chúng cùng đến

được “barrier” thì mới được thực hiện lệnh tiếp theo. Tình huống này thường chỉ gặp khi

tất cả các thread có sử dụng kết quả tính tốn của nhau, và mỗi một trong số chúng phải

tin chắc là các kết quả do các thread khác tính ấy đã được tính ra rồi. Q trình một vi

xử lý ghi kết quả ra bộ nhớ cũng không hẳn là đơn giản – do muốn tăng tốc độ tính tốn

mà, việc ghi này có thể chạy song song với các lệnh tiếp theo. Nếu tình trạng này mà

xảy ra thì các thread có thể đến “Barrier” mà dữ liệu (đã ghi rồi) vẫn chưa ra đến bộ nhớ.

Để ra lệnh cho các vi xử lý phải thật sự ghi xong dữ liệu rồi mới được đi làm các việc

khác chúng ta dùng chỉ thị “flush”.

//..do something

//..write the result to memory

#pragma omp barrier

//..Read memory



Sau đây chúng ta trình bày sơ đồ thuật giải song song, giải hệ phương trình vi phân bằng

phương pháp Runge-Kutta.

#pragma omp parallel

{ #pragma omp sections nowait

{

#pragma omp section

K1 = h · f(xn, yn, zn) ;



#pragma omp flush(K1)

#pragma omp barrier

K2 = h · f(xn + h/2, yn + K1/2, zn + L1/2) ;



#pragma omp flush(K2)

#pragma omp barrier

K3 = h · f(xn + h/2, yn + K2/2, zn + L2/2) ;



#pragma omp flush(K3)

#pragma omp barrier

K4 = h · f(xn + h, yn + K3, zn + L3) ;

K = K /6+ K /3+K /3 + K /6 ;

1



y



2



3



4



=y +K;

n+1



n



#pragma omp section

L = h · g(x , y , z ) ;

1



n



n



n



#pragma omp flush(L1)

#pragma omp barrier

L = h · g(x + h/2, y + K /2, z + L /2) ;

2



n



n



1



n



1



#pragma omp flush(L2)

#pragma omp barrier

L = h · g(x + h/2, y + K /2, z + L /2) ;

3



n



n



2



n



2



#pragma omp flush(L3)

#pragma omp barrier

L = h · g(x + h, y + K , z + L ) ;

4



n



n



3



n



L = L /6 + L /3 + L /3+ L /6 ;

1



z



2



3



=z +L;

n+1



n



} /* end of sections */

x =x +h;

n+1



n



} /* end of parallel section */



4



3



IX.8 Ví dụ.



Đặt giả sử

1) Chúng ta có một mơ hình tính tốn với các phần tử liên kết với nhau theo đồ thị trên,

và sự kết nối được biểu diễn theo bảng d[30] sau



2) Qui luật “vật lý” tác động lên các đỉnh là

Tại mỗi đỉnh đồ thị trên, có lưu một giá trị “áp”. Sau mỗi nhịp thời gian giá trị “áp”

truyền cho nhau, “áp” truyền từ nơi có áp cao đến nơi có áp thấp và sự thay đổi tỷ lệ

thuận với hiệu “áp”.

Bài tốn đặt ra là tính được các giá trị áp tại các đỉnh.

n = 30



là số các kết nối.



m = 15



là số đỉnh.



d[]



bảng



Trình chạy lặp song song, mỗi lần hiệu chỉnh lại “áp” của các đỉnh, cho tới khi nào sai số

trung bình nhỏ hơn 0.001.

Sơ đồ OpenMP

# pragma omp parllel , default (share)

{do while (err > 0.001)

{ # pragma parallel for private(flux), reduction(+:err)

for(i=1; n; i++)

{ flux = (y[d[i,1]] – y[d[i,2]])/2;

#pragma atomic

x[d[i,1]] = x[d[i,1]] – flux;



#pragma atomic

x[d[i,2]] = x[d[i,2]] + flux;

err = err + flux*flux;

}

}

# pragma single

err = err / n;

# pragma parallel for

for (j=1; j
} Mixed System



X. Công nghệ tổ hợp

Để khai thác mạng, mỗi máy có thể có nhiều vi-xử-lý, chúng ta tổ hợp cả hai công nghệ:

công nghệ bộ nhớ dùng chung và cơng nghệ bộ nhớ phân tán. Trong một chương trình

chúng ta có thể sử dụng cùng lúc OpenMP, thread, và MPI.

X.1 MPI & Thread.

Chúng ta có thể sử dụng các máy tính trên một mạng và trên mỗi máy tính khai báo

nhiều thread. Cơng nghệ tích hợp này được trình bày thơng qua các ví dụ sau.

Ví dụ 1.

Bài tốn. Chương trình chạy trên mạng 2 máy tính, mỗi máy có 3 thread. Các thread

trong cùng một máy tính dùng chung một buffer để nhận dữ liệu. Các thread ở trên 2

máy thực hiện việc gửi và nhận dữ liệu cho nhau, máy này gửi cho máy kia.

Các thread sử dụng công cụ mutex để thực hiện được việc gửi/nhận dữ liệu cho nhau.

Thiết lập thông số cho thread

rc = MPI_Init_thread(&argc, &argv, MPI_THREAD_MULTIPLE, &Level);



Nếu Level = MPI_THREAD_MULTIPLE thì các thread có thể sử dụng lệnh MPI để gửi

nhận dữ liệu (địa chỉ nhận phải bao gồm số hiệu của tiến trình và của thread).

Khởi tạo thread và giao nhiệm vụ (các thread đều chạy trình thrdsub()).

rc = pthread_create(&thrdid[i],(void*)NULL,(void*)thrdsub,(void*)&thrdrank[i]);

#include

#include

#include

#include

#include

#include "mpi.h"

void *thrdsub(void *arg);

int comm_rank, comm_size, Level, num_msgs = 50;

pthread_mutex_t mpi_lock = PTHREAD_MUTEX_INITIALIZER;

int main(int argc, char *argv[])

{

int i, rc, thrdrank[4] = {0,1,2,3};

pthread_t thrdid[4];

setbuf(stdout,NULL);

rc = MPI_Init_thread(&argc,&argv,MPI_THREAD_MULTIPLE, &Level);

if (rc != MPI_SUCCESS) { printf("MPI_Init_thread failed with rc=%d\n",rc); exit(-1); }

MPI_Comm_rank(MPI_COMM_WORLD,&comm_rank);



MPI_Comm_size(MPI_COMM_WORLD,&comm_size);

if (comm_size != 2) {printf("**** please run me with exactly 2 processes\n"); exit(-1); }

switch (Level)

{ case MPI_THREAD_SINGLE:

printf("thread level supported: MPI_THREAD_SINGLE\n");

break;

case MPI_THREAD_FUNNELED:

printf("thread level supported: MPI_THREAD_FUNNELED\n");

break;

case MPI_THREAD_SERIALIZED:

printf("thread level supported: MPI_THREAD_SERIALIZED\n");

break;

case MPI_THREAD_MULTIPLE:

printf("thread level supported: MPI_THREAD_MULTIPLE\n");

break;

default:

printf("thread level supported: UNRECOGNIZED\n");

exit(-1);

}

for (i=0; i < 3; i++)

{

rc = pthread_create(&thrdid[i],(void*)NULL,(void*)thrdsub,(void*)&thrdrank[i]);

if (rc < 0) { printf("create of thread failed with rc=%d\n",rc); exit(-1); }

}

for (i=0; i < 3; i++) { thread_join(thrdid[i],NULL); }

MPI_Finalize();

exit(0);

}

void *thrdsub(void *arg)

{

int mythrdrank, num_sent;

char s_buff[1024] = "hello", r_buff[1024];

MPI_Status status;

mythrdrank = *((int *)arg);

printf("comm_rank=%d mythrdrank=%d\n",comm_rank,mythrdrank);

if (Level == MPI_THREAD_SINGLE || Level == MPI_THREAD_FUNNELED)

{ if (mythrdrank > 0) pthread_exit(NULL); }

for (num_sent=0; num_sent < num_msgs; num_sent++)

{if (comm_rank == 0)

{ if (Level == MPI_THREAD_SINGLE || Level == MPI_THREAD_FUNNELED)

{ pthread_mutex_lock(&mpi_lock); }

MPI_Send(s_buff,strlen(s_buff)+1,MPI_CHAR,1,mythrdrank,MPI_COMM_WORLD);

r_buff[0] = '\0';

MPI_Recv(r_buff,1024,MPI_CHAR,1,mythrdrank,MPI_COMM_WORLD,&status);

if (strcmp(s_buff,r_buff) != 0)

{printf("comm_rank=%d mythrdrank=%d bad recv\n",comm_rank,mythrdrank);

exit(-1); }

if (Level == MPI_THREAD_SINGLE || Level == MPI_THREAD_FUNNELED)



Tài liệu bạn tìm kiếm đã sẵn sàng tải về

IX.7 Giải phương trình vi phân.

Tải bản đầy đủ ngay(0 tr)

×