2013-06-08 7 views
5

E 'possibile mantenere la fusione flusso durante l'elaborazione di un vector se unsafeUpdate_ funzione viene utilizzata per aggiornare alcuni elementi di una vector? La risposta sembra non essere nel test che ho fatto. Per il codice qui sotto, vettore temporaneo viene generata in upd funzione, come confermato nel nucleo:Nessun flusso di fusione con unsafeUpdate_ in unboxed vettore

module Main where 
import Data.Vector.Unboxed as U 

upd :: Vector Int -> Vector Int 
upd v = U.unsafeUpdate_ v (U.fromList [0]) (U.fromList [2]) 

sum :: Vector Int -> Int 
sum = U.sum . upd 

main = print $ Main.sum $ U.fromList [1..3] 

Nel nucleo, $wupd funzione viene utilizzata sum - come visto sotto, genera nuovo bytearray:

$wupd :: Vector Int -> Vector Int 
$wupd = 
    \ (w :: Vector Int) -> 
    case w `cast` ... of _ { Vector ipv ipv1 ipv2 -> 
    case main11 `cast` ... of _ { Vector ipv3 ipv4 ipv5 -> 
    case main7 `cast` ... of _ { Vector ipv6 ipv7 ipv8 -> 
    runSTRep 
     (\ (@ s) (s :: State# s) -> 
     case >=# ipv1 0 of _ { 
      False -> case main6 ipv1 of wild { }; 
      True -> 
      case newByteArray# (*# ipv1 8) (s `cast` ...) 
      of _ { (# ipv9, ipv10 #) -> 
      case (copyByteArray# ipv2 (*# ipv 8) ipv10 0 (*# ipv1 8) ipv9) 
        `cast` ... 

C'è un bel loop stretto nel core per la funzione sum ma poco prima di quel ciclo, c'è una chiamata alla funzione $wupd, e quindi una generazione temporanea.

C'è un modo per evitare la generazione temporanea nell'esempio qui? Il modo in cui ci penso, aggiornando un vettore nell'indice i è il caso di analizzare un flusso ma agendo solo sullo stream nell'indice i (saltando il resto) e sostituendo l'elemento con un altro elemento. Quindi, aggiornare un vettore in una posizione arbitraria non dovrebbe interrompere la fusione del flusso, giusto?

+0

parte: 'yarr' supporta fusion e srotolando insieme sul posto che cambia: http: //hackage.haskell .org/packages/archive/yarr/1.3.1/doc/html/Data-Yarr-Repr-Foreign.html # t: F – leventov

+0

@leventov, molto bello! Dal momento che sembra che tu sia l'autore di quella libreria, saresti in grado di condividere come scrivere la funzione di aggiornamento fusibile sul posto per l'esempio vettoriale sopra? Sicuramente, quello che hai fatto per yarr dovrebbe essere applicabile qui, no? – Sal

+0

grande, grande svantaggio di 'yarr-1. *' È strettamente connesso con IO. La modifica sul posto è semplicemente 'do {... write arr 0 2; ...} '. – leventov

risposta

5

Non posso essere sicuro al 100%, perché con vector le tartarughe sono completamente discese (non si raggiunge mai realmente l'effettiva implementazione, c'è sempre un'altra indiretta), ma per quanto ho capito, le varianti update impongono un nuova temporanea attraverso la clonazione:

unsafeUpdate_ :: (Vector v a, Vector v Int) => v a -> v Int -> v a -> v a 
{-# INLINE unsafeUpdate_ #-} 
unsafeUpdate_ v is w 
    = unsafeUpdate_stream v (Stream.zipWith (,) (stream is) (stream w)) 

unsafeUpdate_stream :: Vector v a => v a -> Stream (Int,a) -> v a 
{-# INLINE unsafeUpdate_stream #-} 
unsafeUpdate_stream = modifyWithStream M.unsafeUpdate 

e modifyWithStream chiamate clone (e new),

modifyWithStream :: Vector v a 
       => (forall s. Mutable v s a -> Stream b -> ST s()) 
       -> v a -> Stream b -> v a 
{-# INLINE modifyWithStream #-} 
modifyWithStream p v s = new (New.modifyWithStream p (clone v) s) 

new :: Vector v a => New v a -> v a 
{-# INLINE_STREAM new #-} 
new m = m `seq` runST (unsafeFreeze =<< New.run m) 

-- | Convert a vector to an initialiser which, when run, produces a copy of 
-- the vector. 
clone :: Vector v a => v a -> New v a 
{-# INLINE_STREAM clone #-} 
clone v = v `seq` New.create (
    do 
    mv <- M.new (length v) 
    unsafeCopy mv v 
    return mv) 

e vedo nessun modo che vector sarebbe sbarazzarsi di quello unsafeCopy di nuovo.

+0

in base a questo [carta da riciclo vettoriale] (http://www.cse.unsw.edu.au/~rl/publications/recycling.pdf), si suppone che clone/nuovo si fondano insieme anche se non lo vedo i miei esperimenti – Sal

1

Se è necessario modificare uno o pochissimi elementi, esistono delle belle soluzioni nelle librerie repa e yarr. Conservano la fusione (non sono sicuro di repa) e Haskell-idiomatico.

Repa, utilizzando fromFunction:

upd arr = fromFunction (extent arr) ix 
    where ix (Z .: 0) = 2 
     ix i = index arr i 

Yarr, utilizzando Delayed:

upd arr = Delayed (extent arr) (touchArray arr) (force arr) ix 
    where ix 0 = return 2 
     ix i = index arr i