2013-01-07 10 views
6

私はパフォーマンスを向上させるために小さなCモジュールを作ったが、GHCは外部関数をインライン化せず、コストを呼び出すことで加速がなくなった。例えば 、test.hGHCに強制的に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 } 

メイキング:

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

最後に、Main.asに私の代わりに、望ましいinc年代のcallq <inc>説明書を持っています。

+3

ghcは、生成されたコードでC関数をインライン化することを期待していますか?これは-via-Cオプションを使用するとうまくいく可能性があります。そうしないと、ghcがCコードを読み込んでコードを生成する必要があります。 – augustss

+2

リンク時の最適化がない場合は不可能です。試してみると、HaskellとCの両方をLLVMのビットコードにコンパイルし、.bcファイルを 'llvm-link'と組み合わせ、' opt'で最適化し、 'llc'で実行可能コードを出力します。 –

+0

@MikhailGlushenkov、コマンドシーケンスのスケッチを書くことができますか?私はhaskellコードから '.bc'ファイルを取得する方法をgoogleで調べることに失敗しました。 – leventov

答えて

9

GHCはasmバックエンドまたはLLVMバックエンド経由でCコードをインライン化しません。呼び出しているものが本当にコストがかかる場合は、通常、パフォーマンス上の理由からC言語にコールするだけです。 intをインクリメントすることは、私たちがすでにそのためのprimopsを持っているので、そういうことではありません。

これで、C経由で電話すると、GCCにインラインのものが表示されることがあります(生成されたアセンブリをチェックしてください)。

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

inc = fromIntegral . c_inc . fromIntegral 

incための型シグネチャを提供します:今すぐ

は、しかし、あなたは、コールのコストを最小限に抑えるために、すでに行うことができますいくつかのものがあります。ここでIntegerに変換する貴重なサイクルを費やしています。

呼び出しの前にランタイムがブックマークされないように、呼び出しを「安全でない」とマークします。

FFIコールのオーバーヘッドを測定します。これはナノ秒単位で行う必要があります。しかし、まだ高すぎると感じる場合は、write a new primop and jump to it directly.を入力してください。criterionの番号を最初に付けたほうがよいでしょう。

+0

実際に私の "inc"は、ブランチレスSSE min-max関数のセットです:https://gist.github.com/4476908 – leventov

+0

ああ、私はあなたが本当に新しいプリムプスを求めているのを見ています。 http://hackage.haskell.org/trac/ghc/ticket/3557のものを複製していますか? –

+0

一般的にはありませんが、これらの最小〜最大の命令が特にチケットで考慮されているため、詳細には研究していません。 – leventov

関連する問題