2013-07-24 7 views
9

Das ProblemHaskell Leistung, wenn Klassen und Instanzen mit

ich in Haskell mehrwertig Ausgabefunktionen simuliert werden soll. Die Haskell-Code erzeugt (nicht handschriftlich) - dies ist eine wichtige Information, siehe unten:

Dies kann easly durch Rückgabe eines Tupels von Funktion erfolgt natürlich sein, wie

f x y = (x+y, x-y) 

Aber wenn eine solche Funktion ich muss wissen, welche Art von Tupel es zurückgibt:

... 
(out_f_1, out_f_2)   = f a b 
(out_g_1, out_g_2, out_g_3) = g out_f_1 
... 

Und so weiter ... Aber während Code zu erzeugen, ich weiß nicht, was die Art der ouput ist von f können sagen, so jetzt ich Verwenden Sie das Data.List.Select Paket und simulieren Sie das obige mit:

import Data.List.Select 
... 
out_f = f a b 
out_g = g (sel1 outf) 
... 

Das Problem ist die Leistung - auf meinem Testprogramm, die Version, die Data.List.Select verwendet, ist zweimal langsamer als die Version, die von Hand geschrieben.

Dies ist sehr offensichtlich Situation, weil Data.List.Select geschrieben mit classes und instances, so verwendet es eine Art von Laufzeit-Wörterbuch (Wenn ich nicht falsch liege). (http://hackage.haskell.org/packages/archive/tuple/0.2.0.1/doc/html/src/Data-Tuple-Select.html#sel1)

Die Frage

Ich möchte Sie fragen, ob es möglich ist, irgendwie die Version zu kompilieren (die Data.List.Select verwendet) so schnell zu sein wie die von Hand gefertigt ein?

Ich denke es sollte einen Wechsel zum Compiler geben, der ihm sagt, dass er die Klassen und Interfaces für jede Verwendung "instanziieren" soll (so etwas wie Vorlagen aus C++).

Benchmarks

Test1.hs:

import qualified Data.Vector as V 
import System.Environment 
b :: Int -> Int 
b x = x + 5 
c x = b x + 1 
d x = b x - 1 
a x = c x + d x 
main = do 
    putStrLn "Starting..." 
    args <- getArgs 
    let iternum = read (head args) :: Int in do 
     putStrLn $ show $ V.foldl' (+) 0 $ V.map (\i -> a (iternum-i)) 
     $ V.enumFromTo 1 iternum 
     putStrLn "Done." 

kompilieren mit ghc -O3 Test1.hs

Test2.hs:

import qualified Data.Vector as V 
import Data.Tuple.Select 
import Data.Tuple.OneTuple 

import System.Environment 
b x = OneTuple $ x + 5 
c x = OneTuple $ (sel1 $ b x) + 1 
d x = OneTuple $ (sel1 $ b x) - 1 
a x = OneTuple $ (sel1 $ c x) + (sel1 $ d x) 
main = do 
    putStrLn "Starting..." 
    args <- getArgs 
    let iternum = read (head args) :: Int in do 
     putStrLn $ show $ V.foldl' (+) 0 $ V.map (\i -> sel1 $ a (iternum-i)) 
     $ V.enumFromTo 1 iternum 
     putStrLn "Done." 

kompilieren mit ghc -O3 Test2.hs

Ergebnisse

time ./Test1 10000000 = 5.54 s 
time ./Test2 10000000 = 10.06 s 
+0

Die beiden Benchmarks führen das gleiche für mich. In beiden Fällen erzeugen sie tailrekursive Schleifen, die mit ungetasteten ganzen Zahlen arbeiten. Eine Möglichkeit für den wahrgenommenen Unterschied in der Leistung besteht darin, dass Ihr zweiter Benchmark stärker von der zusätzlichen Zeigerumlenkung betroffen ist, da alles in "OneTuple" verpackt wird, da GHC in diesem Fall leicht die Typklassenwörterbücher verlassen könnte. – sabauma

+0

@sabauma - ok, ich habe es! Meine Tests wurden nicht mit dem '-O3'-Flag kompiliert (weil ich kompiliert habe ohne' -force-rekompilieren '), also hat GHC solche Optimierungen nicht gemacht. Können Sie mir bitte sagen, warum wir 'specialize pragma' verwenden sollten, wenn der Compiler solche Ausdrücke so optimieren kann? –

+3

Dies ist ein ziemlich einfaches Beispiel für den Compiler zu optimieren. GHC kann in diesem Fall die Typclass-Lookups unterstützen, da es in der Lage ist, die polymorphen Aufrufe zu inlinern und zu bestimmen, welche Typklasseninstanz zur Kompilierzeit verwendet werden soll. Sie können (oder wollen) nicht immer eine Inline-Large-Funktion verwenden, in diesem Fall ist es für GHC immer noch vorteilhaft, eine spezielle Version der Funktion zu erzeugen. – sabauma

Antwort

0

Ok, um zu versuchen, sind die Ergebnisse, die ich geschrieben habe nicht genau - die beiden - wie @sabauma gesagt Codes führen in der gleichen Zeit Wenn Sie sie mit aktivierten Optimierungen kompilieren.

Die @ tohava-Antwort ist sehr gut, wenn Sie explizit zeigen möchten, welche Funktionen zu spezialisieren sind (siehe @sabauma-Kommentar oben).

Verwandte Themen