2014-08-29 4 views
8

Nehmen wir an, ich übergebe eine kleine Funktion f an map. Kann Haskell inline f mit map eine kleine Imperativ-Schleife erzeugen? Wenn ja, wie verfolgt Haskell, welche Funktion f wirklich ist? Kann man das mit Arrow-Kombinatoren machen?Kann Haskell Inline-Funktionen als Argument übergeben werden?

+0

Inlining ist ein wirklich einfacher syntaktischer Mechanismus (iterative Ersetzung von linken Seiten durch rechte Seiten von Funktionen), es gibt kein "Bleib 'was" f "ist. Sicher, wenn Sie irgendwo in Ihrem Code "map f" haben, dann kann 'map' an dieser Callsite inline sein, an welcher Stelle auch' f' sein könnte. – jberryman

Antwort

12

Wenn f als Argument übergeben wird, dann nein, wahrscheinlich nicht. Wenn f der Name einer Top-Level-Funktion oder einer lokalen Funktion ist, dann wahrscheinlich ja.

foobar f = ... map f ... 
-- Probably not inlined. 

foobar = ... map (\ x -> ...) ... 
-- Probably inlined. 

Das heißt, entnehme ich, dass die meisten der Performance-Unterschied zwischen Inline- und aus der Linie nicht von der tatsächlichen inlining kommt selbst, sondern von irgendwelchen zusätzlichen nachfolgenden Optimierungen dies ermöglichen könnten.

Die einzige Möglichkeit, über diese Dinge "sicher" zu sein, besteht darin, den Code tatsächlich zu schreiben, ihn tatsächlich zu kompilieren und sich den Core anzusehen, der generiert wird. Und der einzige Weg zu wissen, ob es einen Unterschied macht (positiv oder negativ), ist das Benchmarking der Sache.

+2

Im ersten Fall könnte 'foobar' selbst leicht inline sein, so dass' f' auch inline sein kann. –

+0

Hängt davon ab, wie viel Zeug 'foobar' zusätzlich zum Aufruf' f' — tut, aber ja, es ist sicherlich möglich, dass es inline wird. Der einzige Weg, es wirklich zu wissen, ist zu schauen. – MathematicalOrchid

10

Die Definition der Haskell-Sprache schreibt keine Haskell-Implementierung für Inline-Code vor, oder irgendeine Art von Optimierung durchzuführen. Jede Implementierung ist frei, jede Optimierung anzuwenden, die sie für angemessen hält.

Haskell wird heutzutage oft mit GHC ausgeführt, was den Haskell-Code optimiert. Beim Inlining verwendet GHC einige Heuristiken, um zu entscheiden, ob etwas inline sein soll oder nicht. Der allgemeine Hinweis lautet, die Optimierung mit -O2 einzuschalten und die Ausgabe des Compilers zu überprüfen. Sie können den produzierten Core mit -ddump-simpl oder den Assembly-Code mit -ddump-asm sehen. Einige andere flags können auch nützlich sein.

Wenn Sie dann sehen, dass GHC keine Dinge inliniert, die Sie gerne hätten, können Sie dem Compiler einen Hinweis mit {-# INLINE foo #-} und related pragmas geben.

Seien Sie vorsichtig, wenn Sie gedankenlos Optimierungen anwenden. Häufig verbringen Programmierer ihre Zeit damit, Teile des Programms zu optimieren, die einen geringen Einfluss auf die Gesamtleistung haben. Um dies zu vermeiden, wird dringend empfohlen, zuerst den Code profile einzugeben, damit Sie wissen, wo Ihr Programm viel Zeit verbringt. Hier

+1

Dies ist ein nützlicher Rat, der jedoch die Frage nicht wirklich beantwortet. –

+0

Das Tolle an der Arbeit mit Bibliotheken ist, dass ich normalerweise keine Möglichkeit habe, zu wissen, welche Funktionen meine Benutzer an leistungssensiblen Orten verwenden, so dass ich sicher Zeit damit verbringen kann, (fast) alles zu optimieren. – dfeuer

1

ist ein Beispiel, in dem GHC tut Inline eine Funktion als Argument übergeben:

import qualified Data.Vector.Unboxed as U 
import qualified Data.Vector as V 

plus :: Int -> Int -> Int 
plus = (+) 

sumVect :: V.Vector Int -> Int 
sumVect = V.foldl1 plus 

plus wird als Argument foldl1 übergeben, die in Summieren einen Vektor von ganzen Zahlen ergibt.

letrec { 
      $s$wfoldlM_loop_s759 
      :: GHC.Prim.Int# -> GHC.Prim.Int# -> GHC.Prim.Int# 
      $s$wfoldlM_loop_s759 = 
      \ (sc_s758 :: GHC.Prim.Int#) (sc1_s757 :: GHC.Prim.Int#) -> 
       case GHC.Prim.tagToEnum# @ Bool (GHC.Prim.>=# sc_s758 ww1_s748) 
       of _ { 
       False -> 
        case GHC.Prim.indexArray# 
         @ Int ww2_s749 (GHC.Prim.+# ww_s747 sc_s758) 
        of _ { (# ipv1_X72o #) -> 
        case ipv1_X72o of _ { GHC.Types.I# y_a5Kg -> 
        $s$wfoldlM_loop_s759 
        (GHC.Prim.+# sc_s758 1#) (GHC.Prim.+# sc1_s757 y_a5Kg) 
        } 
        }; 
       True -> sc1_s757 
       }; } 

Das ist die GHC.Prim.+# sc1_s757 y_a5Kg: Im Kern ist plus zum unboxed GHC.Prim.+# :: Int# -> Int# -> Int# inlined und optimiert. Sie können einfache artihmetic Inside-Funktion plus hinzufügen und diesen Core-Ausdruck erweitern.

Verwandte Themen