感謝網友淺水清流投遞本稿。
併發算法是多核時代開始流行的技術趨勢,比如tbb,ppl都提供了大量有用的併發算法。
經典算法中,排序是一個很適合採用分治法併發的場合,比如快速排序。
常規的快速排序,先在數組中選取一個基準點,將數組分區爲小於基準點和大於基準點(相同的數可以到任一邊),對分區的子數組遞歸的執行分區的操作,當子數組長度爲1時退出遞歸。此時數組就完成了排序過程。
01 |
int partition( int *
array, int left, int right) |
04 |
int pivot
= array[index]; |
05 |
swap(array[index],
array[right]); |
06 |
for ( int i=left;
i<right; i++) |
09 |
swap(array[index++],
array[i]); |
11 |
swap(array[right],
array[index]); |
15 |
void qsort ( int *
array, int left, int right) |
19 |
int index
= partition(array, left, right); |
20 |
qsort (array,
left, index - 1); |
21 |
qsort (array,
index + 1, right); |
對快排的過程分析可以發現,分區以及對子數組排序的過程均可以併發執行,這裏首先對數組進行分區,生成分區數組,爲了保證不同分區不受到影響需要先完成分區再進行排序。
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]); |
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]); |
在完成分區之後,遞歸執行排序操作,並將排序好的分區重新寫入待排序數組。
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); |
05 |
for ( size_t i
= 0; i < vless.size(); i++){ |
06 |
(*_container)[i]
= vless[i]; |
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); |
16 |
size_t pos
= (*_container).size()-vmore.size(); |
17 |
for ( size_t i
= 0; i < vmore.size(); i++){ |
18 |
(*_container)[i+pos]
= vmore[i]; |
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()); |
29 |
key
privot = (*_container)[_container->size()/2]; |
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(); |
36 |
auto
result = std::async(std::launch::async, sort_less<key, container>, _container, vless, &depth); |
37 |
sort_more(_container,
vmore, &depth); |
這裏採取了一個有趣的策略,就是通過數組的大小,計算出排序好的元素在原數組中的位置(這樣即使是併發的訪問數組,但是因爲不同的線程各自訪問的自己的下標位置,所以仍然是線程安全的),然後將排序好的數組直接寫入到原數組,完成整個排序。
這裏的併發採用了c++11中的promise:http://imcc.blogbus.com/logs/174131661.html
(全文完)如果您喜歡此文請點贊,分享,評論。