Hế lô anh em. Trong bài này mình sẽ giới thiệu cho anh em một vài trick cơ bản về lập trình xử lý song song (multithreading) trong C++ sử dụng OpenMP. Món này rất dễ nhưng khá ít người biết và sử dụng, nếu các bạn biết áp dụng thì cũng rất thú vị và làm cho code của chúng ta “chất” và “ngầu” vl luôn 😎.
OpenMP là gì ?
OpenMP là tên một tính năng built-in sẵn trong hầu hết các C++ compiler, bạn chỉ cần chỉ định option build khi compile code là có thể sử dụng ngay mà không cần link thêm bất cứ thư viện nào khác. OpenMP có thể được sử dụng để các LTV truyền đạt cho compiler biết rằng một số đoạn code nào đó là độc lập vào cần chạy song song với nhau ở các thread khác nhau để tăng tốc độ xử lý cho chương trình.
Những compiler nào support OpenMP ?
- GCC (GNU Compiler Collection) support OpenMP 4.5 từ GCC version 6.1, OpenMP 4.0 từ GCC version 4.9, OpenMP 3.1 từ GCC version 4.7, OpenMP 3.0 từ GCC version 4.4, và OpenMP 2.5 từ GCC version 4.2. Thêm option -fopenmp để enable OpenMP.
- Clang++ support OpenMP 4.5 từ version 3.9 (without offloading), OpenMP 4.0 từ version 3.8 và OpenMP 3.1 từ version 3.7. Thêm option -fopenmp để enable OpenMP.
- Microsoft Visual C++ (cl) support OpenMP 2.0 từ version 2005. Thêm option /openmp để enable OpenMP.
- Ngoài ra còn Solaris Studio, Intel C Compiler (icc) cũng support OpenMP
Làm thế nào để có thể sử dụng tính năng OpenMP trong C++ ?
- Enable tính năng OpenMP của compiler:
- Với Visual Studio: Có thể enable trên IDE bằng cách đi đến Properties → C/C++ → Language → OpenMP và set là Yes (/openmp)

-
- Với GCC, Clang++: Thêm option -fopenmp vào câu lệnh compile source code. Ví dụ:
1g++ sample.cpp -fopenmp
- Với GCC, Clang++: Thêm option -fopenmp vào câu lệnh compile source code. Ví dụ:
- Include file header omp.h
- Sử dụng các cú pháp của OpenMP: cụ thể ở phần sau ↓
Sử dụng Parallel Sections trong OpenMP
Các xử lý mà bạn muốn chỉ thị cho compiler biết rằng chúng cần được chạy song song ở các thread khác nhau thì cần đặt vào trong các #pragma omp section bên trong #pragma omp parallel sections như code sample bên dưới →
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
#include <iostream> #include <omp.h> int main() { std::cout << "begin "; #pragma omp parallel sections { #pragma omp section { std::cout << "hello "; std::cout << "hello "; std::cout << "hello "; } #pragma omp section { std::cout << "world "; std::cout << "world "; std::cout << "world "; } } std::cout << "end" << std::endl; return 0; } |
2 section với “#pragma omp section” ở đằng trước này sẽ được chạy song song ở 2 thread khác nhau. Chính vì vậy nếu bạn chạy code ở trên thì mỗi lần chạy có thể sẽ ra một kết quả khác nhau. Bên dưới lài kết quả một số lần chạy của mình (trên Visual Studio 2015) →
1 2 3 4 |
begin hello hello hello world world world end # lần 1 begin hello world hello hello world world end # lần 2 begin hello hello hello world world world end # lần 3 begin hello hello world world world hello end # lần 4 |
Sử dụng Parallel For Loop trong OpenMP
Khi các bạn muốn xử lý đặt bên trong một vòng for nào đó được tự động phân chia và chạy song song ở các thread khác nhau thì bạn dùng “#pragma omp parallel for” →
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#include <iostream> #include <omp.h> int main() { int arr[16]; // parallel for loop printf("Assign value of arr[]:\n"); #pragma omp parallel for for (int i = 0; i < 15; ++i) { arr[i] = i; printf(" Thread %d: arr[%d] = %d\n", omp_get_thread_num(), i, arr[i]); } // normal for loop printf("=====================\n"); printf("Check value of arr[]:\n"); for (int i = 0; i < 15; ++i) { arr[i] = i; printf(" arr[%d] = %d\n", i, arr[i]); } return 0; } |
Ở ví dụ trên thì code bên trong vòng for sẽ được tư động chia ra chạy trên nhiều thread, số lần lặp trên mỗi thread sẽ được chia đều nhất có thể giữa các thread. Ở trên do không chỉ định số lượng thread nên số lượng thread sẽ được allocate tự động. Chương trình trên mỗi lần chạy khác nhau có thể print ra màn hình khác nhau nhưng kết quả cuối cùng là giá trị của array arr thì sẽ giống nhau. Dưới đây là kết quả một trong số các lần chạy của chương trình →
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
Assign value of arr[]: Thread 0: arr[0] = 0 Thread 0: arr[1] = 1 Thread 1: arr[4] = 4 Thread 1: arr[5] = 5 Thread 1: arr[6] = 6 Thread 2: arr[8] = 8 Thread 2: arr[9] = 9 Thread 2: arr[10] = 10 Thread 2: arr[11] = 11 Thread 0: arr[2] = 2 Thread 0: arr[3] = 3 Thread 1: arr[7] = 7 Thread 3: arr[12] = 12 Thread 3: arr[13] = 13 Thread 3: arr[14] = 14 ===================== Check value of arr[]: arr[0] = 0 arr[1] = 1 arr[2] = 2 arr[3] = 3 arr[4] = 4 arr[5] = 5 arr[6] = 6 arr[7] = 7 arr[8] = 8 arr[9] = 9 arr[10] = 10 arr[11] = 11 arr[12] = 12 arr[13] = 13 arr[14] = 14 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
#include <iostream> #include <omp.h> int main() { int arr[16]; omp_set_num_threads(2); // parallel for loop printf("Assign value of arr[]:\n"); #pragma omp parallel for for (int i = 0; i < 15; ++i) { arr[i] = i; printf(" Thread %d: arr[%d] = %d\n", omp_get_thread_num(), i, arr[i]); } // normal for loop printf("=====================\n"); printf("Check value of arr[]:\n"); for (int i = 0; i < 15; ++i) { arr[i] = i; printf(" arr[%d] = %d\n", i, arr[i]); } return 0; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
Assign value of arr[]: Thread 0: arr[0] = 0 Thread 0: arr[1] = 1 Thread 0: arr[2] = 2 Thread 1: arr[8] = 8 Thread 0: arr[3] = 3 Thread 0: arr[4] = 4 Thread 0: arr[5] = 5 Thread 0: arr[6] = 6 Thread 0: arr[7] = 7 Thread 1: arr[9] = 9 Thread 1: arr[10] = 10 Thread 1: arr[11] = 11 Thread 1: arr[12] = 12 Thread 1: arr[13] = 13 Thread 1: arr[14] = 14 ===================== Check value of arr[]: arr[0] = 0 arr[1] = 1 arr[2] = 2 arr[3] = 3 arr[4] = 4 arr[5] = 5 arr[6] = 6 arr[7] = 7 arr[8] = 8 arr[9] = 9 arr[10] = 10 arr[11] = 11 arr[12] = 12 arr[13] = 13 arr[14] = 14 |
Thực ra OpenMP còn nhiều cú pháp khác nữa nhưng mình xin tạm dừng về topic ở đây vì không muốn làm nó phức tạp thêm, nên sử dụng ở mức simple như thế này là đủ, mình nghĩ vậy. Nếu anh em nào muốn tìm hiểu thêm thì cứ Google Search keyword “OpenMP C++” sẽ ra cả mớ.
Tham khảo
- https://bisqwit.iki.fi/story/howto/openmp/
- https://docs.microsoft.com/en-us/cpp/parallel/openmp/reference/openmp-directives?view=vs-2019#for-openmp
— Phạm Minh Tuấn (Shun) —
