首先在 [numpy] simd 上进行 SO 搜索会找到许多答案。
一个 https://stackoverflow.com/a/45798012/901925 找到了一个 src/umath/simd.inc.src 文件。它将自己描述为“当前包含基于 amd64、x32 或非通用版本 (CFLAGS=-march=...) 构建的 sse2 函数”。这是低级代码,根据构建,可能会被合并到numpy 二进制文件中。这不是您作为 Python 级别的用户能够检测或控制的东西。
最近有很多点击的问题是How is numpy so fast?。
但答案主要涉及c++ 比较代码及其内存使用。所以它真的没有解决numpy的用法。
但出于您的目的,真正的问题是该操作是否使用已编译的 numpy 方法,或者是否使用 Python 级别的迭代和对象。
首先,您了解numpy 数组是如何存储的,它与列表有何不同?在不知道这种差异的情况下,许多关于 numpy 速度的讨论将难以理解。作为一般规则,使用数组就好像它们是列表一样,使用迭代和列表推导会更慢。在numpy 函数中使用列表会导致速度下降,因为列表必须首先转换为数组。
另外object dtype 数组将它们的数据存储在对象引用中,因此它们的计算以列表理解的速度运行。快速 numpy 方法仅适用于数字 dtypes,可以使用 c 原生类型编译的那些 - 浮点数、整数等。
至于你的示例表达式
A + B
像这样的操作符被实现为ufunc,它充分利用了数组数据存储。由于它可以处理多维数组,并且使用broadcasting,因此底层代码非常复杂,不是您或我可以轻松阅读的内容。在某些较低级别上,它可能会利用处理器兑现和特殊指令,但这更多是c 代码宏和编译器选项的功能。
np.sum(A, axis=0)
sum实际上是一个np.add.reduce,所以上面的cmets适用。但是对于列表来说,原生 python sum 一点也不逊色。
np.where
np.nonzero 是更简单的编译函数之一。它首先使用np.count_nonzero 来查找有多少非零元素。它使用它来分配它将返回的数组元组,然后再次遍历参数以填充索引。它相当快,因为它在干净的c 代码中循环遍历数组的数据缓冲区。
np.apply_along_axis
这很慢,即使与列表推导相比也是如此。它必须为每个一维数组调用一次你的函数。这是对 python 函数的重复调用,它比实际的迭代方法花费的时间最多。像这样的函数不会编译你的函数,所以在某种程度上,它们只是 Python 级迭代的覆盖。 python代码可供学习。
np.einsum
这是一个复杂的函数,根据输入以不同的方式工作。对于更简单的情况,它只使用np.matmul/@,这可能非常快,具体取决于您拥有的BLAS 之类的库。多年前,当我为它编写补丁时,einsum 在cython 中使用了nditer。
np.vstack
这是np.concatenate 的封面。 python代码很容易阅读。 concatenate 已编译。但是应该正确使用这些函数,并使用完整的数组列表。在循环中重复使用比列表append更糟糕。