How to make GHC connect to FFI?

I made a small C module to improve performance, but the GHC does not have built-in external functions, and the cost of calls eliminates acceleration. For example, test.h :

 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 } 

Manufacture:

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

Finally, in Main.as , I have callq <inc> instructions instead of the desired inc .

+6
source share
1 answer

GHC will not embed C code through the asm or LLVM backend. Typically, you are only going to call C for performance reasons, if the thing you are calling is really worth a lot. The int increment is not like that, since we already have primitives for this.

Now, if you call through C, you can get GCC for the built-in stuff (check the generated assembly).

Now, however, some things can be done already to minimize the cost of the call:

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

Provide a type signature for inc . You pay precious cycles converting to Integer here.

Mark the call as “unsafe”, just like you, so that the runtime is not bookmarked before the call.

Measure FFI overhead - it should be in nanoseconds. However, if you think this is too expensive, you can write a new primitive and go directly to it. But you better have a criterion .

+9
source

All Articles