You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何通过pybind11调用的C++函数执行速度慢于Python?

问题

听说C比Python快得多,于是用pybind11做了测试:分别实现C类Foo和Python类Bar,两者都包含自定义函数,用来计算0到100万整数的结果。测试代码如下:

测试代码

main.cpp

#include <pybind11/pybind11.h>
#include <pybind11/functional.h>
#include <pybind11/operators.h>
#include <pybind11/stl.h>

using namespace std;

class Foo {
    private:
        function<double(int)> func;
        double result[1000000];
    public:
        Foo(function<double(int)> f): func(f) {}

        void calculate() {
            for (int t=0; t<1000000; t++) {
                result[t] = func(t);
            }
        }
};


PYBIND11_MODULE(cmake_example, m) {
    pybind11::class_<Foo>(m, "Foo")
        .def(pybind11::init<function<double(int)>>())
        .def("calculate", &Foo::calculate);

    m.attr("__version__") = "0.0.1";
}

mytest.py

import time
import cmake_example


class Bar:
    def __init__(self, func):
        self.func = func
        self.result = [None for _ in range(1_000_000)]

    def calculate(self):
        for t in range(1_000_000):
            self.result[t] = self.func(t)


def myfunction(t):
    return t * t


if __name__ == "__main__":
    start = time.time()
    foo = cmake_example.Foo(myfunction)
    foo.calculate()
    print("cpp:", time.time() - start)

    start = time.time()
    bar = Bar(myfunction)
    bar.calculate()
    print("pyt:", time.time() - start)

测试结果

cpp: 0.4998281002044678
pyt: 0.39750099182128906

疑惑点:

  • 为什么这个场景下C反而比Python慢?原本预期C更快。
  • 更换其他数据类型能不能解决这个问题?

解答

核心原因:跨语言调用的开销

你看到的C更慢,核心问题出在**C循环里每次调用Python函数的跨语言开销**:

  • 当C的Foo::calculate循环调用func(t)时,这个func是Python函数,每次调用都需要完成:Python GIL的获取与释放、C到Python的类型转换(int转Python int)、Python函数调用栈的构建、返回值(Python int转C++ double)的转换,这些步骤的累加开销远大于Python内部调用函数的开销。
  • 反观Python版本的Bar.calculate,函数调用是在解释器内部完成的,没有跨语言的额外开销,而且CPython对内部函数调用和循环有不少优化,所以整体更快。
  • 另外C++里的std::function本身带有虚函数调用的额外开销,也会进一步拉低速度。

关于数据类型的疑问

更换数据类型几乎无法解决这个问题,因为性能瓶颈根本不是数据类型的计算速度,而是跨语言调用的额外开销——哪怕换用int或者其他类型,每次调用的类型转换、GIL操作这些开销依然存在。

优化方案

要让C++版本体现出速度优势,可以从以下几个方向调整:

  1. 把计算逻辑移到C++内部:
    直接在C++里实现myfunction的逻辑,避免跨语言调用。比如修改Foo类,让它可以选择内置的计算函数:
    // 在C++里实现计算逻辑
    double cpp_myfunction(int t) {
        return t * t;
    }
    
    // 给Foo添加内置计算选项
    class Foo {
        // ...
        public:
            Foo() : func(cpp_myfunction) {}
            // 保留原有的构造函数,支持外部传入函数
            Foo(function<double(int)> f): func(f) {}
        // ...
    };
    
  2. 减少跨语言调用次数:
    不要循环调用Python函数,而是一次性把整个数据范围传给Python函数处理,或者让C++一次性处理所有数据,只做一次跨语言交互。
  3. 优化pybind11的函数调用方式:
    使用py::function替代std::function,并手动提前获取GIL,避免循环内反复获取释放:
    void calculate() {
        py::gil_scoped_acquire acquire; // 提前获取GIL,循环内不再重复操作
        for (int t=0; t<1000000; t++) {
            result[t] = func(t);
        }
    }
    

内容的提问来源于stack exchange,提问作者zchmielewska

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 13:43:30