Haskell FFI / C MPFR库包装器问题

时间:2022-09-10 16:59:40

In order to create an arbitrary precision floating point / drop in replacement for Double, I'm trying to wrap MPFR using the FFI but despite all my efforts the simplest bit of code doesn't work. It compiles, it runs, but it crashes mockingly after pretending to work for a while. A simple C version of the code happily prints the number "1" to (640 decimal places) a total of 10,000 times. The Haskell version, when asked to do the same, silently corrupts (?) the data after only 289 print outs of "1.0000...0000" and after 385 print outs, it causes an assertion failure and bombs. I'm at a loss for how to proceed in debugging this since it "should work".

为了创建一个替代Double的任意精度浮点/丢弃,我试图使用FFI包装MPFR,但尽管我付出了所有努力,但最简单的代码不起作用。它编译,它运行,但它假装工作一段时间后嘲讽崩溃。代码的简单C版本愉快地将数字“1”打印到(640个小数位)总共10,000次。当被要求做同样的事情时,Haskell版本在仅仅289次打印输出“1.0000 ... 0000”之后默默地破坏(?)数据,并且在385次打印输出之后,它导致断言失败和炸弹。我不知道如何继续调试它,因为它“应该工作”。

The code can be perused at http://hpaste.org/10923 and downloaded at http://www.updike.org/mpfr-broken.tar.gz

该代码可以在http://hpaste.org/10923上阅读,并在http://www.updike.org/mpfr-broken.tar.gz下载。

I'm using GHC 6.83 on FreeBSD 6 and GHC 6.8.2 on Mac OS X. Note you will need MPFR (tested with 2.3.2) installed with the correct paths (change the Makefile) for libs and header files (along with those from GMP) to successfully compile this.

我在Mac OS X上使用FreeBSD 6上的GHC 6.83和Mac OS X上的GHC 6.8.2。注意你需要安装带有正确路径的MPFR(用2.3.2测试)(更改Makefile)以及libs和头文件(以及那些从GMP)到成功编译。

Questions

  • Why does the C version work, but the Haskell version flake out? What else am I missing when approaching the FFI? I tried StablePtrs and had the exact same results.

    为什么C版本可以工作,但Haskell版本会崩溃?在接近FFI时我还缺少什么?我尝试了StablePtrs并得到了完全相同的结果。

  • Can someone else verify if this is a Mac/BSD only problem by compiling and running my code? (Does the C code "works" work? Does the Haskell code "noworks" work?) Can anyone on Linux and Windows attempt to compile/run and see if you get the same results?

    有人可以通过编译和运行我的代码来验证这是否只是Mac / BSD问题? (C代码“工作”是否有效?Haskell代码“noworks”是否有效?)Linux和Windows上的任何人都可以尝试编译/运行并查看是否得到相同的结果?

C code: (works.c)

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 code: (Main.hs --- doesn't work)

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]  

3 个解决方案

#1


3  

I see the problem too, on a

我也看到了这个问题

$ 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

I also see the output changing from 1.0000...000 to 1.0000...[garbage].

我也看到输出从1.0000 ... 000变为1.0000 ... [垃圾]。

Let's see, the following does work:

让我们看看,以下确实有效:

main = do
    setPrecisionDecimal 640
    mapM_ (const $ c_set_signed_int (fromInteger 1) >>= c_show) [1..10000]

which narrows down the problem to parts of one being somehow clobbered during runtime. Looking at the output of ghc -C and ghc -S, though, isn't giving me any hints.

这会将问题缩小到在运行时期间以某种方式被破坏的部分。但是,看看ghc -C和ghc -S的输出,并没有给我任何提示。

Hmm, ./noworks +RTS -H1G also works, and ./noworks +RTS -k[n]k, for varying values of [n], demonstrate failures in different ways.

嗯,。/ noworks + RTS -H1G也可以工作,而./noworks + RTS -k [n] k,对于[n]的不同值,以不同的方式演示失败。

I've got no solid leads, but there are two possibilities that jump to my mind:

我没有坚实的线索,但有两种可能性让我想到:

  • GMP, which the GHC runtime uses, and MPFR having some weird interaction
  • GHC运行时使用的GMP和MPFR有一些奇怪的交互

  • stack space for C functions called within the GHC runtime is limited, and MPFR not dealing well
  • 在GHC运行时内调用的C函数的堆栈空间是有限的,并且MPFR处理不好

That being said... is there a reason you're rolling your own bindings rather than use HMPFR?

那就是说...你有没有理由推出自己的绑定而不是使用HMPFR?

#2


4  

Judah Jacobsen answered this on the Haskell-cafe mailing list:

Judah Jacobsen在Haskell-cafe邮件列表上回答了这个问题:

This is a known issue with GHC because of the way GHC uses GMP internally (to maintain Integers).

这是GHC的一个已知问题,因为GHC在内部使用GMP(维护整数)的方式。

Apparently C data in the heap is left alone by GHC in basically all cases except code that uses the FFI to access GMP or any C library that relies on GMP (like MPFR that I wanted to use). There are some workarounds (painful) but the "right" way would be to either hack GHC (hard) or get the Simons to remove GHC's dependence on GMP (harder).

显然,除了使用FFI访问GMP的代码或依赖于GMP的任何C库(如我想使用的MPFR)之外,GHC基本上在所有情况下都会将堆中的C数据单独留下。有一些解决方法(痛苦),但“正确”的方式是破解GHC(硬)或让Simons消除GHC对GMP的依赖(更难)。

#3


1  

Aleš Bizjak, maintainer of HMPFR posted to haskell-cafe and showed how to keep GHC from controlling allocation of the limbs (and hence leaving them alone, instead of GCing them and clobbering them):

HMPFR的维护者AlešBizjak向haskell-cafe发布了一篇文章,并展示了如何防止GHC控制肢体的分配(因此不要让他们独自一人,而不是将他们搞砸并破坏他们):

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;  
}

To me, this is much easier than joining the effort to write a replacement for GMP in GHC, which would be the only alternative if I really wanted to use any library that depends on GMP.

对我而言,这比在GHC中为GMP编写替代品要容易得多,如果我真的想使用任何依赖于GMP的库,这将是唯一的选择。

#1


3  

I see the problem too, on a

我也看到了这个问题

$ 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

I also see the output changing from 1.0000...000 to 1.0000...[garbage].

我也看到输出从1.0000 ... 000变为1.0000 ... [垃圾]。

Let's see, the following does work:

让我们看看,以下确实有效:

main = do
    setPrecisionDecimal 640
    mapM_ (const $ c_set_signed_int (fromInteger 1) >>= c_show) [1..10000]

which narrows down the problem to parts of one being somehow clobbered during runtime. Looking at the output of ghc -C and ghc -S, though, isn't giving me any hints.

这会将问题缩小到在运行时期间以某种方式被破坏的部分。但是,看看ghc -C和ghc -S的输出,并没有给我任何提示。

Hmm, ./noworks +RTS -H1G also works, and ./noworks +RTS -k[n]k, for varying values of [n], demonstrate failures in different ways.

嗯,。/ noworks + RTS -H1G也可以工作,而./noworks + RTS -k [n] k,对于[n]的不同值,以不同的方式演示失败。

I've got no solid leads, but there are two possibilities that jump to my mind:

我没有坚实的线索,但有两种可能性让我想到:

  • GMP, which the GHC runtime uses, and MPFR having some weird interaction
  • GHC运行时使用的GMP和MPFR有一些奇怪的交互

  • stack space for C functions called within the GHC runtime is limited, and MPFR not dealing well
  • 在GHC运行时内调用的C函数的堆栈空间是有限的,并且MPFR处理不好

That being said... is there a reason you're rolling your own bindings rather than use HMPFR?

那就是说...你有没有理由推出自己的绑定而不是使用HMPFR?

#2


4  

Judah Jacobsen answered this on the Haskell-cafe mailing list:

Judah Jacobsen在Haskell-cafe邮件列表上回答了这个问题:

This is a known issue with GHC because of the way GHC uses GMP internally (to maintain Integers).

这是GHC的一个已知问题,因为GHC在内部使用GMP(维护整数)的方式。

Apparently C data in the heap is left alone by GHC in basically all cases except code that uses the FFI to access GMP or any C library that relies on GMP (like MPFR that I wanted to use). There are some workarounds (painful) but the "right" way would be to either hack GHC (hard) or get the Simons to remove GHC's dependence on GMP (harder).

显然,除了使用FFI访问GMP的代码或依赖于GMP的任何C库(如我想使用的MPFR)之外,GHC基本上在所有情况下都会将堆中的C数据单独留下。有一些解决方法(痛苦),但“正确”的方式是破解GHC(硬)或让Simons消除GHC对GMP的依赖(更难)。

#3


1  

Aleš Bizjak, maintainer of HMPFR posted to haskell-cafe and showed how to keep GHC from controlling allocation of the limbs (and hence leaving them alone, instead of GCing them and clobbering them):

HMPFR的维护者AlešBizjak向haskell-cafe发布了一篇文章,并展示了如何防止GHC控制肢体的分配(因此不要让他们独自一人,而不是将他们搞砸并破坏他们):

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;  
}

To me, this is much easier than joining the effort to write a replacement for GMP in GHC, which would be the only alternative if I really wanted to use any library that depends on GMP.

对我而言,这比在GHC中为GMP编写替代品要容易得多,如果我真的想使用任何依赖于GMP的库,这将是唯一的选择。