Dont les choses que je dois prendre soin si je suis en utilisant le type Unboxed (comme Int #) dans Haskell / GHC?

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

Question

Je suis en train d'écrire un petit script qui analyse et exécute le code Brainfuck, pour comprendre les options d'optimisation GHC, je suis en train d'optimiser le code pour être un peu plus rapide et de comprendre ce qui se passe là-bas.

Sur des parties est le represantation interne code BF-je utiliser un type spécial pour cela. Voici le code source, inclus les deux fonctions qui font les conversions:

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'idée est que le code sera lu à partir une entrée (chaîne), preparsed et simplifiée par le code ci-dessus, puis exécuté par d'autres fonctions. (On suppose que l'entrée est valide).

Afin d'optimiser cet exemple, j'ai essayé de unbox les params Int des constructeurs de MovePointer et AdjustValue en faisant domething comme ceci:

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

Cela transforme le type de Int barricadés un type de Int# brut Unboxed, qui est un détail de mise en œuvre de GHc. Comme je l'ai lu, cette option est seulement bon dans quelques cas, donc je veux poser, dont des choses que je dois faire attention si je veux effectuer ce type d'optimisation. Mon but est de permettre l'exécution de code BF-en utilisant les avantages de Haskell - la paresse (je veux archieve, que le code ne peut être conservé au besoin en mémoire) et la facilité.

Était-ce utile?

La solution

Cela ressemble vraiment à une optimisation trop tôt pour moi. DEBALLER est surtout utile lorsque vous avez un très grand nombre de BFInstructions assis autour. Je doute que vous aurez jamais assez brainf ** code k pour qu'il vaille la peine. Je suis d'accord avec Gian, il devrait être assez simple à tester, donc le faire en premier.

Dans tous les cas, la chose à prendre en compte les valeurs UNPACK'ed est que vous ne voulez pas le compilateur d'avoir les ReBOX. Vous devriez être ok avec ops numériques, mais autres que vous auriez à examiner attentivement les fonctions que vous utilisez pour voir si vous utilisez toujours les valeurs non emballés comme un argument non stricte. La seule façon d'être sûr est de regarder le cœur ou les fichiers d'interface, pour voir quels paramètres sont strictes et qui ne sont pas. Comme toujours, assurez-vous de compiler avec au moins « O ».

Autres conseils

Est-ce vraiment nécessaire? Vous rencontrez des problèmes de performance avec ce code que vous pensez sont le résultat des valeurs en boîte? Sinon, ne vous inquiétez pas.

Si vous ne croyez que c'est le cas, alors cette page dans le manuel GHC semble fournir les restrictions nécessaires dans un format de liste pratique.

Les principaux points semblent être que tout type d'interaction entre les fonctions polymorphes ou les noms et les types unboxed qui ne sont pas rejetés par le compilateur pourrait encore conduire à des fuites d'espace désagréables. En outre, sans l'essayer, je pense que vous ne serez pas les exceptions lancées dans le cas d'un trop-plein, par exemple, on peut donc supposer que vous devriez détecter ce genre de chose vous-même. Un test simple pourrait vérifier si tel est effectivement le cas ou non.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top