gpt4 book ai didi

Python "print"在嵌入到 MPI 程序中时不起作用

转载 作者:搜寻专家 更新时间:2023-10-31 02:23:24 28 4
gpt4 key购买 nike

我有一个嵌入到 C++ MPI 应用程序中的 Python 3 解释器。此应用程序加载脚本并将其传递给解释器。

当我在没有 MPI 启动器的情况下在 1 个进程上执行程序时(简单地调用 ./myprogram),脚本被正确执行并且它的“打印”语句输出到终端。当脚本有错误时,我使用 PyErr_Print() 在 C++ 端打印它。

然而,当我通过 mpirun 启动程序时(即使在单个进程上),我没有从 python 代码中的“打印”中获得任何输出。当我的脚本有错误时,我也没有从 PyErr_Print() 得到任何东西。

我猜 Python 处理标准输出的方式与 MPI(此处为实际 Mpich)处理将进程输出重定向到启动器并最终重定向到终端的方式不匹配。

关于如何解决这个问题有什么想法吗?

最佳答案

[编辑,遵循 this issue 的建议]

每次调用 PyErr_Print 后都需要 flush_io(),其中 flush_io 可以是这个函数:

void flush_io(void)
{
PyObject *type, *value, *traceback;
PyErr_Fetch(&type, &value, &traceback); // in Python/pythonrun.c, they save the traceback, let's do the the same

for (auto& s: {"stdout", "stderr"}) {
PyObject *f = PySys_GetObject(s);
if (f) PyObject_CallMethod(f, "flush", NULL);
else PyErr_Clear();
}

PyErr_Restore(type, value, traceback);
}

[在我的旧分析之下,它仍然有一些有趣的信息]

我最终遇到了同样的问题(PyErr_Print 无法在 mpirun 中运行)。追溯(涉及 python3 的一些 gdb)并比较工作的东西(./myprogram)和非工作的东西(mpirun -np 1 ./myprogram),我最终在 _io_TextIOWrapper_write_impl at ./Modules/_io/textio.c:1277(顺便说一下 python-3.6.0)。

2次运行的唯一区别是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)。

根据这些信息,我可以得出 3 种解决方案(前 2 种是快速修复,第 3 种更好):

1/在启动 python 解释器的 C 代码中,在创建 sys.stderr 之前设置缓冲标志。代码变为:

Py_UnbufferedStdioFlag = 1;   // force line_buffering for _all_ I/O
Py_Initialize();

这会在所有情况下将 Python 的回溯带回屏幕;但可能会带来灾难性的 I/O ......所以只有 Debug模式下的可接受解决方案。

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 中是这样)。我将此文件中的函数复制到 myprogram 中,结果证明它完全可以完成工作。

关于Python "print"在嵌入到 MPI 程序中时不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29352485/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com