您运行的是哪个版本的 macOS?机器是哪年的?我怀疑对于较旧的机器或 macOS < 10.14,您看不到默认设置,因为 PlaidML 已经注意到 Apple在 10.14 中弃用 OpenGL/CL 以支持 Metal。
FWIW,在我的机器上,我看到了类似的选项,除了金属设备列在“默认配置设备”下。
至于这些选项中的每一个(好吧,也许我被带走了)解释说:
您可以在 CPU 或 GPU 上训练/运行 ML 模型。CPU 不太适合 ML 应用程序中常见的矩阵数学流水线。现代 CPU 具有流式SIMD扩展(SIMD 表示单指令多数据)或 SSE 。这些允许您执行一组更有限的类似矩阵的操作。例如,当添加两个向量而不是考虑每对元素并一个接一个地添加它们时,SIMD 允许您一次添加多个数字。例如,使用以下代码编译:clang -O3 -march=native
#include <array>
auto add(std::array<float, 64> a, std::array<float, 64> b) {
std::array<float, 64> output;
for (size_t i = 0; i < 64; i++) {
output[i] = a[i] + b[i];
}
return output;
}
根据我们是否通过,我们可以看到两种不同的编译-mno-sse
(您可能会猜到,这会生成一个在没有 SSE 的 CPU 上工作的二进制文件)。与上交所:
add(std::array<float, 64ul>, std::array<float, 64ul>):
mov rax, rdi
vmovups zmm0, zmmword ptr [rsp + 8]
vaddps zmm0, zmm0, zmmword ptr [rsp + 264]
vmovups zmmword ptr [rdi], zmm0
vmovups zmm0, zmmword ptr [rsp + 72]
vaddps zmm0, zmm0, zmmword ptr [rsp + 328]
vmovups zmmword ptr [rdi + 64], zmm0
vmovups zmm0, zmmword ptr [rsp + 136]
vaddps zmm0, zmm0, zmmword ptr [rsp + 392]
vmovups zmmword ptr [rdi + 128], zmm0
vmovups zmm0, zmmword ptr [rsp + 200]
vaddps zmm0, zmm0, zmmword ptr [rsp + 456]
vmovups zmmword ptr [rdi + 192], zmm0
vzeroupper
ret
没有 SSE:
add(std::array<float, 64ul>, std::array<float, 64ul>):
mov rax, rdi
lea rcx, [rsp + 264]
lea rdx, [rsp + 8]
xor esi, esi
.LBB0_1:
fld dword ptr [rdx + 4*rsi]
fadd dword ptr [rcx + 4*rsi]
fstp dword ptr [rax + 4*rsi]
fld dword ptr [rdx + 4*rsi + 4]
fadd dword ptr [rcx + 4*rsi + 4]
fstp dword ptr [rax + 4*rsi + 4]
fld dword ptr [rdx + 4*rsi + 8]
fadd dword ptr [rcx + 4*rsi + 8]
fstp dword ptr [rax + 4*rsi + 8]
fld dword ptr [rdx + 4*rsi + 12]
fadd dword ptr [rcx + 4*rsi + 12]
fstp dword ptr [rax + 4*rsi + 12]
fld dword ptr [rdx + 4*rsi + 16]
fadd dword ptr [rcx + 4*rsi + 16]
fstp dword ptr [rax + 4*rsi + 16]
fld dword ptr [rdx + 4*rsi + 20]
fadd dword ptr [rcx + 4*rsi + 20]
fstp dword ptr [rax + 4*rsi + 20]
fld dword ptr [rdx + 4*rsi + 24]
fadd dword ptr [rcx + 4*rsi + 24]
fstp dword ptr [rax + 4*rsi + 24]
fld dword ptr [rdx + 4*rsi + 28]
fadd dword ptr [rcx + 4*rsi + 28]
fstp dword ptr [rax + 4*rsi + 28]
add rsi, 8
cmp rsi, 64
jne .LBB0_1
ret
您不需要深入了解这里发生了什么,但请注意以v
SSE 二进制文件开头的指令。这些是AVX指令。并且zmm0
是一个可以容纳16s的AVX寄存器float
(AVX-512提供512位寄存器,float
s是32位)。LLVM 利用了这一点,而不是逐个元素地添加数字(就像我们在原始代码中写的那样),它一次只添加 16 个。您一个接一个地看到以下程序集的 4 个变体(注意括号内的数学):
vmovups zmm0, zmmword ptr [rsp + (8 + 64*N)]
vaddps zmm0, zmm0, zmmword ptr [rsp + (8 + 4*64 + 64*N)]
vmovups zmmword ptr [rdi + (64*N)], zmm0
这里的数学需要一些关于System V 调用 ABI的知识。简单地说,忽略8 +
. [rsp + 64*N]
让你a[16*N]
,a[16*(N+1)]
独家。[rsp + (4*64 + 64*N)]
跳过所有a
(每个大小为 4 个字节a
的 64个)并让您排他。并且是,排他的。所以这有效地转换为以下伪代码:floats
b[16*N]
b[16*(N+1)]
[rdi + (64*N)]
output[16*N]
output[16*(N+1)]
std::array<float, 16> temp = {a[16*N], a[16*N+1], ..., a[16*N+16]};
temp += {b[16*N], b[16*N+1], ..., b[16*N+16]};
{output[16*n], output[16*N+1], ..., output[16*N+16]} = temp;
确实,我们看到 AVX-512(SIMD 的扩展)允许我们一次以 16 个数字为一组进行加法运算。将此快速与-mno-sse
版本进行比较。应该清楚的是,它正在做更多的工作。再次,我们有一个指令模式(虽然这次它是在一个循环中):
fld dword ptr [rdx + 4*rsi + 4*N]
fadd dword ptr [rcx + 4*rsi + 4*N]
fstp dword ptr [rax + 4*rsi + 4*N]
其中有 8 个(N
范围从 0 到 8,不包含)。这被包裹在一个循环中,该循环重复 8 次(8 * 8 = 64,数组长度)。你应该能够猜到这里发生了什么。它与上面非常相似,只是我们一次处理一个数字而不是 16。fld
类似于vmovups
,fadd
类似于vaddps
。这个伪代码看起来更像我们实际编写的代码:
float temp = a[loop_num*8 + N];
temp += b[loop_num*8 + N];
output[loop_num*8] = temp;
希望直观的是,一次做 16 件事要比一次做 1 件事更有效率。
还有一些花哨的线性代数框架,比如blas,它可以压缩几乎所有你可以从 CPU 中获得的数学性能。
GPU 的工作方式略有不同。粗略的简化是将 GPU 视为具有大量 SIMD 指令的设备(特别适用于浮点运算)。因此,与其一次工作 16 个,不如想象一下只是将整个图像交给它,并且在一次操作中它可以对其应用像素过滤器(例如更改亮度或饱和度)。
那么这个切线与任何事情有什么关系呢?
AVX 指令使得在 CPU 上运行一些代码变得有些合理。您在其中看到的所有选项_cpu
都只能在 CPU 上运行。llvm_cpu
可能会使用与上述类似的技术clang
(clang
在幕后使用 llvm)来编译运行/训练 ML 模型所需的所有数学。鉴于现代 CPU 是多核的,这可能与16 * number_of_cores
加速一样多。
OpenCL 是用于编写数学计算并在各种硬件(包括 GPU)上轻松运行它们的开放标准。OpenCL 也可以由 CPU 模拟(诚然,速度要慢得多——记住 CPU 只能做 16 倍,GPU 可以做更多)。
Metal是 Apple 对 OpenGL/CL 的替代品。它完成了类似的事情,但是是 macOS 特定的(并且是封闭源代码)。
唯一需要评论的区别是“Intel(R) HD Graphics 630”与“AMD Radeon 460”。您的计算机有两个 GPU。第一个是集成显卡。这里的集成意味着您的英特尔 CPU 内部嵌入了一个小 GPU。它的性能不如独立 GPU(与 CPU 分离,通常在台式机的卡外形尺寸中找到),但它可以完成某些不太密集的图形任务(并且通常更节能)。您的 AMD Radeon 460 是独立 GPU。它可能是您为这项任务所拥有的最强大的硬件。
因此,考虑到这一点,我预测这些设备将从最快到最慢:
metal_amd_radeon_pro_460.0
- 独立 GPU 速度很快,Apple 优化了 Metal,使其在新 Mac 上运行良好
opencl_amd_amd_radeon_pro_555_compute_engine.0
- 这仍然使用独立的 GPU,但 OpenCL 被忽略了一点,现在在 macOS 上已弃用,所以它可能不会那么快
metal_intel(r)_hd_graphics_unknown.0
- 集成 GPU 优于 CPU,Apple 优化了 Metal
opencl_intel_intel(r)_hd_graphics_630.0
- 关于其他 OpenCL 的情况同上(除了这是一个集成的非独立 GPU)
llvm_cpu.0
- 这使用 CPU,但 LLVM 非常擅长编写高效的 SIMD 代码。
opencl_cpu.0
- 这模拟 (2) 和 (4),但使用 CPU 会慢得多。此外,它可能没有 LLVM 用来输出高效 SIMD 代码的所有花哨算法。
但这一切都是猜测,您可以通过pip install plaidbench plaidml-keras keras
. 对于每个设备,运行plainml-setup
(选择该设备)然后运行plainbench keras mobilenet
(或任何其他基准测试)。以下是我在我的机器上看到的结果:
| device | exeuction (s) | fps | correctness |
|------------------------------|---------------|--------|-------------|
| Metal AMD Radeon Pro 560 | 9.009 | 112.53 | PASS |
| OpenCL AMD Radeon Pro 560 | 18.339 | 93.29 | PASS |
| OpenCL Intel HD Graphics 630 | 23.204 | 60.18 | FAIL |
| Metal Intel HD Graphics 630 | 24.809 | 41.27 | PASS |
| LLVM CPU | 66.072 | 16.82 | PASS |
| OpenCL CPU Emulation | 155.639 | 6.71 | FAIL |
我已将设备重命名为更漂亮的名称,但它们与标识符的映射应该是显而易见的。
执行时间是运行模型所花费的时间(越低越好),而 FPS 是执行所达到的 FPS(越高越好)。
我们注意到订单通常是我们所期望的。离散 GPU 比集成 GPU 快,集成 GPU 比 CPU 快。需要指出的重要一点是,集成 GPU 和 CPU 仿真上的 OpenCL 未能通过正确性检查。CPU 仿真仅下降了约 7%,但集成 GPU 下降了约 77%。您可能只想选择一个在您的机器上通过正确性检查的设备(有可能——但不能保证——如果它未能通过该检查,后端或设备本身就是错误的)。
tl;dr 使用金属 + 独立 GPU (AMD Radeon)。它是您可用的最快的设备。使用任何基于 CPU 的东西只会让你的粉丝加速并消耗大量电力(并且需要永远完成/训练)。