Accelerate 框架在 Mac M1 上仅使用一个内核

时间:2021-05-18 13:42:08

标签: c macos lapack apple-m1 accelerate-framework

下面的 C 程序 (dgesv_ex.c)

#include <stdlib.h>
#include <stdio.h>

/* DGESV prototype */
extern void dgesv( int* n, int* nrhs, double* a, int* lda, int* ipiv,
                double* b, int* ldb, int* info );

/* Main program */
int main() {
        /* Locals */
        int n = 10000, info;
        /* Local arrays */
        /* Initialization */
        double *a = malloc(n*n*sizeof(double));
        double *b = malloc(n*n*sizeof(double));
        int *ipiv = malloc(n*sizeof(int));
        for (int i = 0; i < n*n; i++ )
        {
                a[i] = ((double) rand()) / ((double) RAND_MAX) - 0.5;
        }
        for(int i=0;i<n*n;i++)
        {
            b[i] = ((double) rand()) / ((double) RAND_MAX) - 0.5;
        }

        /* Solve the equations A*X = B */
        dgesv( &n, &n, a, &n, ipiv, b, &n, &info );
        free(a);
        free(b);
        free(ipiv);
        exit( 0 );
} /* End of DGESV Example */

使用命令在 Mac mini M1 上编译

clang -o dgesv_ex dgesv_ex.c -framework accelerate

仅使用处理器的一个核心(活动监视器也显示)

me@macmini-M1 ~ % time ./dgesv_ex 
./dgesv_ex  35,54s user 0,27s system 100% cpu 35,758 total

我检查了二进制文件的类型是否正确:

me@macmini-M1 ~  % lipo -info dgesv
Non-fat file: dgesv is architecture: arm64

作为比较,在我的英特尔 MacBook Pro 上,我得到以下输出:

me@macbook-intel ˜ % time ./dgesv_ex
./dgesv_ex  142.69s user 0,51s system 718% cpu 19.925 total

这是一个已知问题吗?也许是编译标志或其他?

1 个答案:

答案 0 :(得分:6)

Accelerate 使用 M1 的 AMX 协处理器来执行其矩阵运算,它不使用处理器中的典型路径。因此,计算 CPU 使用率没有多大意义;在我看来,当 CPU 内核向 AMX 协处理器提交指令时,它被视为在等待协处理器完成其工作时保持 100% 的利用率。

通过并行运行 dgesv 基准测试的多个实例,并观察运行时间增加两倍,我们可以看到这一点的证据,但 CPU 监视器仅显示使用 100% 一个内核的两个进程:

clang -o dgesv_accelerate dgesv_ex.c -framework Accelerate
$ time ./dgesv_accelerate

real    0m36.563s
user    0m36.357s
sys     0m0.251s

$ ./dgesv_accelerate & ./dgesv_accelerate & time wait
[1] 6333
[2] 6334
[1]-  Done                    ./dgesv_accelerate
[2]+  Done                    ./dgesv_accelerate

real    0m59.435s
user    1m57.821s
sys     0m0.638s

这意味着每个dgesv_accelerate进程都在消耗一个共享资源;一个我们不太了解的。我很好奇这些 dgesv_accelerate 进程在等待 AMX 协处理器完成其任务时是否真的在消耗计算资源,因此我将您示例的另一个版本与 OpenBLAS 联系起来,这就是我们用作 Julia language 中的默认 BLAS 后端。我正在使用托管在 this gist 中的代码,它有一个方便的 Makefile 用于下载 OpenBLAS(及其附带的编译器支持库,例如 libgfortranlibgcc)并编译所有内容并运行计时测试。< /p>

请注意,由于 M1 是 big.LITTLE 架构,因此我们通常希望避免创建过多线程,以免在“效率”内核上安排大型 BLAS 操作;我们主要想坚持只使用“性能”核心。您可以通过打开活动监视器的“CPU 历史记录”图大致了解正在使用的内容。这是一个展示正常系统负载的示例,然后是运行 OPENBLAS_NUM_THREADS=4 ./dgesv_openblas,然后是 OPENBLAS_NUM_THREADS=8 ./dgesv_openblas。请注意,在四线程示例中,工作是如何正确安排到性能核心上的,而效率核心可以自由地继续做一些事情,例如在我输入此段落时呈现此 StackOverflow 网页,以及在后台播放音乐。然而,一旦我使用 8 个线程运行,音乐就开始跳动,网页开始滞后,效率核心被它们并非设计用于执行的工作负载所淹没。所有这一切,时间甚至根本没有改善:

Activity Monitor CPU History graph

$ OPENBLAS_NUM_THREADS=4 time ./dgesv_openblas 
       18.76 real        69.67 user         0.73 sys
$ OPENBLAS_NUM_THREADS=8 time ./dgesv_openblas 
       17.49 real       100.89 user         5.63 sys

既然我们在M1上有两种不同的消耗计算资源的方式,我们可以比较一下,看看它们是否相互干扰;例如如果我启动您示例的“加速”驱动的实例,它会减慢 OpenBLAS 驱动的实例的速度吗?

$ OPENBLAS_NUM_THREADS=4 time ./dgesv_openblas
       18.86 real        70.87 user         0.58 sys

$ ./dgesv_accelerate & OPENBLAS_NUM_THREADS=4 time ./dgesv_openblas
       24.28 real        89.84 user         0.71 sys

因此,遗憾的是,CPU 使用率似乎是真实的,并且它消耗了 OpenBLAS 版本想要使用的资源。 Accelerate 版本也变慢了一点,但幅度不大。

总而言之,Accelerate-heavy 进程的 CPU 使用率数字具有误导性,但并非完全如此。 Accelerate 似乎确实在使用 CPU 资源,但存在多个 Accelerate 进程必须争夺的隐藏共享资源。使用诸如 OpenBLAS 之类的非 AMX 库会产生更熟悉的性能(并且在这种情况下运行时更好,尽管情况并非总是如此)。处理器的真正“最佳”使用可能是在 3 个 Firestorm 内核和一个 Accelerate 进程上运行 OpenBLAS 之类的东西:

$ OPENBLAS_NUM_THREADS=3 time ./dgesv_openblas
       23.77 real        68.25 user         0.32 sys
$ ./dgesv_accelerate & OPENBLAS_NUM_THREADS=3 time ./dgesv_openblas
       28.53 real        81.63 user         0.40 sys

这一次解决了两个问题,一个需要 28.5 秒,一个需要 42.5 秒(我只是移动了 time 来测量 dgesv_accelerate)。这将 3 核 OpenBLAS 的速度降低了约 20%,将 Accelerate 的速度降低了约 13%,因此假设您有一个应用程序需要解决很长的这些问题队列,您可以将它们提供给这两个引擎并在与适度的开销并行。

我并不是说这些配置实际上是最佳的,只是探索了这个特定工作负载的相对开销是多少,因为我很好奇。 :) 可能有办法改善这一点,而这一切都可能随着新的 Apple Silicon 处理器而发生巨大变化。