Di cui le cose dovrei fare attenzione se sto utilizzando il tipo unboxed (come Int #) in Haskell / GHC?

StackOverflow https://stackoverflow.com/questions/3641573

Domanda

Sto cercando di scrivere un piccolo script che analizza ed esegue il codice Brainfuck, per comprendere le opzioni di ottimizzazione GHC, sto cercando di ottimizzare il codice in modo da essere un po 'più veloce e per capire che cosa sta succedendo lì.

Su delle parti è la represantation interna di BF-code, io uso un tipo di dati speciale per questo. Ecco il codice sorgente, incluse le due funzioni che stanno facendo le conversioni:

data BFinstruction
  = AdjustValue Int
  | MovePointer Int
  | GetChar
  | PutChar
  | Loop BFcode
  deriving (Eq)

type BFcode = [BFinstruction]

unsafeCompileBrainfuck :: String -> BFcode
unsafeCompileBrainfuck = fst . parse [] where
  -- arguments: input string, built code; output: output code, rest of input
  parse :: BFcode -> String -> (BFcode,String)
  parse c ('+':s) = parse (AdjustValue   1 :c) s
  parse c ('-':s) = parse (AdjustValue (-1):c) s
  parse c ('>':s) = parse (MovePointer   1 :c) s
  parse c ('<':s) = parse (MovePointer (-1):c) s
  parse c ('.':s) = parse (PutChar         :c) s
  parse c (',':s) = parse (GetChar         :c) s
  parse c (']':s) = (reverse c, s)
  parse c ('[':s) = parse (Loop l          :c) s' where (l,s') = parse [] s
  parse c []      = (reverse c ,"")
  parse c ( _ :s) = parse                   c  s

simplifyBrainfuck :: BFcode -> BFcode
simplifyBrainfuck ((AdjustValue x):(AdjustValue y):zs) = if x + y /= 0
  then simplifyBrainfuck (AdjustValue (x + y):zs)
  else simplifyBrainfuck zs
simplifyBrainfuck ((MovePointer x):(MovePointer y):zs) = if x + y /= 0
  then simplifyBrainfuck (MovePointer (x + y):zs)
  else simplifyBrainfuck zs
simplifyBrainfuck (x                              :zs) = x: simplifyBrainfuck zs
simplifyBrainfuck []                                   = []

L'idea è che il codice viene letto da un input (stringa), preparsed e semplificata dal codice precedente e quindi eseguito da alcune altre funzioni. (Si presume, che l'ingresso è valido).

Per ottimizzare questo esempio, ho cercato di unboxing i params Int dei MovePointer e AdjustValue costruttori facendo domething in questo modo:

data BFinstruction -- BangPatterns
  = AdjustValue {-# UNPACK #-} !Int
  | MovePointer {-# UNPACK #-} !Int
  | GetChar
  | PutChar
  | Loop BFcode
  deriving (Eq)

Questo accende il tipo Int scatolato in un tipo Int# grezzo unboxed, che è un dettaglio di implementazione di GHC. Mentre leggevo, questa opzione è buono solo in pochi casi, quindi voglio chiedere, di cui le cose che devo fare attenzione se voglio eseguire questo tipo di ottimizzazione. Il mio obiettivo è quello di permettere l'esecuzione di BF-codice utilizzando i vantaggi di Haskell - pigrizia (voglio archieve, che il codice può essere mantenuta solo se necessario in memoria) e facilità.

È stato utile?

Soluzione

Questa sembra davvero come un'ottimizzazione prematura per me. DISIMBALLAGGIO è particolarmente utile quando si ha un numero molto elevato di BFInstructions seduti intorno. Dubito che avrete mai abbastanza Brainf codice ** k per renderlo utile. Sono d'accordo con Gian, dovrebbe essere abbastanza semplice per testare, in modo da fare che prima.

In ogni caso, la cosa di essere a conoscenza con i valori UNPACK'ed è che non si desidera che il compilatore di dover Rebox loro. Si dovrebbe essere ok con ops numerici, ma diverso da quello che ci si deve guardare con attenzione le funzioni che si sta utilizzando per vedere se mai utilizzare i valori decompressi come un argomento non rigoroso. L'unico modo per essere sicuri è quello di guardare al centro, o dei file di interfaccia, per vedere quali parametri sono rigorosi e quali no. Come sempre, assicurati di compilare con almeno "-O".

Altri suggerimenti

Questa è davvero necessario? Stai incontrando problemi di prestazioni con questo codice che tu pensi che sono il risultato dei valori in scatola? In caso contrario, non preoccupatevi.

Se non credi che questo è il caso, quindi la pagina del manuale GHC sembra offrire le restrizioni necessarie in un formato elenco conveniente.

I punti principali sembrano essere che qualsiasi tipo di interazione tra le funzioni polimorfiche o nomi ei tipi disimballati che non viene respinto dal compilatore potrebbe ancora portare a perdite di spazio brutto. Inoltre, senza provarlo, ho il sospetto che non sarà possibile ottenere le eccezioni sollevate nel caso di un overflow, ad esempio, quindi presumibilmente si dovrebbe rilevare questo genere di cose da soli. Un test semplice potrebbe verificare se questo è effettivamente il caso o meno.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top