python的C扩展如何实现零开销
1 简介
这里简介一个python的新扩展接口,以窥语言优化之路。由于python属于高级语言,因此有众多复杂的语言结构,复杂结构的优点是可用性和安全的提高,可以适配更多的使用场景,其缺点就是导致性能的瓶颈。
HPy 作为一个独立的底层接口,提供了一个用于用 C 语言扩展 Python 的新 API。换句话说,您使用#include <hpy.h> 而不是 .#include <Python.h>
- HPy 的优势
CPython 的零开销:
用 HPy 编写的扩展以相同的方式运行,与CPython的C扩展性能一致。
在 PyPy、GraalPy 等替代实现上更快。
通用的二进制文件:为 HPy 通用 ABI 构建的扩展可以在 CPython、PyPy、GraalPython 等上未经修改地加载。
将旧版 C-API 调用与 HPy API 调用混合的迁移路径。一次即可迁移所有代码,扩展可以编译为通用二进制文件 适用于任何 CPython 版本、PyPy 或 GraalPy。
调试模式:
在调试模式下,您可以轻松识别常见问题,例如 如内存泄漏、对象的生命周期无效、API 的使用无效。HPy 调试模式可以是 在运行时激活,以便在通用二进制文件上为您检测Py_INCREF或Py_DECREF这些错误。
更好的 API:
标准的 Python/C API 显示其年龄。HPy 旨在 克服它的一些限制,更加一致,产生更好的质量 扩展,并使其更难引入 Bug。
可演化性:
正如 PEP 620 中对标准 Python/C API 的简要总结的那样 暴露了大量内部实现细节,这使得 发展 C API。HPy 没有这个问题,因为所有内部的 实现细节是隐藏的。
2 如何实现的零开销
- HPY零开销的说明
HPy 是一种用于 Python 的 C 扩展 API,它被设计为现有 CPython C API 的改进和替代。
HPy 的目标是提供更高效、更可移植的方式来编写 Python 的 C 扩展,并且能够支持不同的 Python 解释器(如 PyPy、CPython 等)。
HPy 在设计上通过多种方式实现了“零开销”(zero overhead)的理念,尤其是在性能方面的优化。
- 零开销设计如何实现
抽象层:
在传统的 CPython C API 中,很多操作是直接与 CPython 的内部数据结构耦合的。
这样虽然方便了扩展编写者,但在其他解释器(如 PyPy 或 GraalPython)上无法获得良好的性能,因为这些解释器的内部实现与 CPython 不同。
HPy 通过引入一层抽象,将扩展开发者与 Python 解释器的内部实现隔离开来。
例如,HPy 引入了 HPyContext 结构,它管理了对 Python 对象和函数的访问。这种抽象允许在不同的解释器上进行优化,而不影响扩展代码的行为。
因此在使用 HPy 的优化模式(如 HPy Universal)时,这些抽象层在 CPython 上没有性能损失,接近原生的 C API 性能。在其他解释器上,它们可以通过优化或即时编译(JIT)进一步提高性能。
更少的引用计数操作:
在 CPython 中,每个 Python 对象都带有引用计数(reference counting),这导致了大量的引用计数操作(如 Py_INCREF 和 Py_DECREF),它们在性能敏感的代码中可能成为瓶颈。
HPy 提供了一种机制,避免了在许多情况下显式地执行引用计数操作。特别是在临时对象的使用中,HPy 可以通过引入句柄(handle)机制,减少不必要的引用计数操作,从而减少开销。
- 零开销的含义:
HPy 的句柄机制可以避免传统 C API 中频繁的引用计数增减,进而在不影响扩展功能的前提下减少开销。
多解释器的支持与优化
传统的 C API 仅仅是为 CPython 设计的,它深度依赖于 CPython 的实现细节。这使得使用同样的扩展代码在 PyPy 等解释器上运行时性能较差。
HPy 通过其抽象和可移植的 API 设计,允许不同的解释器为其自身的实现做优化。例如,PyPy 可以通过其 JIT 编译器大幅度提高 HPy 扩展的性能,而不会受到 CPython 引用计数等实现细节的约束。
- 零开销的含义:
在不同的解释器上,HPy 能够充分利用解释器的优化机制,从而最大限度地提高性能。
对于 CPython,HPy 保持与现有 C API 性能相当的水平,而在 PyPy 等解释器上,HPy 可以做到无额外开销,甚至更高效。
局部句柄的优化:
HPy 引入了句柄系统来表示对 Python 对象的引用。在 HPy 的“局部句柄”(local handle)设计中,句柄的生命周期是局部的,只在特定的作用域内有效,这样就可以确保当不再需要这些句柄时,能够及时释放资源。
传统的 CPython API 中,开发者必须手动管理对象的生命周期,使用不当可能导致内存泄漏或其他问题。而 HPy 的局部句柄机制自动化了这一流程。
- 零开销的含义:
通过局部句柄和生命周期管理,HPy 能够避免冗余的内存操作和复杂的对象管理,这在 CPython 之外的解释器中尤其重要,并且可以进一步提升性能。
高效的错误处理机制:
HPy 提供了比传统 C API 更高效的错误处理机制。在传统的 CPython C API 中,处理 Python 错误通常需要多次调用 Python 的内部函数,这会导致一定的性能开销。
HPy 通过引入专门的错误检查函数(如 HPyErr_Occurred 等),并将其与抽象的 HPyContext 结合,使得错误处理更加简洁和高效。
- 零开销的含义
在错误处理场景中,HPy 可以通过优化 API 调用的次数,减少不必要的性能损失,同时简化扩展代码的编写。
延迟调用与内联优化:
HPy 提供了一些高级机制,可以延迟某些昂贵操作的执行,或者内联一些常见的函数调用。这些技术使得性能敏感的代码可以被进一步优化,而无需直接引入解释器内部的细节。
- 零开销的意义:
通过内联和延迟执行,HPy 在保证代码可读性和移植性的同时,最大限度地提升了性能。
HPy 如何在不同解释器上实现“零开销”。
CPython 上的零开销:
对于 CPython,HPy 提供了一种“零开销模式”(HPy ABI),这个模式下,HPy 的性能接近甚至等同于原生的 C API。换句话说,如果你只针对 CPython 编写扩展,使用 HPy 代替传统 C API 不会带来明显的额外开销。
PyPy 等解释器上的零开销:
对于 PyPy 等基于 JIT(即时编译器)的解释器,HPy 的设计允许这些解释器在运行时生成更高效的代码,而不受限于 CPython 的实现细节。通过与解释器的协同优化,HPy 可以在这些平台上提供比传统 C API 更快的执行速度。
3 总结
小结HPy 实现接近“零开销”性能的方式:
使用更高效的抽象层,使扩展与具体的 Python 解释器实现解耦;
减少了不必要的引用计数操作,尤其是通过句柄机制优化临时对象的管理;
引入局部句柄和高效的错误处理机制,简化了代码编写并提高了执行效率;
在支持多解释器的同时,允许解释器对其自身做进一步的优化。
这些设计和优化使得 HPy 在保持与传统 C API 性能相当的同时,提供了更好的可移植性和潜在的性能提升,尤其是在 PyPy 等其他解释器上。
- 点赞
- 收藏
- 关注作者
评论(0)