2010-09-04 19 views
6

J'essaye d'écrire un petit script qui analyse et exécute du code Brainfuck, pour comprendre les options d'optimisation de GHC, j'essaie d'optimiser le code pour être un peu plus rapide et comprendre ce qui se passe là-bas.De quoi devrais-je faire attention si j'utilise un type non boxé (comme Int #) dans Haskell/GHC?

Sur les parties est la représentation interne du code BF, j'utilise un type de données 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 certains autres fonctions. (On suppose que l'entrée est valide).

Afin d'optimiser cet exemple, j'ai essayé de unbox Int params des constructeurs 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 Int en boîte dans une boîte ouverte, Int# brut type, qui est un détail d'implémentation de GHc. Comme je l'ai lu, cette option n'est bonne que dans quelques cas, je veux donc demander, de quelles choses je dois faire attention si je veux effectuer ce genre d'optimisation. Mon but est de permettre l'exécution du code BF en utilisant les avantages de Haskell - la paresse (je veux archiver, que le code ne peut être conservé que si nécessaire en mémoire) et la facilité.

+0

Je me demandais juste combien de personnes vont étiqueter cette offensive, même si BrainF ** k est une langue réelle ... – Oded

Répondre

2

Cela ressemble vraiment à une optimisation prématurée pour moi. UNPACK est surtout utile lorsque vous avez un très grand nombre de BFInstruction assis autour. Je doute que vous aurez jamais assez de code brainf ** k pour le rendre utile. Je suis d'accord avec Gian, il devrait être assez simple pour tester, alors faites-le d'abord. En tout cas, la chose à prendre en compte avec les valeurs UNPACK est que vous ne voulez pas que le compilateur ait à les reboxer. Vous devriez être d'accord avec les opérations numériques, mais à part cela, vous devrez examiner attentivement les fonctions que vous utilisez pour voir si vous utilisez les valeurs décompressées comme argument non strict. La seule façon d'être sûr est de regarder le noyau, ou les fichiers d'interface, pour voir quels paramètres sont stricts et lesquels ne le sont pas. Comme toujours, assurez-vous de compiler avec au moins "-O".

3

Est-ce vraiment nécessaire? Rencontrez-vous des problèmes de performances avec ce code que vous pensez être le résultat de valeurs encadrées? Si non, ne vous embêtez pas.

Si vous croyez que c'est le cas, alors this page in the GHC manual semble fournir les restrictions nécessaires dans un format de liste pratique.

Les points principaux semblent être que n'importe quel type d'interaction entre les fonctions polymorphes ou les noms et les types non-boxed qui n'est pas rejeté par le compilateur pourrait toujours mener à des fuites d'espace méchant. Aussi, sans l'essayer, je suppose que vous ne recevrez pas d'exceptions en cas de débordement, par exemple, alors vous devriez probablement détecter ce genre de chose vous-même. Un test simple pourrait vérifier si c'est effectivement le cas ou non.

+1

+1. Si j'ai besoin de valeurs sans boîte, c'est parce que j'en ai beaucoup. Et si j'en ai beaucoup, ils seront dans un vecteur. Et puis je vais juste utiliser Data.Vector.Unboxed. – jrockway