2013-01-07 7 views
6

Ho realizzato un piccolo modulo C per migliorare le prestazioni, ma GHC non esegue funzioni estranee inline e il costo delle chiamate elimina l'accelerazione. Ad esempio, test.h:Come forzare GHC alle chiamate FFI in linea?

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 } 

Fare:

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

Infine, nel Main.as ho callq <inc> istruzioni invece di desiderabili inc 's.

+3

Si aspetta che ghc integri una funzione C nel suo codice generato? Questo potrebbe funzionare se si usa l'opzione -via-C, altrimenti è senza speranza (dal momento che richiederebbe ghc per leggere il codice C e generare codice per esso). – augustss

+2

Non possibile in assenza di ottimizzazione del tempo di collegamento. Un approccio (hacky) da provare è compilare sia il bitcode Haskell che C-LLVM, combinare i file .bc con 'llvm-link', ottimizzare con' opt' e quindi emettere il codice eseguibile con 'llc'. –

+0

@MikhailGlushenkov, potresti scrivere uno schizzo per creare una sequenza di comandi? Non sono riuscito a scoprire come ottenere i file '.bc' dal codice haskell. – leventov

risposta

9

GHC non incorpora il codice C tramite il suo back-end asm o back-end LLVM. Solitamente, chiamerai in C solo per motivi di prestazioni se la cosa che stai chiamando costa davvero molto. Incrementare un int non è una cosa del genere, dato che abbiamo già dei prim per questo.

Ora, se si chiama via C si può ottenere GCC inline cose (controllare l'assembly generato).

Ora, però, ci sono alcune cose che puoi fare già per minimizzare il costo della chiamata:

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

inc = fromIntegral . c_inc . fromIntegral 

forniscono una firma tipo per inc. Stai pagando cicli preziosi convertendo in intero qui.

Segna la chiamata come "non sicuro", come si fa, in modo che il runtime non è segnalibri alle prima della chiamata.

Misurare l'overhead di chiamata FFI - dovrebbe essere nei nanosecondi. Tuttavia, se lo trovi ancora troppo costoso, è possibile write a new primop and jump to it directly. Ma è meglio avere i numeri criterion prima.

+0

In realtà il mio "inc" è l'insieme delle funzioni minime-max SSE senza diramazione: https://gist.github.com/4476908 – leventov

+0

Ah, vedo - allora davvero vuoi i primopodi nuovi. Stai duplicando qualcosa su http://hackage.haskell.org/trac/ghc/ticket/3557? –

+0

Generalmente no, ma forse queste istruzioni min-max sono particolarmente considerate nel biglietto, non l'ho studiato nei dettagli. – leventov