Xử lý song song (multithreading) với OpenMP trong C++

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ụ:
  • 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 →

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)


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

Ở 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

Ta có thể thấy vòng for được chia ra và chạy trên 4 thread có id lần lượt là 0, 1, 2 và 3. Nếu muốn chỉ định chính xác số lượng thread sử dụng thì có thể dùng hàm omp_set_num_threads(), code dưới đây sẽ chỉ định sử dụng 2 thread Chạy thử và ra kết quả như sau, vòng for chỉ chạy trên 2 thread (id = 0, 1) nhưng kết quả cuối cùng của arr[] thì vẫn vậy

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) —