pybind11 ускоряет вызовы функций - PullRequest
0 голосов
/ 21 октября 2018

У меня есть несколько объектов функций C ++, которые я создаю в Python с использованием pybind11, а затем передаю эти объекты из Python другой функции C ++, которая их вызывает.Поскольку эти функции имеют состояние, они не проходят оптимизацию pybind11 для функций Python без состояния и производительность очень низкая.

Я могу обойти это с помощью уродливого хака, который возвращает указатель созданного объекта C ++ на Python, который затем передает указатель обратно в вызывающую функцию C ++.Тем не менее, я надеялся, что есть более чистый и более удобный способ сделать это.

Вот некоторый код, который копирует это (import_call_execute встраивает процесс Python и запускает его) на основе: https://pythonextensionpatterns.readthedocs.io/en/latest/debugging/debug_in_ide.html

Первая приведенная ниже программа на Python занимает 163 миллисекунды на моей машине, а вторая - всего 0,5 миллисекунды

#include <pybind11/pybind11.h>
#include <pybind11/functional.h>
#include <iostream>
#include <chrono>

#include "py_import_call_execute.hpp"

using namespace std;
using namespace std::chrono;
using namespace pybind11::literals;

namespace py = pybind11;

class TestFunc {
public:
    TestFunc(int a): _a(a) {}

    int operator()(int b) const {
        return _a + b;
    }

    size_t get_ptr() {
        return (size_t)this;
    }
private:
    int _a;
};

int test_dummy_function(const std::function<int(int)> &f) {
    auto start = high_resolution_clock::now();

    int sum = 0;
    for (int i = 0; i < 100000; ++i) {
        sum += f(i);
    }
    auto stop = high_resolution_clock::now();
    auto duration = duration_cast<microseconds>(stop - start);

    cout << "sum: " << sum << " time: " << duration.count() / 1000.0 << " milliseconds" << endl;

    return sum;
}

int test_dummy_function2(std::size_t ptr) {
    auto start = high_resolution_clock::now();

    TestFunc* f = reinterpret_cast<TestFunc*>(ptr);

    int sum = 0;
    for (int i = 0; i < 100000; ++i) {
        sum += (*f)(i);
    }
    auto stop = high_resolution_clock::now();
    auto duration = duration_cast<microseconds>(stop - start);

    cout << "sum: " << sum << " time: " << duration.count() / 1000.0 << " milliseconds" << endl;

    return sum;
}

PYBIND11_MODULE(pybind_testing, m) {
    py::class_<TestFunc>(m, "TestFunc")
    .def(py::init<int>(), "a"_a)
    .def("__call__", &TestFunc::operator(), "b"_a = 3)
    .def("get_ptr", &TestFunc::get_ptr);

    m.def("test_dummy_function", test_dummy_function);
    m.def("test_dummy_function2", test_dummy_function2);
 }

int main(int argc, const char *argv[]) {
    argc = 4;
    const char *argv2[] = {
            "python",
            "/Users/sal/Developer/coatbridge/testing/pybind11",
            "test_pybind11",
            "test_pybind11"};
    return import_call_execute(argc, argv2);
}

Функция Python 1:

import pybind_testing as pt

def test_pybind11():
    test_func = pt.TestFunc(2)
    pt.test_dummy_function(test_func)

Функция Python 2:

import pybind_testing as pt

def test_pybind11():
    test_func = pt.TestFunc(2)
    pt.test_dummy_function2(test_func.get_ptr())

1 Ответ

0 голосов
/ 21 октября 2018

Низкая производительность не имеет ничего общего с pybind11 или Python.Это медленно, потому что вы используете std::function, что совсем не похоже на обычный вызов функции.

Это можно увидеть, заменив код в main() следующим:

TestFunc test_func(2);
test_dummy_function(test_func);
test_dummy_function2(test_func.get_ptr());

Чтобы это исправить, просто прекратите использовать std::function.Вы можете передать объект TestFunc напрямую по ссылке или (умному?) Указателю.Не должно быть необходимости взламывать его адрес на size_t и обратно (хотя учтите, что если вам нужно это сделать, правильный тип - uintptr_t, а не size_t).

...