2013-01-07 13 views
6

Ich machte kleine C-Modul, um die Leistung zu verbessern, aber GHC nicht inline Fremdfunktionen und Anrufe Kosten eliminiert die Beschleunigung. Zum Beispiel test.h:Wie erzwinge GHC zu Inline-FFI-Aufrufe?

int inc (int x); 

test.c:

#include "test.h" 
int inc(int x) {return x + 1;} 

Test.hc:

{-# LANGUAGE ForeignFunctionInterface #-} 
module Test (inc) where 
import Foreign 
import Foreign.C 
foreign import ccall unsafe "test.h inc" c_inc :: CInt -> CInt 
inc = fromIntegral . c_inc . fromIntegral 
{-# INLINE c_inC#-} 
{-# INLINE inC#-} 

Main.hs:

import System.Environment 
import Test 
main = do {args <- getArgs; putStrLn . show . inc . read . head $ args } 

Making:

$ gcc -O2 -c test.c 
$ ghc -O3 test.o Test.hs 
$ ghc --make -O3 test.o Main 
$ objdump -d Main > Main.as 

schließlich in Main.as Ich habe callq <inc> Anweisungen statt wünschenswert inc ‚s.

+3

Sie erwarten, dass GhC eine C-Funktion in den generierten Code einbindet? Dies könnte funktionieren, wenn Sie die Option -via-C verwenden, andernfalls ist es hoffnungslos (da es ghc erfordern würde, den C-Code zu lesen und Code dafür zu generieren). – augustss

+2

Nicht möglich in Abwesenheit von Link-Zeit-Optimierung. Ein (hacky) Ansatz besteht darin, sowohl Haskell als auch C zu LLVM Bitcode zu kompilieren, die .bc Dateien mit 'llvm-link' zu kombinieren, mit' opt' zu optimieren und dann ausführbaren Code mit 'llc' auszugeben. –

+0

@MichailGlushenkov, könnten Sie eine Skizze der Befehlssequenz erstellen? Ich konnte nicht googlen, wie man '.bc' Dateien von Haskell-Code erhält. – leventov

Antwort

9

GHC wird keinen Inline-C-Code über sein ASM-Backend oder LLVM-Backend eingeben. In der Regel werden Sie nur aus Leistungsgründen in C anrufen, wenn das, was Sie anrufen, wirklich viel kostet. Das Inkrementieren eines Int ist nicht so etwas, da wir bereits Primops dafür haben.

Jetzt, wenn Sie über C anrufen, können Sie GCC zu Inline-Sachen bekommen (überprüfen Sie die generierte Baugruppe).

Nun aber gibt es einige Dinge, die Sie bereits tun können, um den Anruf Kosten zu minimieren:

foreign import ccall unsafe "test.h inc" c_inc :: CInt -> CInt 

inc = fromIntegral . c_inc . fromIntegral 

für inc eine Art Unterschrift sorgen. Du zahlst kostbare Zyklen, die hier zu Integer konvertiert werden.

Markieren Sie den Anruf als "unsicher", so dass die Laufzeit vor dem Aufruf nicht mit einem Lesezeichen versehen wird.

Messen Sie den Overhead des FFI-Anrufs - er sollte in Nanosekunden liegen. Allerdings, wenn Sie es immer noch zu teuer finden, können Sie write a new primop and jump to it directly. Aber Sie haben besser Ihre criterion Zahlen zuerst.

+0

Eigentlich ist mein "inc" der Satz von Zweig SSE Min-Max-Funktionen: https://gist.github.com/4476908 – leventov

+0

Ah ich sehe - Sie wollen wirklich neue Primops dann. Du verdoppelst etwas von http://hackage.haskell.org/trac/ghc/ticket/3557? –

+0

Im Allgemeinen nicht, aber vielleicht sind diese Min-Max-Anweisungen besonders im Ticket berücksichtigt, ich habe es nicht im Detail studiert. – leventov