2010-09-04 11 views
6

Estoy tratando de escribir un pequeño script que analiza y ejecuta el código Brainfuck, para entender las opciones de optimización de GHC, estoy tratando de optimizar el código con el fin de ser un poco más rápido y entender lo que está pasando allí.¿De qué cosas debería tener cuidado si estoy usando unboxed type (como Int #) en Haskell/GHC?

En una de las partes es la represantation interna de BF-código, utilice un tipo de datos especial para esto. Aquí está el código fuente, incluidas las dos funciones que están haciendo las conversiones:

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 []         = [] 

La idea es que el código se puede leer en alguna entrada (cadena), preparsed y simplifica el código anterior y luego ejecutado por algunos otras funciones. (Se supone que la entrada es válida).

el fin de optimizar este ejemplo, he tratado de desempacar el Int params de los MovePointer y AdjustValue constructores haciendo domething así:

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

Esto a su vez la caja Int tipo en un sin embalaje, crudo Int# tipo, que es un detalle de implementación de GHc. A medida que leo, esta opción solo es buena en algunos casos, por lo que quiero preguntar de qué cosas tengo que prestar atención si deseo realizar este tipo de optimización. Mi objetivo es permitir la ejecución del código BF utilizando los beneficios de Haskell: la pereza (deseo archivar, que el código solo se puede guardar según lo necesitado en la memoria) y la facilidad.

+0

Sólo me preguntaba cuántas personas van a etiquetar esta ofensiva, a pesar de que brainf ** k es un lenguaje real ... – Oded

Respuesta

2

Esto realmente se parece a una optimización prematura a mí. DESEMPAQUE es principalmente útil cuando tienes un gran número de BFInstruction s sentados. Dudo que alguna vez tengas suficiente código de cerebro ** para que valga la pena. Estoy de acuerdo con Gian, debería ser lo suficientemente simple como para probar, así que hazlo primero.

En cualquier caso, lo que hay que tener en cuenta los valores UNPACK'ed es que usted no quiere que el compilador tiene que Rebox ellos. Deberías estar bien con operaciones numéricas, pero aparte de eso, deberías mirar cuidadosamente las funciones que estás usando para ver si alguna vez usas los valores desempacados como un argumento no estricto. La única forma de estar seguro es mirando el núcleo, o los archivos de la interfaz, para ver qué parámetros son estrictos y cuáles no. Como siempre, asegúrese de compilar al menos "-O".

3

¿Es esto realmente necesario? ¿Encuentra problemas de rendimiento con este código que cree que son el resultado de valores encuadrados? Si no, no te molestes.

Si cree que esto es el caso, entonces this page in the GHC manual parece ofrecer las restricciones necesarias en un formato de lista conveniente.

Los puntos principales parecen ser que cualquier tipo de interacción entre funciones polimórficas o nombres y tipos no compartidos que no sea rechazada por el compilador aún podría provocar filtraciones de espacio desagradables. Además, sin intentarlo, sospecho que no obtendrás excepciones en el caso de un desbordamiento, por ejemplo, así que presumiblemente deberías detectar este tipo de cosas tú mismo. Una prueba simple podría verificar si este es realmente el caso o no.

+1

1. Si necesito valores no compartidos, es porque tengo muchos de ellos. Y si tengo muchos de ellos, estarán en un vector. Y luego usaré Data.Vector.Unboxed. – jrockway

Cuestiones relacionadas