2013-01-07 10 views
6

Zrobiłem mały moduł C, aby poprawić wydajność, ale GHC nie inline funkcji inline, a koszty połączeń eliminuje przyspieszenie. Przykładowo test.h:Jak wymusić GHC na wywoływanych wywołaniach FFI?

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 

Wreszcie w Main.as mam callq <inc> instrukcji zamiast pożądanych inc wieku.

+3

Oczekujesz, że ghc wstawi funkcję C w wygenerowanym kodzie? Może to zadziałać, jeśli użyjesz opcji -via-C, w przeciwnym razie jest to beznadziejne (ponieważ wymagałoby ghc do odczytania kodu C i wygenerowania dla niego kodu). – augustss

+2

Nie możliwe przy braku optymalizacji czasu łącza. Jednym (hackowskim) podejściem jest próba skompilowania kodu Haskell i C do LLVM, połączenie plików .bc z 'llvm-link', optymalizacja za pomocą' opt', a następnie wyemitowanie kodu wykonywalnego z 'llc'. –

+0

@MikhailGlushenkov, czy mógłbyś napisać szkic wykonywania sekwencji poleceń? Nie udało mi się znaleźć w Google sposobu uzyskania plików '.bc' z kodu haskell. – leventov

Odpowiedz

9

GHC nie będzie wstawiał kodu C poprzez jego backend asm lub backend LLVM. Zazwyczaj będziesz wywoływał C tylko ze względu na wydajność, jeśli to, co dzwonisz, naprawdę kosztuje dużo. Inkrementacja int nie jest czymś, co mamy już za sobą.

Teraz, jeśli zadzwonisz przez C, możesz uzyskać GCC do wstawiania rzeczy (sprawdź wygenerowany zespół).

Teraz jednak istnieje kilka rzeczy, które możesz zrobić już, aby zminimalizować koszt połączenia:

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

inc = fromIntegral . c_inc . fromIntegral 

Podaj rodzaj podpisu dla inc. Płacisz cenne cykle, przeliczając tutaj na Integer.

Oznacz rozmowę jako "niebezpieczną", tak jak robisz, aby środowisko wykonawcze nie zostało zakodowane przed wywołaniem.

Zmierz obciążenie połączenia FFI - powinno być w nanosekundach. Jeśli jednak uznasz to za zbyt drogie, możesz write a new primop and jump to it directly. Ale lepiej najpierw mieć numery criterion.

+0

W rzeczywistości moje "inc" jest zestawem funkcji bezgałęziowych SSE min-max: https://gist.github.com/4476908 – leventov

+0

Ah Widzę - naprawdę chcesz nowych primopsów. Powielacie coś z http://hackage.haskell.org/trac/ghc/ticket/3557? –

+0

Generalnie nie, ale może te instrukcje min-max są szczególnie uważane na bilecie, nie studiowałem go szczegółowo. – leventov