Wenn ich eine Funktion von ihrem Einsatzort in ein separates Modul verschiebe, habe ich bemerkt, dass die Leistung des Programms deutlich sinkt.
%Vor%in einem anderen Modul:
%Vor% Dies wird in 0,1 Sekunden ausgeführt, wenn fromDigits
im selben Modul ist, aber 0,4 Sekunden, wenn ich es in ein anderes Modul verschiebe.
Ich gehe davon aus, dass GHC die Funktion nicht inline einbinden kann, wenn sie sich in einem anderen Modul befindet, aber ich habe das Gefühl, sollte es können, da sie im selben Paket sind.
Ich bin nicht sicher, was die Compiler-Einstellungen sind, aber es ist mit Leksah / cabal defaults gebaut. Ich bin ziemlich sicher, dass das mit -O2 als Minimum ist.
Für die polymorphe Typklasse fromDigits
erhalten Sie eine Funktion, die aufgrund der Dictionary-Lookups für (+)
, (*)
und fromInteger
zu groß ist, um ihre Entfaltung automatisch verfügbar zu machen. Das bedeutet, dass es nicht auf die Call-Sites spezialisiert werden kann und die Dictionary-Lookups nicht eliminiert werden können, möglicherweise inline Addition und Multiplikation (was weitere Optimierung ermöglichen könnte).
Wenn GHC in demselben Modul definiert ist, in dem es mit Optimierungen verwendet wird, erstellt GHC eine spezialisierte Version für den Typ, in dem es verwendet wird, falls bekannt. Dann können die Dictionary-Lookups eliminiert werden, und die Operationen (+)
und (*)
können inline ausgeführt werden (wenn der Typ, mit dem sie verwendet werden, Operationen enthält, die für Inlining geeignet sind).
Aber das hängt davon ab, welcher Typ bekannt ist. Wenn Sie also die polymorphen calc
und fromDigits
in einem Modul haben, aber nur in einem anderen Modul, sind Sie wieder in der Position, dass nur die generische Version verfügbar ist, aber da seine Entfaltung nicht verfügbar ist, kann es nicht auf die Anrufseite spezialisiert oder anderweitig optimiert sein.
Eine Lösung besteht darin, die Entfaltung der Funktion in der Interface-Datei offenzulegen, damit sie dort, wo die notwendigen Daten (insbesondere der Typ) zur Verfügung stehen, richtig optimiert werden kann. Sie können die Entfaltung der Funktion in der Schnittstellendatei anzeigen, indem Sie ein {-# INLINE #-}
oder, ab GHC 7, ein {-# INLINABLE #-}
Pragma für die Funktion hinzufügen. Das macht den fast unveränderten Quellcode verfügbar, wenn der aufrufende Code kompiliert wird, so dass die Funktion richtig mit mehr verfügbaren Informationen optimiert werden kann.
Der Nachteil ist Code-Bloat, Sie erhalten eine Kopie des optimierten Codes an jeder Call-Site (für INLINABLE
ist es nicht so extrem, Sie erhalten mindestens eine Kopie pro aufrufendem Modul, das ist normalerweise nicht so schlecht) .
Eine alternative Lösung besteht darin, spezialisierte Versionen im definierenden Modul zu erzeugen, indem {-# SPECIALISE #-}
pragmas (US-Rechtschreibprüfung auch akzeptiert) hinzugefügt wird, damit GHC optimierte Versionen für die wichtigen Typen erstellen kann ( Int
, Integer
, Word
, ?). Dadurch werden auch Rewrite-Regeln erstellt, so dass die Verwendung der specialized-for-Typen neu geschrieben wird, um die spezialisierte Version zu verwenden (beim Kompilieren mit Optimierungen).
Der Nachteil ist, dass einige Optimierungen, die möglich wären, wenn der Code inline ist, nicht sind.
Tags und Links haskell compiler-optimization