Haskell FFI / C MPFRライブラリラッパーの問題
-
05-07-2019 - |
質問
Doubleの代わりに任意の精度の浮動小数点/ドロップを作成するために、 MPFR FFIを使用していますが、私の努力にもかかわらず、最も単純なコードは機能しません。コンパイル、実行はされますが、しばらく動作するふりをした後、モックでクラッシュします。コードの単純なCバージョンは、数字「1」を喜んで出力します。 〜(小数点以下640位)合計10,000回。 Haskellバージョンは、同じことをするように求められたとき、「1.0000 ... 0000」の289枚の出力だけでデータを静かに破損(?)します。 385枚の印刷後、アサーションの失敗と爆弾が発生します。これが「うまくいくはず」だからデバッグの進め方に困っています。
http://hpaste.org/10923 でコードを熟読し、 http://www.updike.org/mpfr-broken.tar.gz
FreeBSD 6のGHC 6.83とMac OS XのGHC 6.8.2を使用しています。libsとヘッダーファイルの正しいパス(Makefileの変更)でインストールされたMPFR(2.3.2でテスト済み)が必要です。 GMPのものと一緒に)これを正常にコンパイルします。
質問
-
Cバージョンは機能するのに、Haskellバージョンは機能しなくなるのはなぜですか? FFIに近づくと、他に何が欠けていますか? StablePtrsを試したところ、まったく同じ結果が得られました。
-
コードをコンパイルして実行することで、これがMac / BSDのみの問題であるかどうかを他の誰かが確認できますか? (Cコードは動作しますか?Haskellコードは動作しませんか?)LinuxとWindowsの誰でもコンパイル/実行を試みて、同じ結果が得られるかどうかを確認できますか?
Cコード:(works.c)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gmp.h>
#include <mpfr.h>
#include "mpfr_ffi.c"
int main()
{
int i;
mpfr_ptr one;
mpf_set_default_prec_decimal(640);
one = mpf_set_signed_int(1);
for (i = 0; i < 10000; i++)
{
printf("%d\n", i);
mpf_show(one);
}
}
Haskellコード:(Main.hs ---は機能しません)
module Main where
import Foreign.Ptr ( Ptr, FunPtr )
import Foreign.C.Types ( CInt, CLong, CULong, CDouble )
import Foreign.StablePtr ( StablePtr )
data MPFR = MPFR
foreign import ccall "mpf_set_default_prec_decimal"
c_set_default_prec_decimal :: CInt -> IO ()
setPrecisionDecimal :: Integer -> IO ()
setPrecisionDecimal decimal_digits = do
c_set_default_prec_decimal (fromInteger decimal_digits)
foreign import ccall "mpf_show"
c_show :: Ptr MPFR -> IO ()
foreign import ccall "mpf_set_signed_int"
c_set_signed_int :: CLong -> IO (Ptr MPFR)
showNums k n = do
print n
c_show k
main = do
setPrecisionDecimal 640
one <- c_set_signed_int (fromInteger 1)
mapM_ (showNums one) [1..10000]
解決
問題も見られます、
$ uname -a
Linux burnup 2.6.26-gentoo-r1 #1 SMP PREEMPT Tue Sep 9 00:05:54 EDT 2008 i686 Intel(R) Pentium(R) 4 CPU 2.80GHz GenuineIntel GNU/Linux
$ gcc --version
gcc (GCC) 4.2.4 (Gentoo 4.2.4 p1.0)
$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 6.8.3
また、出力が1.0000 ... 000から1.0000 ... [garbage]に変化するのを見ます。
見てみましょう、次は機能します:
main = do
setPrecisionDecimal 640
mapM_ (const $ c_set_signed_int (fromInteger 1) >>= c_show) [1..10000]
これは、実行中に何らかの形で破壊される one
の部分に問題を絞り込みます。ただし、 ghc -C
および ghc -S
の出力を確認しても、ヒントは得られません。
うーん、 ./ noworks + RTS -H1G
も機能し、 [のさまざまな値に対して
、さまざまな方法で障害を示します。 ./ noworks + RTS -k [n] k
も機能しますn]
確固たるリードはありませんが、2つの可能性が思い浮かびます。
- GHCランタイムが使用するGMPと、奇妙な相互作用を持つMPFR
- GHCランタイム内で呼び出されるC関数のスタック領域は制限されており、MPFRはうまく処理されません
言われていることは、 HMPFR ?
他のヒント
Judah JacobsenはHaskell-cafeメーリングリストでこれに回答しました:
これは GHCの既知の問題です。GHCの使用方法が原因です。内部的にGMP(整数を維持するため)。
明らかに、ヒープ内のCデータは、GMPまたはGMPに依存するCライブラリ(使用したいMPFRなど)にアクセスするためにFFIを使用するコードを基本的にすべてのケースでGHCによってそのまま残されます)。いくつかの回避策(痛みを伴う)がありますが、「正しい」方法は、GHCをハッキングする(ハード)か、またはSimonsにGHCのGMPへの依存を削除する(ハード)ことです。
エール&#353; Bizjak、 HMPFR のメンテナーがhaskell-cafeに投稿して公開GHCが手足の割り当てを制御しないようにする方法(したがって、GCして手足を壊すのではなく、手足をそのままにしておく):
mpfr_ptr mpf_new_mpfr()
{
mpfr_ptr result = malloc(sizeof(__mpfr_struct));
if (result == NULL) return NULL;
/// these three lines:
mp_limb_t * limb = malloc(mpfr_custom_get_size(mpfr_get_default_prec()));
mpfr_custom_init(limb, mpfr_get_default_prec());
mpfr_custom_init_set(result, MPFR_NAN_KIND, 0, mpfr_get_default_prec(), limb);
return result;
}
私にとって、これはGHCにGMPの代替を書く努力に参加するよりもはるかに簡単です。