Dopo aver letto Stack Overflow questione Using vectors for performance improvement in Haskell descrivendo una rapida sul posto quicksort in Haskell, mi misi due obiettivi:smistamento veloce in Haskell
Attuare lo stesso algoritmo con una mediana di tre per evitare la cattiva prestazioni su vettori pre-ordinati;
Creazione di una versione parallela.
Ecco il risultato (alcuni pezzi minori sono stati lasciati per semplicità):
import qualified Data.Vector.Unboxed.Mutable as MV
import qualified Data.Vector.Generic.Mutable as GM
type Vector = MV.IOVector Int
type Sort = Vector -> IO()
medianofthreepartition :: Vector -> Int -> IO Int
medianofthreepartition uv li = do
p1 <- MV.unsafeRead uv li
p2 <- MV.unsafeRead uv $ li `div` 2
p3 <- MV.unsafeRead uv 0
let p = median p1 p2 p3
GM.unstablePartition (< p) uv
vquicksort :: Sort
vquicksort uv = do
let li = MV.length uv - 1
j <- medianofthreepartition uv li
when (j > 1) (vquicksort (MV.unsafeSlice 0 j uv))
when (j + 1 < li) (vquicksort (MV.unsafeSlice (j+1) (li-j) uv))
vparquicksort :: Sort
vparquicksort uv = do
let li = MV.length uv - 1
j <- medianofthreepartition uv li
t1 <- tryfork (j > 1) (vparquicksort (MV.unsafeSlice 0 j uv))
t2 <- tryfork (j + 1 < li) (vparquicksort (MV.unsafeSlice (j+1) (li-j) uv))
wait t1
wait t2
tryfork :: Bool -> IO() -> IO (Maybe (MVar()))
tryfork False _ = return Nothing
tryfork True action = do
done <- newEmptyMVar :: IO (MVar())
_ <- forkFinally action (\_ -> putMVar done())
return $ Just done
wait :: Maybe (MVar()) -> IO()
wait Nothing = return()
wait (Just done) = swapMVar done()
median :: Int -> Int -> Int -> Int
median a b c
| a > b =
if b > c then b
else if a > c then c
else a
| otherwise =
if a > c then a
else if b > c then c
else b
per i vettori con 1.000.000 di elementi, ottengo i seguenti risultati:
"Number of threads: 4"
"**** Parallel ****"
"Testing sort with length: 1000000"
"Creating vector"
"Printing vector"
"Sorting random vector"
CPU time: 12.30 s
"Sorting ordered vector"
CPU time: 9.44 s
"**** Single thread ****"
"Testing sort with length: 1000000"
"Creating vector"
"Printing vector"
"Sorting random vector"
CPU time: 0.27 s
"Sorting ordered vector"
CPU time: 0.39 s
mie domande sono:
- Perché le prestazioni sono sti ll diminuendo con un vettore pre-ordinato?
- Perché l'utilizzo di forkIO e quattro thread non riesce a migliorare le prestazioni?
Sono per andare a letto, quindi nessuna analisi in questo momento, solo quello che salta fuori. Quando si esegue una biforcazione su ogni chiamata ricorsiva, si creano molti grandi thread, la pianificazione del thread sopraffatta travolge il lavoro effettivo da eseguire. Se c'è anche la sincronizzazione tra i vari thread che accedono all'array coinvolto, ciò ucciderebbe totalmente le prestazioni anche per meno thread. Se vuoi accelerare, forchetta solo per le prime chiamate ricorsive per non avere più thread in esecuzione di quelli che hai. –
Per il parallelismo veloce si desidera utilizzare 'par', non' forkIO'. Vedi il pacchetto 'parallelo' [qui] (http://hackage.haskell.org/package/parallel-3.2.0.3) per maggiori dettagli. –
@GabrielGonzalez fa 'par' funziona bene con calcoli che sono" solo "operazioni IO? Inoltre, è necessario comprendere il modulo Control.Parallel.Strategies? – Simon