Chỉ thị liên quan tới chia sẻ công việc

Một phần của tài liệu Song song hoá thuật toán Bames hut với OpenMP (Trang 29)

2.6.4.1 Do/for

Chỉ thị Do/for cho biết vòng lặp for nằm trong khai báo phải được thực thi song song.

Lê Thị Lan Phương 24

Hình 12: Hình minh họa chỉ thị Do/for

Cấu trúc:

#pragma omp for [clause [clause]…] <new_line> C/C++ for loop Trong đó clause có thể là: • private (list) firstprivate (list) lastprivate (list) reduction (operator:list)

schedule (type [,chunk_size])

ordered

nowait

Ý nghĩa của private, firstprivate, lastprivate, reduction, schedule đã được mô tả ở

Lê Thị Lan Phương 25

o ordered: phải được xuất hiện khi trong vòng lặp for có sử dụng chỉ thị ordered

o nowait: cho biết các thread không cần phải tiến hành đồng bộ

hóa khi kết thúc vòng lặp song song. Chúng tiếp tục thực hiện các câu lệnh sau vòng lặp mà không cần phải chờđợi thread nào.

Có thể kết hợp giữa khai báo song song với chỉ thị chia sẻ công việc bằng cấu trúc sau:

#pragma omp parallel for [clauses] <new_line> for loop

2.6.4.2 Sections

Chỉ thị Sections chỉ ra các đoạn mã được phân chia như thế nào giữa các thread. Một khai báo sections có thể gồm nhiều section con độc lập với nhau. Mỗi một section

được thực hiện 1 lần bởi một thread. Nếu thời gian thực hiện là đủ nhanh và cách cài đặt cho phép, một thread có thể thực hiện nhiều hơn 1 section.

Nếu số lượng thread nhiều hơn số lượng section, khi đó một vài thread có thể rỗi. Nếu số lượng thread ít hơn so với section, tùy thuộc vào cách cài đặt sẽ xác định các section được thực hiện như thế nào.

Lê Thị Lan Phương 26

Hình 13: Hình minh họa chỉ thị sections

Cấu trúc:

#pragma omp sections [clause[ clause] . . . ] <new-line>

{

[#pragma omp section <new-line>]

structured-block

[#pragma omp section <new-line> structured-block . . . ] } Trong đó clause có thể là: • private lastprivate firstprivate reduction

Lê Thị Lan Phương 27

nowait

Ý nghĩa của các thông số trên giống nhưđã được mô tảở các phần trước. Có thể kết hợp giữa khai báo sections với khai báo parallel. (adsbygoogle = window.adsbygoogle || []).push({});

#pragma omp parallel sections [clauses] <new-line>

{

[#pragma omp section <new-line>]

structured-block

[#pragma omp section <new-line>]

structured-block

. . . ] }

2.6.4.3 Single

Chỉ thị cho biết đoạn mã nằm trong khai báo single sẽđược thực thi bởi duy nhất một thread. Nếu không có tùy chọn nowait, các thread khác sẽ không thực hiện chỉ thị

Lê Thị Lan Phương 28

Hình 14: Hình minh họa chỉ thị single

Cấu trúc:

#pragma omp single [clauses] <new-line> structured-block Trong đó clause có thể là: • private firstprivate nowait 2.6.5 Chỉ thị đồng bộ hóa

Xét ví dụđơn giản dưới đây:

2 thread nằm trên 2 bộ xử lý khác nhau đều cùng thực hiện việc tăng giá trị của biến x vào một thời điểm. (giả sử x = 0)

THREAD 1: increment(x)

THREAD 2: increment(x)

Lê Thị Lan Phương 29 { x = x + 1; } THREAD 1: 10 LOAD A, (x address) 20 ADD A, 1 30 STORE A, (x address) { x = x + 1; } THREAD 2: 10 LOAD A, (x address) 20 ADD A, 1 30 STORE A, (x address) Có thể xảy ra trường hợp:

• thread1 lưu giá trị x vào thanh ghi A

• thread2 lưu giá trị x vào thanh ghi A

• thread1 cộng thêm 1 vào giá trị x trong thanh ghi A

• thread2 cộng thêm 1 vào giá trị x trong thanh ghi A

• thread1 lưu giá trị trong thanh ghi A tại địa chỉ của x

• thread2 lưu giá trị trong thanh ghi A tại địa chỉ của x Kết quả: x=1, không phải là 2 như mong đợi.

Để tránh tình trạng trên, việc tăng giá trị x phải được đồng bộ hóa giữa các thread

để đảm bảo cho kết quả chính xác.

Dưới đây là một số chỉ thị liên quan tới đồng bộ hóa.

2.6.5.1 Master

Chỉ thị cho biết đoạn mã nằm trong khai báo master sẽđược thực hiện bởi master thread. Các thread khác sẽ bỏ qua đoạn mã này và tiếp tục thực hiện bình thường.

Lê Thị Lan Phương 30 (adsbygoogle = window.adsbygoogle || []).push({});

#pragma omp master <new-line> structured-block

2.6.5.2 Critical

Chỉ thị xác định đoạn mã nằm trong khai báo sẽ được truy cập bởi duy nhất một thread vào một thời điểm. Các thread khác sẽ phải chờ cho đến khi không có thread nào thực hiện đoạn mã đó.

Cấu trúc:

#pragma omp critical [(name)] <new-line> structured-block

2.6.5.3 Barrier

Khi 1 thread gặp chỉ thị barrier, thread đó sẽ phải chờ cho đến khi nào tất cả các thread còn lại đều gặp chỉ thị này.

Cấu trúc:

#pragma omp barrier <new-line>

2.6.5.4 Atomic

Chỉ thị atomic xác định một vùng bộ nhớ cụ thể nào đó sẽ được cập nhật một cách từng phần, không cho phép nhiều thread cùng thực hiện tại đó vào một thời điểm. Chỉ thị chỉ áp dụng cho các câu lệnh đơn.

Cấu trúc:

#pragma omp atomic <new-line> statement_expression Các câu lệnh đơn có thể là: ++x x++ --x x--

Lê Thị Lan Phương 31

và các toán tử +, -, *, /, &, ^, |, >> hoặc <<

2.6.5.5 Flush

Chỉ thị flush sẽ ghi lại các biến visible trong thread vào bộ nhớ. Người lập trình có thể tự xác định quá trình đồng bộ hóa một cách trực tiếp trên bộ nhớ chia sẻ thông qua việc sử dụng flush. Tùy chọn list được sử dụng để xác định danh sách các biến cần flush, nếu không có tùy chọn này tất cả các biến sẽđược ghi lại vào bộ nhớ.

Cấu trúc:

#pragma omp flush [(list)] <new-line>

2.6.5.6 Ordered

Chỉ thị ordered xác định vòng lặp sẽ được thực hiện theo thứ tự như thể được thực thi trên bộ xử lý tuần tự. Ordered chỉ xuất hiện trong khai báo chỉ thị lặp Do/for. Tại một thời điểm, chỉ có một thread thực hiện công việc trong phần khai báo ordered.

Cấu trúc:

#pragma omp ordered <new-line> structured-block

2.6.6 Thư viện và một số biến môi trường

2.6.6.1 Một số hàm trong thư viện của OpenMP

Để sử dụng được các hàm có sẵn trong OpenMP, cần phải thêm file header

“omp.h” trong khi include các thư viện.

#include <omp.h>

Một vài hàm cơ bản trong thư viện của OpenMP:

void omp_set_num_threads(int num_threads) (adsbygoogle = window.adsbygoogle || []).push({});

int omp_get_num_threads(void)

int omp_get_max_theads(void)

int omp_get_thead_num(void)

Lê Thị Lan Phương 32

int omp_in_parallel(void)

void omp_set_dynamic(int dynamic_threads)

int omp_get_dynamic(void)

void omp_set_nested(int nested)

int omp_get_nested(void)

…………

2.6.6.2 Một số biến môi trường trong OpenMP

Biến môi trường được sử dụng để điều khiển quá trình thực thi các đoạn mã song song. Truyền giá trị cho các biến môi trường tùy thuộc vào trình biên dịch và kiến trúc hệ

thống bộ nhớ chia sẻ.

Có thể truyền giá trị cho biến môi trường bằng:

export tên_biến = giá trị

setenv tên_biến giá_trị

ví dụ:

export OMP_NUM_THREADS=5

setenve OMP_NUM_THREADS 5 Một số biến môi trường thường gặp:

OMP_SCHEDULE

OMP_NUM_THREADSOMP_DYNAMIC

Lê Thị Lan Phương 33

2.7 Ví dụ về lập trình song song với OpenMP 2.7.1 omp_hello.c 2.7.1 omp_hello.c

Chương trình minh họa hoạt động của các thread khi thực hiện song song.

• Biến tidđược khai báo là private, lưu ID của mỗi thread.

• Biến private nthreads cho biết số lượng thread tham gia vào quá trình song song.

2.7.2 Cách biên dịch

Tùy theo các nhà cung cấp và trình biên dịch hiện sử dụng, có thể có nhiều cách

để biên dịch một chương trình OpenMP.

Với hệ thống máy IBM, dùng cờ-qsmp=omp (adsbygoogle = window.adsbygoogle || []).push({});

Với hệ thống máy Intel, dùng cờ-openmp

Với hệ thống Compag, dùng cờ-omp

/* omp_hello.c */ #include <omp.h> int main () { int nthreads, tid;

/* Fork a team of threads giving them their own copies of variables */

#pragma omp parallel private(nthreads, tid) {

/* Obtain thread number */ tid = omp_get_thread_num();

printf("Hello World from thread = %d\n", tid); /* Only master thread does this */

if (tid == 0) {

nthreads = omp_get_num_threads();

printf("Number of threads = %d\n", nthreads); }

Lê Thị Lan Phương 34

Trên máy IBM AIX, để dịch chương trình “omp_hello.c”, dùng lệnh:

xlc_r –qsmp=omp omp_hello.c –o hello

Để thực hiện chương trình, gõ lệnh: ./hello

Nếu không chỉ rõ số lượng thread cần sử dụng để thực hiện quá trình song song, thì chương trình sẽ lấy số thread mặc định hiện có trong hệ thống kiến trúc bộ nhớ chia sẻ. Có thể xác định số thread cần thiết thông qua hàm thư viện omp_set_num_threads(int num_threads) hoặc truyền giá trị cho biến môi trường OMP_NUM_THREADS bằng lệnh: export OMP_NUM_THREADS

Giả sử, trong ví dụ trên đặt số thread là 4:

export OMP_NUM_THREADS=4

Xem trang

http://www.navo.hpc.mil/Resources/Hardware/Romulus_Users_Guide.html#ProgEnv

để biết thêm chi tiết về một số chỉ thị biên dịch.

2.7.3 Kết quả

Hello World from thread = 0 Number of threads = 4

Hello World from thread = 3 Hello World from thread = 1 Hello World from thread = 2

Lê Thị Lan Phương 35

Chương 3: SONG SONG HÓA THUT TOÁN BARNES-HUT

3.1 Treecode

Để tiến hành song song hóa thuật toán Barnes-Hut với bài toán N-body, ta xét chương trình treecode của J. Barnes làm ví dụ.

Treecode là một trong những chương trình mô phỏng bài toán N-body. Dựa trên nền tảng của thuật toán Barnes-Hut, treecode thực hiện nhanh hơn và kiểm soát lỗi tốt hơn so với các chương trình mô phỏng hệ N-body trước đó.

Như đã biết, trong thuật toán Barnes-Hut, sau khi xây dựng cây Quadtree (hoặc Octree), ta tiến hành duyệt cây để tính lực tác dụng lên từng hạt. Tuy nhiên, việc duyệt cây tốn rất nhiều thời gian. Treecode giảm thiểu chi phí cho duyệt cây bằng cách sử dụng tính chất: các hạt liền kề có danh sách các tương tác với nó là giống nhau. Ý tưởng này đã

được sử dụng trước đó để nhằm tăng tốc khi tính toán lực trên các máy vector [3], nhưng các chương trình đó chỉ đơn giản áp dụng tìm kiếm cây trong một khối hạt nhỏ. Treecode áp dụng cho tất cả các mức của cây. (adsbygoogle = window.adsbygoogle || []).push({});

Lực tác dụng lên các hạt được tính thông qua một vòng duyệt cây đệ quy. Quá trình duyệt cây đệ quy này lưu trữ và cập nhật danh sách các tương tác (interaction list). Tại mỗi mức của cây, hạt b (giả sử nằm trong ô c) được sử dụng để phân biệt với danh sách các tương tác mà b có thể có. Khi duyệt đệ quy tới hạt b, danh sách tương tác sẽ được sử dụng để tính lực hấp dẫn và thế năng tại hạt b.

3.1.1 Cấu trúc dữ liệu của cây

Cấu trúc dữ liệu chính được sử dụng trong treecode là cây Octree, bao gồm các body và các cell. Lá của cây lưu trữ thông tin của body. Các nút trong của cây là các cell. Tiến hành duyệt toàn bộ cây bắt đầu từ gốc của cây.

Lê Thị Lan Phương 36

Hình 15: Cấu trúc dữ liệu cây trong treecode (1)

Cấu trúc node biểu diễn các thông tin chung của body và cell. Theo lý thuyết, mỗi một thành phần của cây có thểđược biểu diễn là tổ hợp của body và cell. Nhưng cách biểu diễn đó là không hiệu quả, vì cấu trúc của body và cell đòi hỏi không gian bộ nhớ

khác nhau. Do vậy người ta sử dụng cấu trúc node để biểu diễn chung cho body và cell. Việc ép kiểu được sử dụng để chuyển con trỏ có kiểu tùy ý thành con trỏ trỏ tới node, body và cell.

typedef struct _node {

short type;

bool update;

real mass;

vector pos; struct _node *next; } node, *nodeptr;

#define Type(x) (((nodeptr) (x))->type) #define Update(x) (((nodeptr) (x))->update) #define Mass(x) (((nodeptr) (x))->mass) #define Pos(x) (((nodeptr) (x))->pos) #define Next(x) (((nodeptr) (x))->next)

Lê Thị Lan Phương 37

Type(q) trả lại kiểu của node q, có giá trị là CELL hoặc BODY

Update(q) có giá trị là boolean, cho biết q có cần cập nhật lực tương tác không?

Next(q) là con trỏ, trỏ tới node tiếp theo của q, sau khi tất cả các con của q đã được duyệt.

Mass(q) là khối lượng của hạt q hoặc là khối lượng của tất cả các hạt có trong cell q

Pos(q) là vị trí của hạt q hoặc vị trí của tâm khối trong cell q Cấu trúc body biểu diễn các hạt. typedef struct { node bodynode; vector vel; vector acc; real phi; } body, *bodyptr;

#define Vel(x) (((bodyptr) (x))->vel) #define Acc(x) (((bodyptr) (x))->acc) #define Phi(x) (((bodyptr) (x))->phi)

Trong đó:

Vel(b) là vận tốc của hạt b

Acc(b) là gia tốc của hạt b

Phi(b) là thế năng của hạt b

Cấu trúc cell biểu diễn các nút trong của cây

#define NSUB (1 << NDIM) typedef struct {

Lê Thị Lan Phương 38 #if !defined(QUICKSCAN) real rcrit2; #endif nodeptr more; union { nodeptr subp[NSUB]; matrix quad; } sorq; } cell, *cellptr; #if !defined(QUICKSCAN)

#define Rcrit2(x) (((cellptr) (x))->rcrit2) #endif

#define More(x) (((cellptr) (x))->more)

#define Subp(x) (((cellptr) (x))->sorq.subp) #define Quad(x) (((cellptr) (x))->sorq.quad) (adsbygoogle = window.adsbygoogle || []).push({});

Trong đó:

Subq(c) là mảng các con trỏ trở tới các con của c

More(c) là con trỏ trỏ tới con đầu tiên trong các con của c

Quad(c) là ma trận quadrupole moments

Rcrit2(c) là bình phương bán kính mà nếu nằm ngoài bán kính đó, ô cell c được coi như là một cell interaction.

Lê Thị Lan Phương 39

Hình 16: Cấu trúc dữ liệu cây trong treecode (2)

3.1.2 Các biến toàn cục

Treecode sử dụng các biến toàn cục. Biến toán cục được sử dụng chung cho tất cả

các tệp của chương trình và được khai báo với định nghĩa global. Định nghĩa global như

sau:

#define global extern

Các tham sđầu vào:

theta: cho biết độ chính xác khi tính toán lực. Tham số này không được xác định nếu bản QUICKSCAN được biên dịch

options: là một chuỗi các tùy chọn để điều khiển thời gian chạy

usequad: cờ cho biết có sử dụng quadrupole correctión hay không?

Các biến được s dng khi xây dng cây:

root: con trỏ trỏ tới root cell

rsize: kích cỡ của root cell

ncell: số các cell được sử dụng để xây dựng cây

tdepth: chiều cao của cây

cputree: thời gian CPU đòi hỏi để xây dựng cây

Lê Thị Lan Phương 40

actmax:độ dài lớn nhất của danh sách active trong khi tính lực

nbbcalc: số các tương tác giữa các hạt với nhau

nbccalc: số các tương tác giữa các hạt và các cell

cpuforce: thời gian CPU cần để tính lực

3.2 Thử nghiệm và đánh giá hiệu năng của treecode 3.2.1 Thử nghiệm chương trình treecode 3.2.1 Thử nghiệm chương trình treecode

Toàn bộ mã chương trình treecode do J. Barnes viết có thể được download tại trang web http://www.ifa.hawaii.edu/~barnes/treecode/treeguide.html

Treecode được viết bằng ngôn ngữ C (ANSI C). Giả sử chương trình được biên dịch bởi trình biên dịch của hệđiều hành LINUX. (adsbygoogle = window.adsbygoogle || []).push({});

Download file treecode.tar.gz tại trang web trên. Để thực hiện chương trình, copy file vào thư mục riêng, rồi thực hiện lệnh gunzip để giải nén tệp.

$ gunzip treecode.tar.gz $ tar xvf treecode.tar

Thư mục sau đó sẽ chứa các file .c , .h và Makefile

Trước khi biên dịch, có thể cần phải chỉnh sửa một số thông tin trong Makefile tùy theo kiến trúc máy tính và trình biên dịch hiện đang sử dụng. Ở đây, ta sửa lại thông tin trong Makefile bằng cách thêm tùy chọn –pg vào các cờ biên dịch: CCFLAGS và cờ

LDFLAGSđể khi thực hiện chương trình sẽ sinh ra file gmon.out

Tùy chọn biên dịch trong Makefile được sửa lại như dưới đây:

# Compiler options. # LINUX:

CCFLAGS = -pg -DLINUX LDFLAGS = -pg

OPTFLAG = -O3

Lê Thị Lan Phương 41

$ make treecode

Thực hiện chương trình bằng ./treecode [tham số]

Có thể xem các tham số bằng câu lệnh: ./treecode –help

treecode Hierarchical N-body code (theta scan) in= Input file with initial conditions out= Output file of N-body frames

dtime=1/32 Leapfrog integration timestep eps=0.025 Density smoothing length

theta=1.0 Force accuracy parameter usequad=false if true, use quad moments options= Various control options tstop=2.0 Time to stop integration dtout=1/4 Data output timestep

nbody=4096 Number of bodies for test run seed=123 Random number seed for test run save= Write state file as code runs restore= Continue run from state file VERSION=1.4 Joshua Barnes February 21 2001

Khi thực hiện chương trình, kết quả của quá trình tính toán sẽđược hiển thị ra màn hình, có dạng như sau:

Lê Thị Lan Phương 42

3.2.2 Đánh giá hiệu năng

Mặc dù treecode là một cải tiến của thuật toán Barnes-Hut, với tốc độ tính toán lực nhanh hơn và vấn đề kiểm soát lỗi tốt hơn so với các chương trình trước đó, song vấn

đề duyệt cây vẫn chiếm đa số thời gian thực hiện chương trình.

Có thể xem thời gian thực hiện các hàm trong toàn bộ chương trình thông qua

Một phần của tài liệu Song song hoá thuật toán Bames hut với OpenMP (Trang 29)