简体   繁体   English

共享库与打开进程性能

[英]Shared library vs. opening a process performance

I have a certain base of Python code (a Flask server).我有一定的 Python 代码基础(Flask 服务器)。 I need the server to perform a performance-critical operation, which I've decided to implement in C++.我需要服务器执行性能关键操作,我决定在 C++ 中实现该操作。 However, as the C++ part has other dependencies, trying out ctypes and boost.python yielded no results (not found symbols, other libraries even when setting up the environment, etc., basically, there were problems ).但是,由于C++部分还有其他依赖,尝试了ctypesboost.python没有结果(没有找到符号,其他库甚至在设置环境时等等,基本上都有问题)。 I believe a suitable alternative would be for me to just compile the C++ part into an executable (a single function/procedure is required) and run it from python using commands or subprocess , communicating through stdin/out for example.我相信一个合适的替代方案是我只需将 C++ 部分编译为可执行文件(需要单个函数/过程)并使用commandssubprocess进程从 python 运行它,例如通过stdin/out进行通信。 The only thing I'm worried about is that this will slow down the procedure enough to matter and since I'm unable to create a shared object library, calling its function from python directly, I cannot benchmark the speedup.我唯一担心的是这会减慢程序的速度,因为我无法创建共享的 object 库,从 python 调用它的 function 库,我无法直接进行基准测试。

When I compile the C++ code into an executable and run it with some sample data, the program takes ~5s to run.当我将 C++ 代码编译成可执行文件并使用一些示例数据运行它时,程序需要~5s才能运行。 This does not account for opening the process from python, nor for passing data between the processes.这不考虑从 python 打开进程,也不考虑在进程之间传递数据。

The question is: How big of a speedup can one expect by using ctypes/boost with a SO compared to creating a new process to run the procedure?问题是:与创建一个新进程来运行该过程相比,通过使用带有 SO 的 ctypes/boost 可以期望多大的加速? If I regard the number to be big enough, it would motivate me to solve the encountered problems, basically, I'm asking if it's worth it .如果我认为这个数字足够大,它会激励我解决遇到的问题,基本上,我是在问是否值得

If you're struggling with creating binding using Boost.Python, you can manually expose your API via c-functions and use them via FFI.如果您在使用 Boost.Python 创建绑定时遇到困难,您可以通过 c 函数手动公开您的 API 并通过 FFI 使用它们。

Here's a simple example, which briefly explains my idea.这是一个简单的例子,它简要解释了我的想法。 At first, you create a shared library, but add some extra functions here, which in the example I put into extern "C" section.首先,您创建了一个共享库,但在此处添加了一些额外的函数,在示例中我将其放入extern "C"部分。 It's necessary to use extern "C" since otherwise function names will be mangled and their actual names are likely to be different from those you've declared:必须使用extern "C"否则 function 名称将被破坏,并且它们的实际名称可能与您声明的名称不同:

#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);
}
}

Then you create function bindings in python.然后在 python 中创建 function 绑定。 As you can see function declarations are almost the same as they are in the cpp file below.如您所见,function 声明与下面的 cpp 文件中的声明几乎相同。 I used built-in types only (like void * , uint8_t and anything, but I believe FFI allows you to declare and use custom structs as well):我只使用了内置类型(如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)

And that's basically it.基本上就是这样。

In my opinion inter-processing is going to be the last resort when nothing else works since:在我看来,当没有其他方法起作用时,交互处理将是最后的手段,因为:

  • you need to put a lot of efforts implementing details of your communication protocol, either if you use something popular, there could be a lot of issues, especially from c++-side;你需要付出很多努力来实现你的通信协议的细节,如果你使用流行的东西,可能会出现很多问题,尤其是从 c++ 端;
  • inter-process communication is generally more expensive in terms of processor time.就处理器时间而言,进程间通信通常更昂贵。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM