Проблемы оболочки библиотеки Haskell FFI / C MPFR - PullRequest
4 голосов
/ 06 октября 2008

Чтобы создать произвольную точность с плавающей запятой / заменой для Double, я пытаюсь обернуть MPFR , используя FFI, но, несмотря на все мои усилия, самый простой бит кода не работает. Он компилируется, запускается, но насмешливо вылетает после того, как некоторое время притворяется, что работает. Простая C-версия кода с радостью печатает число от «1» до (640 десятичных знаков) в общей сложности 10000 раз. Версия на Haskell, когда ее просят сделать то же самое, молча повреждает (?) Данные только после 289 распечаток «1.0000 ... 0000» и после 385 распечаток, это вызывает ошибку подтверждения и бомбы. Я в недоумении, как приступить к отладке этого, так как это «должно работать».

Код можно просмотреть на http://hpaste.org/10923 и загрузить на http://www.updike.org/mpfr-broken.tar.gz

Я использую GHC 6.83 во FreeBSD 6 и GHC 6.8.2 в Mac OS X. Обратите внимание, что вам понадобится MPFR (протестированный с 2.3.2) с правильными путями (измените Makefile) для библиотек и заголовочных файлов ( вместе с таковыми из GMP), чтобы успешно скомпилировать это.

Вопросы

  • Почему работает версия C, но версия на Haskell не работает? Что еще мне не хватает при приближении к FFI? Я попробовал StablePtrs и получил точно такие же результаты.

  • Может ли кто-нибудь еще проверить, является ли это проблемой только для Mac / BSD, скомпилировав и выполнив мой код? (Работает ли код на C? Работает ли код на Haskell? Noworks?) Может ли кто-нибудь в 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]  

Ответы [ 3 ]

4 голосов
/ 06 октября 2008

Джуда Якобсен ответил на это в списке рассылки Haskell-cafe:

Это известная проблема с GHC из-за способа, которым GHC использует GMP внутри (для поддержки целых чисел).

Очевидно, что данные C в куче оставлены GHC в основном во всех случаях , за исключением кода, который использует FFI для доступа к GMP или любой библиотеке C, которая использует GMP (например, MPFR, который я хотел использовать) , Есть некоторые обходные пути (болезненные), но «правильным» способом было бы либо взломать GHC (тяжело), ​​либо заставить Саймонов убрать зависимость GHC от GMP (сложнее).

3 голосов
/ 06 октября 2008

Я тоже вижу проблему на

$ 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] по-разному демонстрируют сбои.

У меня нет твердого лидерства, но есть две возможности, которые приходят мне в голову:

  • GMP, который использует среда выполнения GHC, и MPFR со странным взаимодействием
  • пространство стека для функций C, вызываемых во время выполнения GHC, ограничено, и MPFR работает плохо

Как говорится, есть ли причина, по которой вы катаете свои собственные привязки, а не используете HMPFR ?

1 голос
/ 06 октября 2008

Алеш Бизжак, сопровождающий HMPFR , отправил сообщение в 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;  
}

Для меня это гораздо проще, чем объединить усилия для написания замены для GMP в GHC, которая была бы единственной альтернативой, если бы я действительно хотел использовать любую библиотеку, которая зависит от GMP.

...