Nếu bạn quyết định sử dụng Openmp 3.0
, bạn có thể sử dụng tính năng task
:
#pragma omp parallel
#pragma omp single
{
for(auto it = l.begin(); it != l.end(); ++it)
#pragma omp task firstprivate(it)
it->process();
#pragma omp taskwait
}
này sẽ thực hiện vòng lặp trong một thread, nhưng ủy nhiệm việc xử lý các yếu tố cho người khác.
Nếu không có OpenMP 3.0
cách dễ nhất là viết tất cả các con trỏ đến các phần tử trong danh sách (hoặc vòng lặp trong vec-tơ và lặp lại trên vectơ đó. Bằng cách này bạn sẽ không phải sao chép bất kỳ thứ gì và tránh chi phí sao chép các phần tử bản thân, vì vậy nó không cần phải nhiều overhead:
std::vector<my_element*> elements; //my_element is whatever is in list
for(auto it = list.begin(); it != list.end(); ++it)
elements.push_back(&(*it));
#pragma omp parallel shared(chunks)
{
#pragma omp for
for(size_t i = 0; i < elements.size(); ++i) // or use iterators in newer OpenMP
elements[i]->process();
}
Nếu bạn muốn tránh sao chép ngay cả những gợi ý, bạn luôn có thể tạo song song cho vòng lặp bằng tay bạn có thể có đề truy cập vào các yếu tố xen kẽ của. danh sách (như được đề xuất bởi KennyTM) hoặc chia phạm vi trong các bộ phận gần như tương đương nhau trước khi lặp lại và lặp qua những phần đó. stnodes hiện đang được xử lý bởi các luồng khác (ngay cả khi chỉ có con trỏ tiếp theo), điều này có thể dẫn đến chia sẻ sai. Đây sẽ trông gần như thế này:
#pragma omp parallel
{
int thread_count = omp_get_num_threads();
int thread_num = omp_get_thread_num();
size_t chunk_size= list.size()/thread_count;
auto begin = list.begin();
std::advance(begin, thread_num * chunk_size);
auto end = begin;
if(thread_num = thread_count - 1) // last thread iterates the remaining sequence
end = list.end();
else
std::advance(end, chunk_size);
#pragma omp barrier
for(auto it = begin; it != end; ++it)
it->process();
}
Rào cản không được nghiêm chỉnh cần thiết, tuy nhiên nếu process
đột biến các yếu tố xử lý (có nghĩa là nó không phải là một phương pháp const), có thể có một số loại chia sẻ sai mà không có nó, nếu các luồng lặp qua chuỗi đã bị biến đổi. Bằng cách này sẽ lặp lại 3 * n lần so với chuỗi (trong đó n là số lượng chủ đề), do đó, tỷ lệ có thể ít hơn thì tối ưu cho một số lượng lớn các luồng.
Để giảm chi phí, bạn có thể đặt thế hệ phạm vi bên ngoài #pragma omp parallel
, tuy nhiên bạn sẽ cần phải biết có bao nhiêu luồng sẽ tạo thành phần song song. Vì vậy, bạn có thể phải tự thiết lập các num_threads
, hoặc sử dụng omp_get_max_threads()
và xử lý các trường hợp số lượng các chủ đề tạo ra là ít hơn omp_get_max_threads()
(mà chỉ là một ràng buộc trên). Cách cuối cùng có thể được xử lý bởi có thể gán mỗi thread severa khối trong trường hợp đó (sử dụng #pragma omp for
nên làm điều đó):
int max_threads = omp_get_max_threads();
std::vector<std::pair<std::list<...>::iterator, std::list<...>::iterator> > chunks;
chunks.reserve(max_threads);
size_t chunk_size= list.size()/max_threads;
auto cur_iter = list.begin();
for(int i = 0; i < max_threads - 1; ++i)
{
auto last_iter = cur_iter;
std::advance(cur_iter, chunk_size);
chunks.push_back(std::make_pair(last_iter, cur_iter);
}
chunks.push_back(cur_iter, list.end();
#pragma omp parallel shared(chunks)
{
#pragma omp for
for(int i = 0; i < max_threads; ++i)
for(auto it = chunks[i].first; it != chunks[i].second; ++it)
it->process();
}
này sẽ chỉ mất ba lần lặp trên list
(hai, nếu bạn có thể nhận được kích thước của danh sách mà không cần lặp lại).Tôi nghĩ đó là điều tốt nhất bạn có thể làm cho các trình vòng lặp truy cập không ngẫu nhiên mà không cần sử dụng tasks
hoặc lặp qua một số cơ sở hạ tầng ngoài (như một vectơ của con trỏ).
Cảm ơn câu trả lời chi tiết. – mshang
Tôi muốn lặp lại toàn bộ 'bản đồ'. Làm thế nào tôi có thể lặp lại bằng cách sử dụng OpenMp toàn bộ bản đồ? – user