ANNOUNCE: cinvoke 0.1 released
I am happy to finally announce cinvoke 0.1, a binding to the C library cinvoke[1], allowing functions to be loaded and called whose names and types are not known before run-time. Why? Sometimes you can't use the Haskell foreign function interface because you parse the type of the function from somewhere else, i.e. you're writing an interpreter for a language that has an FFI itself. What? The main function it exports is: cinvoke :: Symbol -> RetType b -> [Arg] -> IO b And because code is worth a thousand words, here's a small program that uses libc to write a 1Gb buffer of random garbage to a file:
module Main where
import Foreign.CInvoke
main = do cxt <- newContext libc <- loadLibrary cxt "libc.so.6" malloc <- loadSymbol libc "malloc" creat <- loadSymbol libc "creat" write <- loadSymbol libc "write" free <- loadSymbol libc "free" let sz = 2^30 buf <- cinvoke malloc (retPtr retVoid) [argCSize sz] fd <- cinvoke creat retCInt [argString "/tmp/test", argCUInt 0o644] n <- cinvoke write retCSize [argCInt fd, argPtr buf, argCSize sz] cinvoke free (retPtr retVoid) [argPtr buf]
It hopefully works on any machine on which cinvoke works, but has only been tested on linux x86_64. As the current version of cinvoke only installs a static library, it does not work from GHCi at the moment (without hacking cinvoke to build a shared library). More interesting examples are included in examples/ in the package. Where? Hackage: http://hackage.haskell.org/package/cinvoke Cheers, Remi [1] http://www.nongnu.org/cinvoke/
On Sun, Mar 6, 2011 at 2:38 PM, Remi Turk <rturk@science.uva.nl> wrote:
I am happy to finally announce cinvoke 0.1, a binding to the C library cinvoke[1], allowing functions to be loaded and called whose names and types are not known before run-time.
Why?
Sometimes you can't use the Haskell foreign function interface because you parse the type of the function from somewhere else, i.e. you're writing an interpreter for a language that has an FFI itself.
What?
The main function it exports is:
cinvoke :: Symbol -> RetType b -> [Arg] -> IO b
And because code is worth a thousand words, here's a small program that uses libc to write a 1Gb buffer of random garbage to a file:
module Main where
import Foreign.CInvoke
main = do cxt <- newContext libc <- loadLibrary cxt "libc.so.6" malloc <- loadSymbol libc "malloc" creat <- loadSymbol libc "creat" write <- loadSymbol libc "write" free <- loadSymbol libc "free" let sz = 2^30 buf <- cinvoke malloc (retPtr retVoid) [argCSize sz] fd <- cinvoke creat retCInt [argString "/tmp/test", argCUInt 0o644] n <- cinvoke write retCSize [argCInt fd, argPtr buf, argCSize sz] cinvoke free (retPtr retVoid) [argPtr buf]
It hopefully works on any machine on which cinvoke works, but has only been tested on linux x86_64. As the current version of cinvoke only installs a static library, it does not work from GHCi at the moment (without hacking cinvoke to build a shared library). More interesting examples are included in examples/ in the package.
Where? Hackage: http://hackage.haskell.org/package/cinvoke
Cheers, Remi
Is there any information on how this (and libffi I guess) compare to GHC's FFI in terms of performance? Is it equivalent? Once you've loaded a function with loadSymbol and are cinvoking it with various arguments, versus a plain "foreign import" of the same. (Also, I assume cinvoke corresponds to the FFI's 'unsafe' calls, i.e. if the function tries to call back into the GHC runtime then Bad Things will happen, and it'll block threads on the same 'Capability' if it runs too long?)
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
-- Work is punishment for failing to procrastinate effectively.
On Tue, Mar 08, 2011 at 01:01:58PM +0100, Gábor Lehel wrote:
On Sun, Mar 6, 2011 at 2:38 PM, Remi Turk <rturk@science.uva.nl> wrote:
Where? Hackage: http://hackage.haskell.org/package/cinvoke
Cheers, Remi
Is there any information on how this (and libffi I guess) compare to GHC's FFI in terms of performance? Is it equivalent? Once you've loaded a function with loadSymbol and are cinvoking it with various arguments, versus a plain "foreign import" of the same.
Count on it having at least an order of magnitude more overhead. I did some simple test of calling the following three trivial functions (with constant arguments, and ignoring the return values, 2M times) and got the following timings: int blub0() { return 42; } int blub1(int a) { return 42; } int blub5(int a, int b, int c, int d, int e) { return 42; } Unsafe FFI Safe FFI Safe dynamic FFI CInvoke blub0 0.03 0.19 0.20 1.62 blub1 0.03 0.20 0.20 2.44 blub5 0.04 0.20 0.20 4.35 It's not that bad for functions that actually (try to) do something though. For example, trying to remove a non-existent file: unlink 3.06 3.04 3.27 7.15 If I remember correctly, libffi was slightly faster, but mostly thanks to the fact that I didn't make it exception safe yet. So if you care about performance and are able to directly use the FFI, you clearly should.
(Also, I assume cinvoke corresponds to the FFI's 'unsafe' calls, i.e. if the function tries to call back into the GHC runtime then Bad Things will happen, and it'll block threads on the same 'Capability' if it runs too long?)
Actually, it doesn't: Considering the rather large overhead of CInvoke itself, I just import everything 'safe'. Though to be honest I didn't actually test any callbacks into Haskell. Cheers, Remi
On Wed, Mar 9, 2011 at 5:26 PM, Remi Turk <rturk@science.uva.nl> wrote:
On Tue, Mar 08, 2011 at 01:01:58PM +0100, Gábor Lehel wrote:
On Sun, Mar 6, 2011 at 2:38 PM, Remi Turk <rturk@science.uva.nl> wrote:
Where? Hackage: http://hackage.haskell.org/package/cinvoke
Cheers, Remi
Is there any information on how this (and libffi I guess) compare to GHC's FFI in terms of performance? Is it equivalent? Once you've loaded a function with loadSymbol and are cinvoking it with various arguments, versus a plain "foreign import" of the same.
Count on it having at least an order of magnitude more overhead. I did some simple test of calling the following three trivial functions (with constant arguments, and ignoring the return values, 2M times) and got the following timings:
int blub0() { return 42; } int blub1(int a) { return 42; } int blub5(int a, int b, int c, int d, int e) { return 42; }
Unsafe FFI Safe FFI Safe dynamic FFI CInvoke blub0 0.03 0.19 0.20 1.62 blub1 0.03 0.20 0.20 2.44 blub5 0.04 0.20 0.20 4.35
It's not that bad for functions that actually (try to) do something though. For example, trying to remove a non-existent file:
unlink 3.06 3.04 3.27 7.15
If I remember correctly, libffi was slightly faster, but mostly thanks to the fact that I didn't make it exception safe yet.
So if you care about performance and are able to directly use the FFI, you clearly should.
That describes my situation. Thanks! For the record, what units were your measurements in? (I notice that the overhead of safe FFI calls seems to be pretty smallish, which is also quite heartening.)
(Also, I assume cinvoke corresponds to the FFI's 'unsafe' calls, i.e. if the function tries to call back into the GHC runtime then Bad Things will happen, and it'll block threads on the same 'Capability' if it runs too long?)
Actually, it doesn't: Considering the rather large overhead of CInvoke itself, I just import everything 'safe'. Though to be honest I didn't actually test any callbacks into Haskell.
Oh, yeah, it makes sense that the safety of the calls cinvoke makes would be same as the safety under which cinvoke itself is imported. Didn't think of that.
Cheers, Remi
-- Work is punishment for failing to procrastinate effectively.
participants (2)
-
Gábor Lehel -
Remi Turk