前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >HPy - 为 Python 扩展提供更优秀的 C API

HPy - 为 Python 扩展提供更优秀的 C API

作者头像
niqin.com
发布2022-06-30 16:44:29
5800
发布2022-06-30 16:44:29
举报
文章被收录于专栏:Rust 生态与实践

官方的 Python/C API 是针对 CPython 的实现的:公开了许多内部细节,使得 API 实现难度较大;而且,如果要为 PyPy、GraalPython、Jython、IronPython 等替代实现开发 API,更是各类问题多多。最近发现了一个性能更好的开源 Python 扩展,HPy。简单试用后,感觉值得推荐。

HPy 介绍

HPy 提供了一个新的 API,以用 C 扩展 Python,有零开销、更快速、方便调试、通用的二进制文件(不用任何修改,可在 CPython、PyPy、GraalPython 等解释器上直接加载),以及更加符合时代标准等优点。换句话说,如果您做过 C 中嵌入 Python 的开发,必定熟悉这行代码:

代码语言:javascript
复制
#include <Python.h>

现在,如果你使用 HPy,则可以替换为:

代码语言:javascript
复制


#include <hpy.h>

HPy 优点

但是,虽然直接介绍了代码,但为什么要用 HPy?作为一个 2019 年才诞生的项目,HPy 设计挺优秀,官方言其具有如下优点:

  • CPython 之上的零开销:用 HPy 编写的扩展相比于官方 CPython,未有任何额外开销。
  • 在 PyPy、GraalPython(目前比较热火的 Python3 实现,性能提升极大,但对 C 扩展) 等 CPython 替代实现上,运行更快。
  • 更友好的调试模式:HPy 调试模式可以自动检测诸多错误。在 HPy 的调试模式下,您可以很容易地识别常见问题,如内存泄漏、对象的无效生存期、API 的无效使用等。
  • 通用的二进制文件:为 HPy 通用 ABI 构建的扩展,可以在 CPython、PyPy、GraalPython 之上不用任何修改,直接加载运行。
  • 更好的 API:标准的 Python/C API 具有其产生时代的特性限制,而 HPy 的设计可以克服一些限制。让扩展 API 更加一致,更易写易读,并且可使 bug 更易于暴露。

HPy 类库

目前,已经有 ultrajson-hpy(ultrajson 是 Python 生态中极速的 JSON 编解码器)、piconump(类似于 ndarray),以及 numpy-hpy(大名鼎鼎的 numpy)等模块可与 HPy 兼容试用。可以看到,还处于开发初期,但科学计算、数据解析已经可以尝试了。

HPy 示例

概念和优点,都是官方的说法,我们实际代码操练一下,看看试用体验,以及 HPy 是否具有优势。示例很简单,即实现一个 add 运算的扩展。

首先,我们用标准 Python/C 扩展方式实现

命名文件为 hello_old.c

代码语言:javascript
复制
#include <Python.h>

static PyObject* add(PyObject* self, PyObject* args) {
    long a, b;
    if (!PyArg_ParseTuple(args, "ll", &a, &b))
        return NULL;
    return PyLong_FromLong(a+b);
}

static PyMethodDef HelloMethods[] = {
    {"add", (PyCFunction)add, METH_VARARGS, "两个整数相加"},
    {NULL, NULL, 0, NULL}
};

static struct PyModuleDef moduledef = {
    PyModuleDef_HEAD_INIT,
    "hello_old",
    "使用标准 Python/C API",
    -1,
    HelloMethods,
};

PyMODINIT_FUNC
PyInit_hello_old(void) {
    return PyModule_Create(&moduledef);
}

我们通过 setup.py 编译扩展文件:

代码语言:javascript
复制
from setuptools import setup, Extension
setup(
    name="hello",
    ext_modules = [
        Extension('hello_old', ['hello_old.c']),
    ],
)

最后,我们运行 setup.py 并使用此扩展实现:

代码语言:javascript
复制
$ python setup.py build_ext --inplace
...

$ python 
>>> import hello_old
>>> hello_old.add(10, 20)
30

然后,我们用 HPy 方式实现

命名文件为 hello_new.c

代码语言:javascript
复制
#include <hpy.h>

HPyDef_METH(add, "add", add_impl, HPyFunc_VARARGS,
            .doc = "两个整数相加");

static HPy add_impl(HPyContext ctx, HPy self, HPy *args, HPy_ssize_t nargs) {
    long a, b;
    if (!HPyArg_Parse(ctx, NULL, args, nargs, "ll", &a, &b))
        return HPy_NULL;
    return HPyLong_FromLong(ctx, a+b);
}

static HPyDef *hello_defines[] = {
    &add,
    NULL
};

static HPyModuleDef moduledef = {
    HPyModuleDef_HEAD_INIT,
    .m_name = "hello_new",
    .m_doc = "使用 HPy API",
    .m_size = -1,
    .defines = hello_defines,
};

HPy_MODINIT(hello_new)
static HPy init_hello_new_impl(HPyContext ctx) {
    return HPyModule_Create(ctx, &moduledef);
}

同样,我们需要通过 setup.py 编译扩展文件。我们在 setup.py 加入 hpy_ext_modules 定义项,一次将两种方式实现的扩展全部编译:

代码语言:javascript
复制
from setuptools import setup, Extension
setup(
    name="hello",
    ext_modules = [
        Extension('hello_old', ['hello_old.c']),
    ],
    hpy_ext_modules = [
        Extension('hello_new', ['hello_new.c']),
    ],
    setup_requires=['hpy.devel'],
)

HPy 扩展实现方式的编译,需要将参数 --hpy-abi=universal 传递给 setup.py

代码语言:javascript
复制
$ python setup.py --hpy-abi=universal build_ext --inplace
$ ls -1 *.so
hello_new.hpy.so
hello_old.cpython-38-x86_64-linux-gnu.so

我们可以看到,是兼容 32位和 64 位处理器的。好,我们看看能不能跑的通:

代码语言:javascript
复制


$ python
>>> import hello_old, hello_new
>>> hello_old.add(10, 20)
30
>>> hello_new.add(30, 40)
70
>>> hello_new
<module 'hello_new' from '/.../hello-hpy/hello_new.py'>
>>> hello_new.__file__
'/.../hello-hpy/hello_new.hpy.so'

简单试用就是以上所述,总体说来体验还行。但是上面提到,HPy 还处于开发初期,建议使用 github 仓库版本。

本次体验到此结束,以后在使用过程中,如果有新的推荐点,笔者再分享。

谢谢您的阅读!

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-03-31,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Rust 生态与实践 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • HPy 介绍
  • HPy 优点
  • HPy 类库
  • HPy 示例
    • 首先,我们用标准 Python/C 扩展方式实现
      • 然后,我们用 HPy 方式实现
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档