【问题标题】:segfault on f2py callbackf2py 回调的段错误
【发布时间】:2016-04-20 19:45:21
【问题描述】:

我正在研究分形生成器代码。主要代码用python编写,迭代部分用fortran编写。我使用f2py 将两个代码粘合在一起。 这是我使用的fortran函数:

function iterate(z0, func, zmax, niter) result(n)

    implicit none

    complex(kind=8), intent(in) :: z0
    real(kind=8), intent(in) :: zmax
    integer, intent(in) :: niter
    external func
    complex(kind=8) :: func

    integer :: n
    complex(kind=8) :: z

    n = 0
    z = z0
    do while ((n < niter) .and. (abs(z) < zmax))
        z = func(z)
        n = n + 1
    end do

end function iterate 

这是生成的包装代码的文档字符串:

n = iterate(z0,func,zmax,niter,[func_extra_args])

Wrapper for ``iterate``.

Parameters
----------
z0 : input complex
func : call-back function
zmax : input float
niter : input int

Other Parameters
----------------
func_extra_args : input tuple, optional
    Default: ()

Returns
-------
n : int

Notes
-----
Call-back functions::

  def func(z): return z
  Required arguments:
    z : input complex
  Return objects:
    z : complex

我在尝试时收到 Segmentation fault 错误 将iterate 与任何python 回调函数一起使用。 这是我得到的示例结果:

>>> from foo import iterate
>>> iterate(1.0j, lambda x: 4.0 + x**2,  4.0, 256)
Segmentation fault

我查看了所有关于 f2py 回调的可用文档,但是 还没有找到任何解决这个问题的方法。任何帮助将不胜感激。

更新

这是来自 gdb 的回溯:

Program received signal SIGSEGV, Segmentation fault.
cb_func_in_iterate2__user__routines (return_value=0x7fffffffdbc0, z_cb_capi=0x3ff0000000000000)
    at /tmp/tmpT8xG1q/src.linux-x86_64-2.7/juliamodule.c:470
470 /tmp/tmpT8xG1q/src.linux-x86_64-2.7/juliamodule.c: No such file or directory.
(gdb) backtrace 
#0  cb_func_in_iterate2__user__routines (return_value=0x7fffffffdbc0, z_cb_capi=0x3ff0000000000000)
    at /tmp/tmpT8xG1q/src.linux-x86_64-2.7/juliamodule.c:470
#1  0x00007ffff6b6482b in iterate2 (z0=(1,1), func=func@entry=0x7ffff6b60c20 <cb_func_in_iterate2__user__routines>, zmax=4, niter=256)
    at julia.f90:38
#2  0x00007ffff6b64897 in f2pywrapiterate2 (iterate2f2pywrap=0, z0=(1,1), func=func@entry=0x7ffff6b60c20 <cb_func_in_iterate2__user__routines>, 
    zmax=4, niter=256) at /tmp/tmpT8xG1q/src.linux-x86_64-2.7/julia-f2pywrappers.f:25
#3  0x00007ffff6b61f5e in f2py_rout_julia_iterate2 (capi_self=<optimized out>, capi_args=<optimized out>, capi_keywds=<optimized out>, 
    f2py_func=0x7ffff6b64880 <f2pywrapiterate2>) at /tmp/tmpT8xG1q/src.linux-x86_64-2.7/juliamodule.c:811
#4  0x00000000004caaa1 in PyEval_EvalFrameEx ()
#5  0x00000000004c87a1 in PyEval_EvalCodeEx ()
#6  0x00000000005030ef in ?? ()
#7  0x00000000004f8c72 in PyRun_FileExFlags ()
#8  0x00000000004f7d77 in PyRun_SimpleFileExFlags ()
#9  0x00000000004982f2 in Py_Main ()
#10 0x00007ffff6f12b45 in __libc_start_main (main=0x497d80 <main>, argc=2, argv=0x7fffffffe2a8, init=<optimized out>, fini=<optimized out>, 
    rtld_fini=<optimized out>, stack_end=0x7fffffffe298) at libc-start.c:287
#11 0x0000000000497ca0 in _start ()

【问题讨论】:

  • 我能问一下,您为什么要尝试使用两种不同的语言来做一些至少看起来相对简单的事情?例如,为什么不直接用 python 写呢?
  • iterate 函数的 Fortran 代码比对应的 python 快了大约 10 倍。
  • 至于你的问题,要明确一点,如果你 z = func(z)z = 4.0 + z*z 你是说它会工作?不从参数列表或任何东西中删除函数,只是做一些小的改变。
  • 好吧,那我得让你失望了。我很确定在 python 和 fortran 之间反复切换会减慢你的速度。你的 fortran 代码并没有做很多事情,所以你要进入 fortran 代码,然后很快就必须回到 python,开关会产生一些成本,运行你的 lambda 函数,这可能是在纯 python 代码中花费大量时间(1/4 到 1/3),然后返回到 fortran,再次产生一些开销。我建议尝试pypy。这是 Python 2.7 的一个更快的实现。速度可能就足够了。
  • 当我用 z = 4.0 + z*z 替换 z = func(z) 时,我得到编译错误。我相信这是由于func 没有在那里使用,因为如果我在iterate 中使用func 但不调用它,代码编译并运行良好。我会看看pypy,但仍然希望能解决这个问题。

标签: python numpy fortran f2py


【解决方案1】:

我有一个解决方法,但恐怕不是一个完整的解决方案。关键点似乎是当func 是一个函数时,f2py 会生成一些看起来很奇怪并且可能只是不正确的 c 代码。将func 更改为子例程似乎可以使其工作。

要使您的代码正常工作,请更改以下内容:

external func
complex(kind=8) :: func

...

do while ((n < niter) .and. (abs(z) < zmax))
    z = func(z)
...

interface foo
   subroutine func(f, z)
     complex(kind=8), intent(out) :: f
     complex(kind=8), intent(in) :: z
   end subroutine func
end interface foo

...

do while ((n < niter) .and. (abs(z) < zmax))
    call func(z, z)
...

这对我来说很好,并给出了正确的答案。如果您对此感到满意,则可以停止阅读。以下是我如何解决它。


至于为什么会这样,需要运行f2pylike

f2py -m modname -h iterate.pyf iterate.f90
f2py -c --debug --build-dir build iterate.pyf iterate.f90

然后我们可以使用gdb python调试代码,然后

run -c "import iterate; iterate.iterate(1.0j, lambda x: 4.0 + x*x, 4.0, 256)"

(尽管听上去,你可能已经走到了这一步)。这样做,我们在build/src.linux-x86_64.3.4/iteratemodule.c 的以下行中发现了段错误(您的目录可能不同):

complex_double z=(*z_cb_capi);

z_cb_capi0x0,因此是段错误。这出现在在 Fortran 代码中调用 func 时实际调用的 Cython 辅助函数中。函数声明出现在上述行之前,在 func 是函数而不是子例程的版本中,它看起来像以下混乱:

static 
#ifdef F2PY_CB_RETURNCOMPLEX
complex_double
#else
void
#endif
 cb_func_in_iterate__user__routines (
#ifndef F2PY_CB_RETURNCOMPLEX
complex_double *return_value
#endif

#ifndef F2PY_CB_RETURNCOMPLEX
,
#endif
complex_double *z_cb_capi) {

本质上是以下之一:

static void cb_func_in_iterate__user__routines (complex_double *return_value, complex_double *z_cb_capi)
static complex_double cb_func_in_iterate__user__routines (complex_double *z_cb_capi)

取决于是否定义了F2PY_CB_RETURNCOMPLEXF2PY_CB_RETURNCOMPLEX 似乎从未被定义,因为 gdb 报告该函数具有第一个定义,但它被称为第二个定义。您可以看到这一点,因为 return_value 在 Fortran 代码中设置为 z 的地址,而没有 (NULL) 传递给 z_cb_capi

所以,这最终导致我们采用第二种方法来解决这个问题:将-DF2PY_CB_RETURNCOMPLEX 传递给f2py

f2py -c -DF2PY_CB_RETURNCOMPLEX --debug --build-dir build iterate.pyf iterate.f90

然后这会以第二种形式编译cb_func_in_iterate__user__routines,这意味着它确实被正确调用了。

现在,我认为您不应该这样做,所以我怀疑这是 f2py 中的一个错误。

【讨论】:

  • 谢谢!我也发现了 z_cb_capi 的问题,但没有设法进一步解决。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-02
相关资源
最近更新 更多