共享库 vs. 开启进程性能

Shared library vs. opening a process performance

我有一定的 Python 代码基础(Flask 服务器)。我需要服务器执行性能关键操作,我决定用 C++ 实现它。但是由于C++部分有其他依赖,试了ctypesboost.python都没有结果(找不到符号,其他库连环境都搭建等等,基本上有问题)。我相信一个合适的替代方法是将 C++ 部分编译成一个可执行文件(需要一个 function/procedure),然后 运行 从 python 使用 commandssubprocess,例如通过 stdin/out 进行通信。我唯一担心的是这会减慢程序的速度,而且由于我无法创建共享对象库,直接从 python 调用它的函数,所以我无法对加速进行基准测试。

当我将 C++ 代码编译成可执行文件并 运行 它带有一些示例数据时,程序需要 ~5s 到 运行。这不考虑从 python 打开进程,也不考虑在进程之间传递数据。

问题是:与为 运行 程序创建一个新进程相比,将 ctypes/boost 与 SO 一起使用可以预期有多大的加速? 如果我认为数字足够大,就会激励我去解决遇到的问题,基本上,我在问是否值得

如果您正在努力使用 Boost.Python 创建绑定,您可以通过 c 函数手动公开您的 API 并通过 FFI 使用它们。

这里举个简单的例子,简单说明一下我的想法。首先,您创建一个共享库,但在此处添加一些额外的函数,在示例中我将其放入 extern "C" 部分。有必要使用 extern "C" 否则函数名称将被破坏并且它们的实际名称可能与您声明的名称不同:

#include <cstdint>
#include <cstdio>

#ifdef __GNUC__
#define EXPORT __attribute__ ((visibility("default")))
#else // __GNUC__
#error "Unsupported compiler"
#endif // __GNUC__

class data_processor {
public:
  data_processor() = default;

  void process_data(const std::uint8_t *data, std::size_t size) {
    std::printf("processing %zu bytes of data at %p\n", size, data);
  }
};

extern "C" {
EXPORT void *create_processor() {
  return new data_processor();
}

EXPORT void free_processor(void *data) {
  delete static_cast<data_processor *>(data);
}

EXPORT void process_data(void *object, const std::uint8_t *data, const std::uint32_t size) {
  static_cast<data_processor *>(object)->process_data(data, size);
}
}

然后在 python 中创建函数绑定。如您所见,函数声明与下面的 cpp 文件中的声明几乎相同。我只使用了内置类型(比如 void *uint8_t 等等,但我相信 FFI 也允许您声明和使用自定义结构):

from cffi import FFI

mylib_api = FFI()
mylib_api.cdef("""
    void *create_processor();
    void free_processor(void *object);
    void process_data(void *object, const uint8_t *data, uint32_t size);
""")
mylib = mylib_api.dlopen("mylib.so-file-location")

processor = mylib.create_processor()
try:
    buffer = b"buffer"
    mylib.process_data(processor, mylib_api.from_buffer("uint8_t[]", python_buffer=buffer), len(buffer))
finally:
    mylib.free_processor(processor)

基本上就是这样。

在我看来,当其他方法都不起作用时,内部处理将是最后的手段:

  • 你需要付出很多努力来实现你的通信协议的细节,如果你使用流行的东西,可能会有很多问题,尤其是从 c++ 端;
  • 进程间通信通常在处理器时间方面更昂贵。