並行快速排序

並行快速排序

感謝網友淺水清流投遞本稿。

併發算法是多核時代開始流行的技術趨勢,比如tbbppl都提供了大量有用的併發算法。

經典算法中,排序是一個很適合採用分治法併發的場合,比如快速排序

常規的快速排序,先在數組中選取一個基準點,將數組分區爲小於基準點和大於基準點(相同的數可以到任一邊),對分區的子數組遞歸的執行分區的操作,當子數組長度爲1時退出遞歸。此時數組就完成了排序過程。

01 int partition(int* array, int left, int right)
02 {
03         int index = left;
04         int pivot = array[index];
05         swap(array[index], array[right]);
06         for (int i=left; i<right; i++)
07         {
08                 if (array[i] > pivot)    // 降序
09                         swap(array[index++], array[i]);
10         }
11         swap(array[right], array[index]);
12         return index;
13 }
14  
15 void qsort(int* array, int left, int right)
16 {
17         if (left >= right)
18                 return;
19         int index = partition(array, left, right);
20         qsort(array, left, index - 1);
21         qsort(array, index + 1, right);
22 }

對快排的過程分析可以發現,分區以及對子數組排序的過程均可以併發執行,這裏首先對數組進行分區,生成分區數組,爲了保證不同分區不受到影響需要先完成分區再進行排序。

01 template <typename key, typename container >void parallel_sort(container & _container)template <typename key, typename container >
02 void partition_less(std::vector<key> * vless, container * _container, key privot){
03 for(size_t i = 0; i < (*_container).size(); i++){
04         if ((*_container)[i] < privot){
05             vless->push_back((*_container)[i]);
06         }
07     }
08 }
09  
10 template <typename key,  typename container >
11 void partition_more(std::vector<key> * vmore, container * _container, key privot){
12 for(size_t i = 0; i < (*_container).size(); i++){
13         if ((*_container)[i] >= privot){
14             vmore->push_back((*_container)[i]);
15         }
16     }
17 }

在完成分區之後,遞歸執行排序操作,並將排序好的分區重新寫入待排序數組。

01 template <typename key, typename container >
02 int sort_less(container * _container, std::vector<key> & vless, boost::atomic_uint32_t * depth){
03     parallel_sort_impl<key>(&vless, *depth);
04  
05     for(size_t i = 0; i < vless.size(); i++){
06         (*_container)[i] = vless[i];
07     }
08  
09     return 0;
10 }
11  
12 template <typename key, typename container >
13 int sort_more(container * _container, std::vector<key> & vmore, boost::atomic_uint32_t * depth){
14     parallel_sort_impl<key>(&vmore, *depth);
15  
16     size_t pos = (*_container).size()-vmore.size();
17     for(size_t i = 0; i < vmore.size(); i++){
18         (*_container)[i+pos] = vmore[i];
19     }
20  
21     return 0;
22 }
23  
24 template <typename key, typename container >
25 void parallel_sort_impl(container * _container, boost::atomic_uint32_t & depth){
26     if (_container->size() < threshold || depth.load() > processors_count()){
27         std::sort(_container->begin(), _container->end());
28     }else{
29         key privot = (*_container)[_container->size()/2];
30  
31     std::vector<key> vless, vmore;
32     auto partition_result = std::async(std::launch::async, partition_less<key, container>, &vless, _container, privot);
33     partition_more(&vmore, _container, privot);
34     partition_result.get();
35  
36         auto result = std::async(std::launch::async, sort_less<key, container>, _container, vless, &depth);
37         sort_more(_container, vmore, &depth);
38         result.get();
39     }
40 }

這裏採取了一個有趣的策略,就是通過數組的大小,計算出排序好的元素在原數組中的位置(這樣即使是併發的訪問數組,但是因爲不同的線程各自訪問的自己的下標位置,所以仍然是線程安全的),然後將排序好的數組直接寫入到原數組,完成整個排序。

這裏的併發採用了c++11中的promise:http://imcc.blogbus.com/logs/174131661.html

(全文完)如果您喜歡此文請點贊,分享,評論。


發佈了90 篇原創文章 · 獲贊 45 · 訪問量 71萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章