动手学深度学习框架(2)- python 端如何调用 c++ 的代码
0 前言
朋友们好,欢迎来到深度学习框架系列专题。在上一篇文章中,我抛出了一系列问题,算是本专题的序言了,今天我们将正式开启 paddle 学习之旅,去了解它的实现原理和技术细节。对于本文的选题,我想了许久,最后还是觉得讲讲『python 端如何调用 c++ 的代码』最为合适,阅读深度学习框架源码,不管是 pytorch 还是 tensorflow,这都是绕不开的,也是最基础的。试想:如果一个新手看着 python 端的一行行代码,却不知道它是在哪定义的,函数体里内容是什么,岂不是抓狂~
1 什么是 pybind?
pybind11 是一个轻量级的只包含头文件的 c++ 库,用于将你的 c++ 代码暴露给 python 调用(反之亦可,但主要还是前者)。pybind11 借鉴了 Boost::Python 库的设计,但使用了更为简洁的实现方式,使用了大量 c++11 的新特性,更易于使用。
2 paddle 中 python 端是如何调用 c++ 代码的?
关于 pybind 的原理,网上已经有不少文章已经介绍过了,不再赘述,这里主要讲下 paddle 框架中 python 和 c++ 端是如何绑定的。
示例 1
首先,我们来看一个最基础的,也是最常见的用法。Program 是 paddle 框架中用来描述神经网络的结构,如果要使用 paddle 分布式训练功能,首先得实例化一个 Program 对象。
import paddle.fluid as fluid
program=fluidgram()
很明显,Program 是 python 中的一个类,它的定义是在文件
python/paddle/fluid/framework.py 里,代码如下:
class Program(object):
def __init__(self):
self.desc=coregramDesc()
...
由上述代码可知,实例化 Python Program 对象时,实际上调用的是 core 里的 ProgramDesc. 接着,我们先来看看
python/paddle/fluid/core.py 中干了啥:
from ctypes import cdll
cdll.LoadLibrary(core_avx.so)
什么意思呢?paddle c++ 代码编译完之后生成 core_avx.so 文件,而当我们安装完 paddle wheel 包之后,python/paddle/fluid 目录下就会有 core_avx.so. 也就是说,core.py 文件的主要功能就是加载 c++ 端的 so. 那 python 端的 ProgramDesc 类型又是在哪定义的呢?答案是protobuf.
void BindProgramDesc(pybind11::module *m) {
pybind11::class_(*m, "ProgramDesc", "")
.def(pybind11::init<>())
.def("__init__",
[](pd::ProgramDesc &self, const pd::ProgramDesc &other) {
new (&self) pd::ProgramDesc(other);
})
//...
到这儿,python 端终于能够访问到 c++ 端的类型了,即 pd::ProgramDesc,其中,namespace pd=paddle::framework.
示例 2
我们再来看看 executor.py 中的用法。
from . import core
g_scope=core.Scope()
assert isinstance(scope, core._Scope)
var =scope.find_var(_to_name_str(name))
这个 Scope() 又是什么呢?pybind 文件中有如下定义:
PYBIND11_MODULE(core_avx, m) {
m.def("Scope",
[]() -> Scope * {
auto *s=new Scope();
ScopePool::Instance().Insert(std::unique_ptr(s));
return s;
},
R"DOC(
Create a new scope.
Returns:
out (core._Scope): the created scope.
)DOC",
py::return_value_policy::reference);
//...
}
PYBIND11_MODULE 这个宏用来告诉 python: import 神马,"core_avx" 是这个 module 的名字,"m" 是一个 py::module 类型的变量,可以理解成代表 module 本身, "module::def()",也就是 "m.def",用来定义 python 端的接口函数。
上述代码中绑定了 python 端 Scope() 和 c++ 中的函数(lambda 表达式)。进一步发现,调用 python 函数的返回值显然是 python 端的,也就是 _Scope,而它对应的也就是 c++ 端的 Scope 类,绑定代码如下:
py::class_(m, "_Scope", R"DOC(
Scope is an association of a name to Variable. All variables belong to Scope.
Variables in a parent scope can be retrieved from local scope.
You need to specify a scope to run a Net, i.e., `exe.Run(&scope)`.
One net can run in different scopes and update different variable in the
scope.
You can create var in a scope and get it from the scope.
Examples:
.. code-block:: python
import paddle.fluid as fluid
# create tensor from a scope and set value to it.
param=scope.var('Param').get_tensor()
param_array=np.full((height, row_numel), 5.0).astype("float32")
param.set(param_array, place)
)DOC")
.def("_remove_from_pool",
[](Scope &self) { ScopePool::Instance().Remove(&self); })
.def("var",
[](Scope &self, const std::string &name) -> Variable * {
return self.Var(name);
},
py::arg("name"),
R"DOC(
Find or create variable named :code:`name` in the current scope.
If the variable named :code:`name` does not exist in the
current scope, the variable would be created. Otherwise,
return the existing variable.
Args:
name (str): the variable name.
Returns:
out (core.Variable): the found or created variable.
)DOC",
py::return_value_policy::reference)
//...
示例 3
再来看一个嵌套的例子:
ExecutionStrategy=core.ParallelExecutor.ExecutionStrategy
BuildStrategy=core.ParallelExecutor.BuildStrategy
if self._build_strategy is None:
self._build_strategy=BuildStrategy()
self._build_strategy.is_distribution=_is_pserver_mode(self._program)
上述代码中的 BuildStrategy 类型绑定的又是 c++ 端哪个函数呢?在 pybind 文件中有如下定义:
py::class_ pe(m, "ParallelExecutor");
py::class_ exec_strategy(pe, "ExecutionStrategy", R"DOC(
...
)
记住:paddle 框架代码中,core、core_avx 都是 python 的 module. 除了 pybind、protobuf 之外,paddle 中类似地还有 data_set_py 等文件中也定义了 python 与 c++ 的绑定关系。
另外要注意的是,paddle 中并不是每一个 python 类型、函数或变量都是调用 c++ 端的,paddle 框架中也有许多 python 代码,它们的作用要么调用后端 c++ 代码,要么封装一些 c++ 功能,或者用 python 实现部分功能,以便给用户提供简洁、便捷的 api.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了