为什么 M1 Max 上的 Python native 比旧的 Intel i5 上的 Python 慢得多?

Why Python native on M1 Max is greatly slower than Python on old Intel i5?

我刚拿到带有 M1 Max 芯片的新 MacBook Pro,正在设置 Python。我已经尝试了几种组合设置来测试速度 - 现在我很困惑。先把我的问题放在这里:

支持我的问题的证据如下:


以下是我试过的设置:

1. Python 由

安装

2。

安装的 Numpy
conda install -c apple tensorflow-deps
python -m pip install tensorflow-macos
python -m pip install tensorflow-metal

3。 运行 来自


这里是测试代码:

import time
import numpy as np
np.random.seed(42)
a = np.random.uniform(size=(300, 300))
runtimes = 10

timecosts = []
for _ in range(runtimes):
    s_time = time.time()
    for i in range(100):
        a += 1
        np.linalg.svd(a)
    timecosts.append(time.time() - s_time)

print(f'mean of {runtimes} runs: {np.mean(timecosts):.5f}s')

结果如下:

+-----------------------------------+-----------------------+--------------------+
|   Python installed by (run on)→   | Miniforge (native M1) | Anaconda (Rosseta) |
+----------------------+------------+------------+----------+----------+---------+
| Numpy installed by ↓ | Run from → |  Terminal  |  PyCharm | Terminal | PyCharm |
+----------------------+------------+------------+----------+----------+---------+
|          Apple Tensorflow         |   4.19151  |  4.86248 |     /    |    /    |
+-----------------------------------+------------+----------+----------+---------+
|        conda install numpy        |   4.29386  |  4.98370 |  4.10029 | 4.99271 |
+-----------------------------------+------------+----------+----------+---------+

这很慢。为了比较,

这里是CPU信息详情:

$ sysctl -a | grep -e brand_string -e cpu.core_count
machdep.cpu.brand_string: Intel(R) Core(TM) i5-6360U CPU @ 2.00GHz
machdep.cpu.core_count: 2
% sysctl -a | grep -e brand_string -e cpu.core_count
machdep.cpu.brand_string: Apple M1 Max
machdep.cpu.core_count: 10

我严格按照教程中的说明进行操作 - 但为什么会发生所有这些情况?是我的安装缺陷,还是M1 Max芯片的问题?由于我的工作严重依赖本地 运行s,因此本地速度对我来说非常重要。对可能的解决方案或您自己设备上的任何数据点的任何建议将不胜感激:)

可能的原因:不同的 BLAS 库

由于基准是 运行 线性代数例程,这里可能测试的是 BLAS 实现。 osx-64 平台的默认 Anaconda 发行版将随英特尔的 MKL 实现一起提供; osx-arm64 平台只有通用的 Netlib BLAS 和 OpenBLAS 实现选项。

对于我(配备 Intel i9 的 MacOS),我得到以下基准测试结果:

BLAS Implmentation Mean Timing (s)
mkl 0.95932
blis 1.72059
openblas 2.17023
netlib 5.72782

因此,我怀疑旧的 MBP 安装了 MKL,而 M1 系统正在安装 Netlib 或 OpenBLAS。也许尝试弄清楚 Netlib 或 OpenBLAS 在 M1 上是否更快,并保持更快的那个。


指定 BLAS 实现

这里具体是我测试的不同环境:

# MKL
conda create -n np_mkl python=3.9 numpy blas=*=*mkl*

# BLIS
conda create -n np_blis python=3.9 numpy blas=*=*blis*

# OpenBLAS
conda create -n np_openblas python=3.9 numpy blas=*=*openblas*

# Netlib
conda create -n np_netlib python=3.9 numpy blas=*=*netlib*

和 运行 基准脚本 (so-np-bench.py) 和

conda run -n np_mkl python so-np-bench.py

# etc.

2022 年 3 月 28 日更新:请参阅下面@AndrejHribernik 的评论。


如何在M1 Max上安装numpy,加速性能最强(苹果的vecLib)?这是截至 2021 年 12 月 6 日的答案。


步骤

我。安装 miniforge

所以你的 Python 是 运行 原生的 arm64,不是通过 Rosseta 翻译的。

  1. 下载Miniforge3-MacOSX-arm64.sh,然后
  2. 运行脚本,然后再打开一个shell
$ bash Miniforge3-MacOSX-arm64.sh
  1. 创建环境(这里我用namenp_veclib
$ conda create -n np_veclib python=3.9
$ conda activate np_veclib

二.使用指定为 vecLib

的 BLAS 接口安装 Numpy
  1. 要编译numpy,首先需要安装cythonpybind11
$ conda install cython pybind11
  1. 通过(感谢@Marijn 的 answer)编译 numpy - 不要使用 conda install
$ pip install --no-binary :all: --no-use-pep517 numpy
  1. 2. 的替代方法是从源代码构建
$ git clone https://github.com/numpy/numpy
$ cd numpy
$ cp site.cfg.example site.cfg
$ nano site.cfg

编辑复制的site.cfg:添加以下行:

[accelerate]
libraries = Accelerate, vecLib

然后构建并安装:

$ NPY_LAPACK_ORDER=accelerate python setup.py build
$ python setup.py install
  1. 在 2 或 3 之后,现在测试 numpy 是否正在使用 vecLib:
>>> import numpy
>>> numpy.show_config()

然后,像/System/Library/Frameworks/vecLib.framework/Headers这样的信息应该被打印出来。

三。使用 conda

进一步安装其他包

让conda识别pip安装的包

conda config --set pip_interop_enabled true

必须这样做,否则,例如conda install pandas,然后 numpy 将在 The following packages will be installed 列表中并再次安装。但是新安装的来自 conda-forge 频道,速度很慢。


与其他安装的比较:

1。竞争对手:

除了上面那个最优的,我也尝试了其他几种安装方式

  • 一个。 np_default: conda create -n np_default python=3.9 numpy
  • 乙。 np_openblas: conda create -n np_openblas python=3.9 numpy blas=*=*openblas*
  • C。 np_netlib: conda create -n np_netlib python=3.9 numpy blas=*=*netlib*

以上ABC选项直接从conda-forge频道安装。 numpy.show_config() 将显示相同的结果。要查看差异,请按 conda list 检查 - 例如openblas 软件包安装在 B 中。请注意 mklblis 在 arm64 上不支持。

  • D. np_openblas_source:首先通过brew install openblas安装openblas。然后将 [openblas] 路径 /opt/homebrew/opt/openblas 添加到 site.cfg 并从源代码构建 Numpy。
  • M1i9–9880H 在这个 post.
  • 我的旧 i5-6360U 2 核 MacBook Pro 2016 13 英寸

2。基准:

这里我使用了两个基准:

  1. mysvd.py:我的SVD分解
import time
import numpy as np
np.random.seed(42)
a = np.random.uniform(size=(300, 300))
runtimes = 10

timecosts = []
for _ in range(runtimes):
    s_time = time.time()
    for i in range(100):
        a += 1
        np.linalg.svd(a)
    timecosts.append(time.time() - s_time)

print(f'mean of {runtimes} runs: {np.mean(timecosts):.5f}s')
  1. dario.pyDario Radečić 在上面 post 的基准测试脚本。

3。结果:

+-------+-----------+------------+-------------+-----------+--------------------+----+----------+----------+
|  sec  | np_veclib | np_default | np_openblas | np_netlib | np_openblas_source | M1 | i9–9880H | i5-6360U |
+-------+-----------+------------+-------------+-----------+--------------------+----+----------+----------+
| mysvd |  1.02300  |   4.29386  |   4.13854   |  4.75812  |      12.57879      |  / |     /    |  2.39917 |
+-------+-----------+------------+-------------+-----------+--------------------+----+----------+----------+
| dario |     21    |     41     |      39     |    323    |         40         | 33 |    23    |    78    |
+-------+-----------+------------+-------------+-----------+--------------------+----+----------+----------+