如何修复 Cython 接口中的程序突然退出到 Fortran

How to fix abrupt program exit in Cython interface to Fortran

简短版

我有旧的 Fortran 代码,我正尝试通过 Cython 扩展模块对其进行包装来访问(已经为其他 Fortran 库完成了该操作)。当从 Fortran 内部调用时,该代码有效,但当通过 Cython 包装器访问时,它会强制程序停止,退出代码为 -1073741819 (0xC0000005)-1,并且没有其他消息。更重要的是:这个 崩溃是随机发生的 :有时 Fortran 代码设法 return,但 Python 脚本仍然在之后崩溃。

我不确定问题出在哪里,因为 Fortran 子例程有一个简单的签名,应该使界面相对简单。下面有更多详细信息(尽可能多地删减)。

非常感谢您的想法!

Cython 设置

相关文件:

这是编译设置文件。正如我之前提到的,我已经为其他 Fortran-Cython 界面使用了几乎相同的脚本,所以应该没问题,但是...

from distutils.core import setup
from Cython.Distutils import build_ext
from Cython.Distutils import Extension  # Adds functionality to the standard distutils.extension.Extension
import Cython.Compiler.Options
from numpy import get_include

module_name = 'cython_module'
cython_interface = 'cython_module.pyx'
link_args = ['wrapper.o', 'mylib.lib']
fortran_libs = [  # Additional library directories for .lib files
    r'C:\Program Files (x86)\IntelSWTools\compilers_and_libraries_2020.2.254\windows\compiler\lib\intel64_win'
]
# Generate an additional HTML file with some feedback on optimization quality
Cython.Compiler.Options.annotate = True

ext_modules = [Extension(
    module_name,
    [cython_interface],
    # I need this for using Complex numbers in other library functions not shown here
    define_macros=[("CYTHON_CCOMPLEX", "0")],
    extra_compile_args=['/O2'],
    extra_link_args=link_args,
    library_dirs=fortran_libs,
    cython_directives={'language_level': "3"}
)]

setup(name='cython_module',
      cmdclass={'build_ext': build_ext},
      # Needed if building with NumPy.
      include_dirs=[get_include()],
      ext_modules=ext_modules)

Cython 代码

cython_module.pyx 的内容:

import numpy as np

cdef extern from "<complex.h>":  # You can ignore this
    ctypedef struct _Dcomplex

cdef extern from "wrapper.h" nogil:
    void py_indexh(double* a, double* b, long long* m, long long* n);

def indexh(double px, double py):
    cdef long long[:] m = np.empty((1000,), dtype=np.int64, order='F')
    cdef long long[:] n = np.empty((1000,), dtype=np.int64, order='F')
    py_indexh(&px, &py, <long long*> m[0], <long long*> n[0])
    return m, n

以及头文件(仅供参考,非常简单):

#include <complex.h>
extern void py_indexh(double* a, double* b, long long* m, long long* n);

Fortran 包装器

我只展示 Fortran 包装器,而不是整个库子例程主体,因为它相当长且令人费解,而且当从 Fortran 本身调用时它显然可以工作。这是:

subroutine py_indexh(a, b, M, N) bind(c)
    use iso_c_binding, only: c_float, c_double, c_int, c_int32_t, c_int64_t, c_double_complex, c_bool
    implicit none

    interface
        subroutine indexh(a, b, M, N)
            real(8),        intent(in)  :: a, b
            integer(8),     intent(out) :: M(1000), N(1000)
        end subroutine indexh
    end interface

    real(c_double),        intent(in)   :: a, b
    integer(C_INT64_T),     intent(out) :: M(1000), N(1000)

    print *, 'Entering indexh from Fortran...'
    call indexh(a, b, M, N)
    print *, 'Exitted indexh from Fortran...'
end subroutine py_indexh

问题/输出

代码编译没有重大问题。仅显示此警告,我认为这与 .lib 文件的编译方式有关。请让我知道我是否应该对此更加担心,它对我所做的 Fortran 的其他 Cython 包装没有造成任何伤害: LINK : warning LNK4098: defaultlib 'LIBCMT' conflicts with use of other libs; use /NODEFAULTLIB:library

当导入 cython 模块并调用 cython_module.indexh(3e-3, 4e-3) 时,它无提示地崩溃,退出代码为 -1-1073741819 (0xC0000005)。然而,在崩溃之前,Fortran 子例程已成功进入,因为 'Entering indexh from Fortran...' 打印在控制台上。有时,甚至显示第二个 print 'Exitted indexh from Fortran...',这应该表明子程序已完成。但是,无论显示一个还是两个打印,程序都会在函数调用后立即崩溃。

澄清一下,indexh(...)的主体中没有奇怪的多线程业务或任何不寻常的东西,只有很多嵌套循环动作。

再次感谢任何帮助或想法!如果我可以显示更多信息,请告诉我。

<long long*> m[0], <long long*> n[0]

这会将 mn 的第一个元素(即整数)转换为 long long*.

你想要

&m[0], &n[0]

获取第一个元素的地址。

当编译器告诉您错误时,转换通常只是关闭编译器的一种方式。