为什么 python 不能矢量化 map() 或列表推导



我对矢量化了解不多,但我有兴趣理解为什么像python这样的语言不能通过库接口在迭代对象上提供矢量化,就像它提供线程支持一样。我知道许多 numpy 方法都是矢量化的,但必须使用 numpy 进行通用计算可能会受到限制。

我目前的理解是,python 无法对函数进行矢量化处理,即使它们与"SIMD"模式匹配。例如,从理论上讲,map()函数的任何列表理解或使用都不应该是可矢量化的,因为它们输出的列表是在输入列表中的独立输入上运行相同函数的结果吗?

以我的初步理解,似乎每当我使用map(),理论上,我应该能够创建一个表示函数的指令集;然后输入中的每个元素只需要通过编译的同一函数运行即可。设计一个提供simd_map(func, iterable)的工具,它试图"及时"编译func,然后从iterable中获取成批的输入,并利用处理器的simd功能通过func()运行这些批次,这在技术上挑战是什么?

谢谢!

map应用的操作是任意 Python 代码。 CPython是一个解释器,而不是一个JIT编译器。

CPython可能有一些预制的 C 函数(作为解释器的一部分提前编译(用于对数组的 SIMD 操作,但它没有 AFAIK。 即便如此,它也必须将提供的func优化为可以执行模式识别的功能,以注意到它正在执行例如a[i] = max(a[i], some_value)

但通常CPython不会这样做;解释器开销是循环数组元素的一个巨大问题。CPython 远不原生标量循环的性能,因此即使没有自动矢量化,也有巨大的增益空间。像 IIRC 慢 200 的因素。 例如,为什么按位运算符比乘法/除法/取模慢?表明某些操作甚至没有小整数的"快速路径",并且该开销足以使&比内部使用硬件除法指令的//慢。

此外,Python 列表不会存储为简单的连续int32_tdouble数组,因此 CPU SIMD 无论如何都效率不高。 这就是为什么 numpy 数组很特别:它们确实像基元类型的 C 数组一样存储值。


(警告:我几乎不了解Python,也不经常使用它。 但我想我知道的足够多,这个答案是正确的:它是一个用 C 编写的解释器,不会即时生成本机机器代码。 唯一可以运行的本机循环是作为解释器的一部分预编译的循环,或者在 NumPy 库中。

您希望矢量化或 JIT 编译使用 numba、pypy 或 cython,但请注意,速度是以牺牲灵活性为代价的。

Numba 是一个 Python 模块,它将为你编译某些函数,但它不支持某些(许多(Python 结构上的多种输入和 barfs。它工作时非常快,但可能很难争论。它也非常针对使用 numpy 数组。

pypy 是 JIT 的 cpython 解释器的完全替代品。它支持整个 python 规范,但不能很好地与扩展集成,因此某些库将无法正常工作。

Cython是Python的扩展,它编译成一个二进制文件,其行为类似于Python模块。但是,它确实要求您使用特殊的语法来利用速度增益,并要求您显式将事物声明为 ctypes 才能真正获得任何优势。

我的建议是: 如果你是纯Python,请使用Pypy。(如果它对你有用,它基本上毫不费力( 如果您需要加快 numpy 没有好方法的数值计算,请使用 numba。 如果您需要速度而其他 2 个不起作用,请使用 cython。

最新更新