我正在研究D編程語言的並行化庫。現在我對基本原語(並行foreach,map,reduce和tasks/futures)非常滿意,我開始考慮一些更高級別的並行算法。並行化的更明顯的候選者是排序。(什麼時候)是平行排序實用的,你如何編寫一個高效的排序?
我的第一個問題是,排序算法的並行化版本是否適用於真實世界,還是他們主要是學術?如果它們有用,它們在哪裏有用?我個人很少在我的工作中使用它們,僅僅是因爲我通常使用比單個sort()調用更粗糙的並行度級別將所有內核全部綁定到100%。其次,似乎快速排序對於大型陣列來說幾乎是尷尬的平行,但我不能得到我認爲我應該得到的近線性加速。對於快速排序,唯一固有的串行部分是第一個分區。我試着在每個分區後並行排序兩個子陣列,並行化快速排序。在簡化的僞代碼:
// I tweaked this number a bunch. Anything smaller than this and the
// overhead is smaller than the parallelization gains.
const smallestToParallelize = 500;
void quickSort(T)(T[] array) {
if(array.length < someConstant) {
insertionSort(array);
return;
}
size_t pivotPosition = partition(array);
if(array.length >= smallestToParallelize) {
// Sort left subarray in a task pool thread.
auto myTask = taskPool.execute(quickSort(array[0..pivotPosition]));
quickSort(array[pivotPosition + 1..$]);
myTask.workWait();
} else {
// Regular serial quick sort.
quickSort(array[0..pivotPosition]);
quickSort(array[pivotPosition + 1..$]);
}
}
即使對於非常大的陣列,其中第一個分區佔用的時間可以忽略不計,我只能得到大約在雙核30%的增速相比,該算法的純串行版本。我猜測瓶頸是共享內存訪問。任何關於如何消除這個瓶頸或其他瓶頸可能的見解?
編輯:我的任務池有固定數量的線程,等於系統中的核心數減1(因爲主線程也工作)。此外,我正在使用的等待類型是工作等待,即如果任務已啓動但未完成,調用workWait()
的線程會將其他作業從池中取走並執行,直至完成等待。如果任務未啓動,則在當前線程中完成。這意味着等待不是低效的。只要有工作要做,所有的線程都會保持忙碌。
我不知道如何讓自己快速排序並行較好,但有一個叫samplesort變體,它天生就比一個香草快速排序快得多,而據我所看到的,應該同樣可並行化。 –