Openmp là gì

Những người khác kể từ đó đã trả lời hầu hết câu hỏi nhưng tôi muốn chỉ ra một số trường hợp cụ thể trong đó một kiểu lập lịch cụ thể phù hợp hơn những kiểu khác. Lập lịch kiểm soát cách phân chia các lần lặp vòng lặp giữa các luồng. Chọn lịch trình phù hợp có thể có tác động lớn đến tốc độ của ứng dụng.

staticlịch trình có nghĩa là các khối lặp lại được ánh xạ tĩnh tới các luồng thực thi theo kiểu vòng lặp. Điều thú vị với lập lịch tĩnh là thời gian chạy OpenMP đảm bảo rằng nếu bạn có hai vòng lặp riêng biệt với cùng số lần lặp và thực thi chúng với cùng số luồng bằng cách sử dụng lập lịch tĩnh, thì mỗi luồng sẽ nhận được chính xác cùng một phạm vi lặp [ s] ở cả hai vùng song song. Điều này khá quan trọng trên hệ thống NUMA: nếu bạn chạm vào bộ nhớ nào đó trong vòng lặp đầu tiên, bộ nhớ đó sẽ nằm trên nút NUMA nơi chuỗi đang thực thi. Sau đó, trong vòng lặp thứ hai, cùng một luồng có thể truy cập cùng một vị trí bộ nhớ nhanh hơn vì nó sẽ nằm trên cùng một nút NUMA.

Hãy tưởng tượng có hai nút NUMA: nút 0 và nút 1, ví dụ như bảng mạch Intel Nehalem hai ổ cắm với CPU 4 nhân trong cả hai ổ cắm. Sau đó, các luồng 0, 1, 2 và 3 sẽ nằm trên nút 0 và các luồng 4, 5, 6 và 7 sẽ nằm trên nút 1:

| | core 0 | thread 0 | | socket 0 | core 1 | thread 1 | | NUMA node 0 | core 2 | thread 2 | | | core 3 | thread 3 | | | core 4 | thread 4 | | socket 1 | core 5 | thread 5 | | NUMA node 1 | core 6 | thread 6 | | | core 7 | thread 7 |

Mỗi lõi có thể truy cập bộ nhớ từ mỗi nút NUMA, nhưng truy cập từ xa chậm hơn [1,5x - chậm hơn 1,9 lần trên Intel] so với truy cập nút cục bộ. Bạn chạy một cái gì đó như thế này:

char *a = [char *]malloc[8*4096]; #pragma omp parallel for schedule[static,1] num_threads[8] for [int i = 0; i < 8; i++] memset[&a[i*4096], 0, 4096];

4096 byte trong trường hợp này là kích thước tiêu chuẩn của một trang bộ nhớ trên Linux trên x86 nếu các trang lớn không được sử dụng. Mã này sẽ bằng không toàn bộ mảng 32 KiB a. Lời malloc[]gọi chỉ dự trữ không gian địa chỉ ảo nhưng không thực sự "chạm" vào bộ nhớ vật lý [đây là hành vi mặc định trừ khi một số phiên bản khác của mallocđược sử dụng, ví dụ như một phiên bản làm trống bộ nhớ như calloc[]vậy]. Bây giờ mảng này liền kề nhưng chỉ trong bộ nhớ ảo. Trong bộ nhớ vật lý, một nửa của nó sẽ nằm trong bộ nhớ gắn với ổ cắm 0 và một nửa trong bộ nhớ gắn với ổ cắm 1. Điều này là do các phần khác nhau được làm 0 bởi các luồng khác nhau và các luồng đó nằm trên các lõi khác nhau và có một thứ gọi là lần chạm đầu tiên Chính sách NUMA có nghĩa là các trang bộ nhớ được cấp phát trên nút NUMA nơi chứa chuỗi đầu tiên "chạm vào" trang bộ nhớ.

| | core 0 | thread 0 | a[0] ... a[4095] | socket 0 | core 1 | thread 1 | a[4096] ... a[8191] | NUMA node 0 | core 2 | thread 2 | a[8192] ... a[12287] | | core 3 | thread 3 | a[12288] ... a[16383] | | core 4 | thread 4 | a[16384] ... a[20479] | socket 1 | core 5 | thread 5 | a[20480] ... a[24575] | NUMA node 1 | core 6 | thread 6 | a[24576] ... a[28671] | | core 7 | thread 7 | a[28672] ... a[32768]

Bây giờ chúng ta hãy chạy một vòng lặp khác như thế này:

#pragma omp parallel for schedule[static,1] num_threads[8] for [i = 0; i < 8; i++] memset[&a[i*4096], 1, 4096];

Mỗi luồng sẽ truy cập vào bộ nhớ vật lý đã được ánh xạ và nó sẽ có cùng một ánh xạ luồng tới vùng nhớ như trong vòng lặp đầu tiên. Nó có nghĩa là các luồng sẽ chỉ truy cập bộ nhớ nằm trong các khối bộ nhớ cục bộ của chúng, điều này sẽ nhanh chóng.

Bây giờ tưởng tượng rằng một đề án lập kế hoạch được sử dụng cho vòng lặp thứ hai: schedule[static,2]. Thao tác này sẽ "cắt" không gian lặp thành các khối gồm hai lần lặp và tổng cộng sẽ có 4 khối như vậy. Điều gì sẽ xảy ra là chúng ta sẽ có luồng sau đây ánh xạ vị trí bộ nhớ [thông qua số lần lặp]:

| | core 0 | thread 0 | a[0] ... a[8191] Language->OpenMP Support mở lên [tham số /openmp], như vậy VC++ khi biên dịch có thể sử dụng ngữ pháp của OpenMP rồi;


Khi lập trình với OpenMP, các bạn nên include OpenMP vào trong header file nhé !

Lập trình OpenMP có thể thông qua những dòng lệnh cấp cao, rất đơn giản để chuyển thành dạng xử lý song song, đạt kết quả tăng tốc cho hệ thống bằng cách dùng multi thread API; Trong trường hợp đơn giản nhất, chỉ cần một dòng lệnh là có thể khống chế các vòng lặp thành xử lý song song !

Nếu muốn chuyển vòng lặp for thành dạng xử lý song song , phải làm thế nào ?

Rất đơn giản , chỉ cần thêm vào trước vòng lặp một dòng lệnh :

như vậy là OK rồi ! Để hiểu rõ hơn, chúng ta cùng xem đoạn lập trình đơn giản này nhé !















    Ở đoạn code trên, trong hàm main[] là một vòng lặp đơn giản, chạy 10 lần, mỗi lần đều gọi hàm show[] và in ra các giá trị của i .

    Nếu lập trình trên giao diện Ubuntu, chúng ta có thể biên dịch như sau :
Ví dụ file chương trình là test.c
Ta có thể biên dịch :     gcc  test.c   -o  test
Sau đó chạy chương trình :     ./test

Đương nhiên ta sẽ được kết quả :






Nếu muốn dùng OpenMP để chuyển vòng lặp for thành xử lý song song, chúng ta chỉ cần sửa thêm vào như sau :




Đơn giản, từ đầu đến cuối chỉ cần thêm vào 2 dòng [trong khung màu xanh], và chúng ta bắt đầu chạy chương trình. Đối với hệ thống Ubuntu, ta cần phải include thêm thư viện của OpenMP vào nữa, chúng ta biên dịch như sau :

   biên dịch  :    gcc   -fopenmp   test.c   -o   testOMP





Từ kết quả trên, chúng ta có thể thấy, hệ thống không chạy theo thứ tự từ 0 đến 9 nữa ! mà OpenMP đã phân chia vòng lặp từ 0 - 9 thành 2 phần là 0 - 4, và 5 - 9, sau đó giao cho từng thread xử lý, bởi vậy chúng ta mới có kết quả như vậy.


Ở đây chỉ là ví dụ đơn giản, chưa hiện rõ sự khác biệt về tốc độ xử lý, nếu như cần phải tính toán với lượng dữ liệu lớn , và mức tính toán phức tạp hơn thì các bạn có thể thấy rõ hơn về công dụng của OpenMP trong trình xử lý song song. Khi cho chương trình chạy, chúng ta có thể thông qua trình quản lý để xem các nhân đang hoạt động như thế nào. Khi chạy với lập trình OpenMP,  thông thường các nhân đều chạy với công suất 100%...hehe...Nhưng các bạn cứ yên tâm, có lẽ sẽ không bị đứng máy đâu !



--------------------------------------------------------------------------------------------------------------------------


Qua bài viết này, chúng ta mới chỉ học được 2 dòng lệnh , thực tế thì OpenMP còn nhiều lệnh khác, nhưng thực sự dùng đến cũng không nhiều. Bài viết sau, mình sẽ giới thiệu thêm !


Nếu có chỗ nào còn chưa đúng, xin các bạn góp ý chỉ bảo  !










Video liên quan

Chủ Đề