2010-09-04 11 views
6

Ich versuche, ein kleines Skript zu schreiben, das Brainfuck-Code analysiert und ausführt, um die GHC-Optionen der Optimierung zu verstehen, ich versuche, den Code zu optimieren, um ein bisschen schneller zu sein und zu verstehen, was dort vor sich geht.Von welchen Dingen sollte ich vorsichtig sein, wenn ich in Haskell/GHC ungepackten Typ (wie Int #) verwende?

Einer der Teile ist die interne Repräsentation von BF-Code, ich verwende hierfür einen speziellen Datentyp. Hier ist der Sourcecode, enthalten die beiden Funktionen, die die Umwandlungen tun:

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

Die Idee ist, dass der Code von einem Eingang (String) gelesen werden, preparsed und durch den obigen Code vereinfacht und dann durch einige ausgeführt andere Funktionen. (Es wird angenommen, dass die Eingabe gültig ist).

Um dieses Beispiel zu optimieren, habe ich versucht, den Int params des MovePointer und AdjustValue Konstrukteurs unbox von domething wie dies zu tun:

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

Dies wird den boxed Int Typen in einen unboxed, roh Int# drehen Typ, der ein Implementierungsdetail von GHc ist. Wie ich gelesen habe, ist diese Option nur in wenigen Fällen gut, also möchte ich fragen, auf welche Dinge ich achten muss, wenn ich diese Art der Optimierung durchführen möchte. Mein Ziel ist es, die Ausführung von BF-Code mit den Vorteilen von Haskell zu ermöglichen - Faulheit (ich möchte erreichen, dass der Code nur im Speicher gehalten werden kann) und Leichtigkeit.

+0

Ich frage mich nur, wie viele Leute diese Offensive markieren, obwohl BrainF ** k eine aktuelle Sprache ist ... – Oded

Antwort

2

Das sieht wirklich wie eine vorzeitige Optimierung für mich aus. UNPACK ist meistens nützlich, wenn Sie eine sehr große Anzahl von BFInstruction s sitzen haben. Ich bezweifle, dass du jemals genug brainf ** k-Code hast, um es lohnenswert zu machen. Ich stimme Gian zu, es sollte einfach genug sein, um zu testen, also tu das zuerst.

In jedem Fall ist die Sache mit UNPACK'ed Werten bewusst, dass Sie nicht möchten, dass der Compiler sie neu einreihen muss. Sie sollten mit numerischen Ops in Ordnung sein, aber ansonsten müssten Sie sich genau die Funktionen ansehen, die Sie verwenden, um zu sehen, ob Sie jemals die entpackten Werte als nicht striktes Argument verwenden. Der einzige Weg, um sicher zu sein, ist, den Kern oder die Interface-Dateien zu sehen, um zu sehen, welche Parameter streng sind und welche nicht. Wie immer, stellen Sie sicher, dass Sie mindestens mit "-O" kompilieren.

3

Ist das wirklich notwendig? Treten Leistungsprobleme mit diesem Code auf, die Ihrer Meinung nach das Ergebnis von Boxed-Werten sind? Wenn nicht, nicht stören.

Wenn Sie glauben, dass dies der Fall ist, scheint this page in the GHC manual die notwendigen Einschränkungen in einem praktischen Listenformat zu bieten.

Die Hauptpunkte scheinen zu sein, dass jede Art von Interaktion zwischen polymorphen Funktionen oder Namen und nicht gepackten Typen, die nicht vom Compiler abgelehnt wird, immer noch zu fiesen Space Leaks führen kann. Außerdem, ohne es zu versuchen, vermute ich, dass Sie im Fall eines Überlaufs keine Ausnahmen erhalten werden, also sollten Sie diese Art von Dingen vermutlich selbst entdecken. Ein einfacher Test könnte prüfen, ob dies tatsächlich der Fall ist oder nicht.

+1

+1. Wenn ich ungeboxte Werte brauche, liegt das daran, dass ich viele davon habe. Und wenn ich viele habe, sind sie in einem Vektor. Und dann verwende ich nur Data.Vector.Unboxed. – jrockway