代码之家  ›  专栏  ›  技术社区  ›  Jared Updike

Haskell FFI/C MPFR库包装器问题

  •  4
  • Jared Updike  · 技术社区  · 18 年前

    MPFR 使用FFI,但尽管我做了很多努力,最简单的代码还是不起作用。它可以编译、运行,但在假装工作了一段时间后,它会模拟地崩溃。一个简单的C版本的代码愉快地将数字“1”打印到(640位小数位),总共打印10000次。当要求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和头文件(以及GMP中的头文件)安装正确路径(更改生成文件)的MPFR(使用2.3.2进行测试),才能成功编译此文件。

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

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

    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 回复  |  直到 18 年前
        1
  •  4
  •   Jared Updike    18 年前

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

    这是 a known issue with GHC 因为GHC内部使用GMP的方式(维护整数)。

    显然,基本上在所有情况下,GHC都不处理堆中的C数据 除了 使用FFI访问GMP或任何依赖GMP的C库(如我想使用的MPFR)的代码。有一些变通方法(痛苦),但“正确”的方法是要么破解GHC(困难),要么让西蒙斯消除GHC对GMP的依赖(困难)。

        2
  •  3
  •   ephemient    18 年前

    我也看到了这个问题,从一个侧面来看

    $ 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…[垃圾]。

    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] ,以不同的方式演示故障。

    • GHC运行时使用的GMP和MPFR有一些奇怪的交互

    话虽如此。。。您使用自己的绑定而不是使用 HMPFR ?

        3
  •  1
  •   Jared Updike    18 年前

    艾尔·比扎克,这家公司的维护者 HMPFR 张贴到haskell咖啡馆,展示了如何防止GHC控制肢体的分配(从而使肢体不受影响,而不是对肢体进行GCG和撞击):

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