2016-06-13 14 views
6

non capisco perché questo programma utilizzando repa:Cosa c'è di sbagliato nell'uso di Identity monad con mmultP quando si usa repa?

import Data.Array.Repa 
import Data.Array.Repa.Algorithms.Matrix 
import Data.Functor.Identity 

go = runIdentity $ do 
    let mat = fromListUnboxed (ix2 2 2) [1..4] 
    let ins = fromListUnboxed (ix2 2 1) [1, 1] 
    mmultP mat ins 

mi sta dando il seguente avviso:

Data.Array.Repa: Performing nested parallel computation sequentially. 
    You've probably called the 'compute' or 'copy' function while another 
    instance was already running. This can happen if the second version 
    was suspended due to lazy evaluation. Use 'deepSeqArray' to ensure 
    that each array is fully evaluated before you 'compute' the next one. 

Non ho calcoli nidificati, non ho chiamato compute o copy, e tutto che ho usato per fare il calcolo è all'interno della stessa monade. Ha qualcosa a che fare con la valutazione pigra? In tal caso, come faccio a eseguire il calcolo parallelo mentre utilizzo la monade Identity (per mantenere puro il calcolo generale)?

Per riferimento, la sostituzione di runIdentity con runST lo fa funzionare, anche se in entrambi i casi la funzionalità del monad specifica non viene utilizzata affatto.

+0

Guardando all'interno del codice sorgente, vedo che 'mmultP' chiama' computeP', che chiama 'unsafePerformIO'. Non ne ho molta esperienza, ma potrebbe esserci un'incompatibilità tra 'unsafePerformIO' e la monade 'Identity' (forse il comportamento di' Identity' con la pigrizia)? – madjar

+0

@madjar Non ne ho idea. Non vedo come. – rityzmon

risposta

2

La ragione per cui il vincolo Monad in computeP e operazioni parallele simili è forzare il calcolo sequenziale dove richiesto. Questo è descritto in [Programmazione parallela e simultanea in Haskell], nella sottosezione Monads e computeP.

Nel tuo caso, il problema sembra essere causato da l'implementazione interna di mmultP:

mmultP :: Monad m 
     => Array U DIM2 Double 
     -> Array U DIM2 Double 
     -> m (Array U DIM2 Double) 

mmultP arr brr 
= [arr, brr] `deepSeqArrays` 
    do trr  <- transpose2P brr 
     let (Z :. h1 :. _) = extent arr 
     let (Z :. _ :. w2) = extent brr 
     computeP 
     $ fromFunction (Z :. h1 :. w2) 
     $ \ix -> R.sumAllS 
        $ R.zipWith (*) 
         (unsafeSlice arr (Any :. (row ix) :. All)) 
         (unsafeSlice trr (Any :. (col ix) :. All)) 

Si chiama prima transpose2P e poi computeP, e transpose2P chiama internamente computeUnboxedP. Se si utilizza la monade Identity, non è forzata la sequenza, quindi entrambi questi calcoli paralleli possono essere eseguiti in parallelo, quindi il parallelismo nidificato.

Se si desidera mantenere le cose pure e anche non si vuole utilizzare ST, è possibile sostituire con IdentityEval, che è una versione rigorosa di Identity:

import Control.Parallel.Strategies 
... 
go = runEval $ do ... 
+0

Grazie. La mia confusione con questa radicata nella monade 'Identity' non ha forzato il sequenziamento. Perché? Non è una monade che usa '>> =' per fare una sequenza? – rityzmon

+0

Le Monade descrivono i calcoli, ma in generale non come vengono valutati. Per alcune monadi l'ordine viene applicato ('IO',' ST', 'Eval', ...), ma per alcune monade non (' Reader', 'Identity', ...). La valutazione pigra di Haskell si applica ancora, quindi se l'ordine non è forzato, i calcoli sono ancora valutati pigramente. In particolare, 'Identity' è una monade che non fa nulla, fornisce solo un'interfaccia monadica per i calcoli puri. –

Problemi correlati