我在C++ MPI应用程序中嵌入了Python3解释器。此应用程序加载脚本并将其传递给解释器。
当我在没有MPI启动器的情况下在1个进程上执行程序时(简单地调用./myprogram),脚本可以正确执行,并且它的"print“语句输出到终端。当脚本出现错误时,我使用PyErr_Print()在C++端打印它。
然而,当我通过mpirun启动程序时(即使是在单个进程上),我没有从python代码中的"print“得到任何输出。当我的脚本有错误时,我也不能从PyErr_Print()得到任何东西。
我猜Python处理标准输出的方式与MPI (这里实际上是Mpich )将进程的输出重定向到启动器并最终重定向到终端的方式不匹配。
有没有办法解决这个问题?
发布于 2018-03-02 19:53:18
我最终也遇到了同样的问题(PyErr_Print
不能在mpirun上运行)。回溯(涉及python3的一些gdb )并比较正常工作的部分(./myprogram)和非工作的部分(mpiunpython1 ./myprogram),我最终在_io_TextIOWrapper_write_impl
中找到了./Modules/_io/textio.c:1277
(顺便说一句,Python3.6.0)。
两次运行之间唯一的区别是self->line_buffering
是1对0(此时self
代表sys.stderr
)。然后,在pylifecycle.c:1128
中,我们可以看到是谁决定了这个值:
if (isatty || Py_UnbufferedStdioFlag)
line_buffering = Py_True;
因此,MPI似乎在启动程序之前对stderr做了一些事情,这使得它不是tty。我还没有调查在mpirun中是否有一个选项可以将tty标志保留在stderr上...如果有人知道这一点,那将会很有趣(尽管再想一想,mpi可能有很好的理由将他的文件描述符放在stdout&stderr的位置,例如,它的--output-filename )。
有了这些信息,我可以提出三个解决方案(前两个是快速修复,第三个更好):
1/在启动python解释器的C代码中,在创建sys.stderr之前设置缓冲标志。代码变成:
Py_UnbufferedStdioFlag = 1; // force line_buffering for _all_ I/O
Py_Initialize();
这将使Python的回溯在所有情况下都回到屏幕上;但可能会给出灾难性的I/O ...所以只有在调试模式下才是可接受的解决方案。
2/在python (嵌入式)脚本中,在最开始添加以下内容:
import sys
#sys.stderr.line_buffering = True # would be nice, but readonly attribute !
sys.stderr = open("error.log", 'w', buffering=1 )
然后,该脚本将回溯跟踪转储到此文件error.log。
我还尝试在PyErr_Print()之后添加一个对fflush(stderr)或fflush(NULL)的调用...但这并不起作用(因为sys.stderr有自己的内部缓冲)。不过,这将是一个不错的解决方案。
3/经过进一步的研究,我在
Python/pythonrun.c:57:static void flush_io(void);
它实际上是在此文件中的每个PyErr_Print之后调用的。不幸的是,它是静态的(只存在于该文件中,在Python.h中没有引用,至少在3.6.0中是这样)。我将这个文件中的函数复制到我的程序中,结果证明它完全可以完成这项工作。
https://stackoverflow.com/questions/29352485
复制相似问题