My program uses GPG for signing files. I'm using GPGME in Haskell and the issue is that it is about 16 times slower than using GPG from the command line. Here is an example:
Haskell code:
module Main where
import qualified Data.ByteString as B
import qualified Crypto.Gpgme as Gpg
main :: IO ()
main = do
fileContents <- B.readFile "randomfile"
eitherSigned <- sign fileContents
case eitherSigned of
Left err -> print err
Right signed ->
B.writeFile "signedfile" signed
sign :: B.ByteString -> IO (Either [Gpg.InvalidKey] B.ByteString)
sign bs =
let
sign' :: Gpg.Ctx -> IO (Either [Gpg.InvalidKey] Gpg.Plain)
sign' ctx = Gpg.sign ctx [] Gpg.Normal bs
in
Gpg.withCtx "/home/t/.gnupg" "C" Gpg.OpenPGP sign'
I generate a junk 10MB file with
$ dd if=/dev/zero of=randomfile bs=10000000 count=1
I sign it with GPG from the command line:
$ time gpg -s randomfile
and get
gpg -s randomfile 0.14s user 0.00s system 99% cpu 0.136 total
I sign it with my Haskell program with
$ time stack exec hasksign
and get
stack exec hasksign 0.27s user 0.07s system 14% cpu 2.239 total
I tried running the Haskell code again with profiling on and got this result:
hasksign +RTS -p -RTS randomfile
total time = 2.21 secs (2208 ticks @ 1000 us, 1 processor)
total alloc = 115,627,312 bytes (excludes profiling overheads)
COST CENTRE MODULE SRC %time %alloc
newCtx Crypto.Gpgme.Ctx src/Crypto/Gpgme/Ctx.hs:(21,1)-(51,21) 77.0 0.0
signIntern Crypto.Gpgme.Crypto src/Crypto/Gpgme/Crypto.hs:(310,1)-(354,14) 20.4 8.6
collectResult.go.\ Crypto.Gpgme.Internal src/Crypto/Gpgme/Internal.hs:(29,21)-(34,46) 2.1 81.9
main Main src/Main.hs:(7,1)-(13,43) 0.1 8.7
I have looked in the newCtx function where the time is spent but it is not clear to me what is costing so much.