使用PlaidML中的实验设备支持吗?

时间:2019-01-24 20:07:26

标签: macos gpu amd-gpu

我想使用PlaidML加快Mac Pro计算机上的深度学习培训。安装PlaidML之后,我运行“ plaidml-setup”,并收到以下消息:

virtual ToString()

为什么说这是“实验装置”?在Mac Pro上配置PlaidML是否正常?

我应该单击“是”继续设置吗?

编辑: 单击“是”后,系统将显示另一组选项:

检测到多个设备(您可以通过设置PLAIDML_DEVICE_IDS进行覆盖)。 请选择默认设备:

PlaidML Setup (0.3.5)

Thanks for using PlaidML!

Some Notes:
  * Bugs and other issues: https://github.com/plaidml/plaidml
  * Questions: https://stackoverflow.com/questions/tagged/plaidml
  * Say hello: https://groups.google.com/forum/#!forum/plaidml-dev
  * PlaidML is licensed under the GNU AGPLv3

Default Config Devices:
   No devices.

Experimental Config Devices:
   llvm_cpu.0 : CPU (LLVM)
   opencl_amd_amd_radeon_pro_555_compute_engine.0 : AMD AMD Radeon Pro 555 Compute Engine (OpenCL)
   metal_amd_radeon_pro_460.0 : AMD Radeon Pro 460 (Metal)
   opencl_intel_intel(r)_hd_graphics_630.0 : Intel Inc. Intel(R) HD Graphics 630 (OpenCL)
   opencl_cpu.0 : Intel CPU (OpenCL)
   metal_intel(r)_hd_graphics_unknown.0 : Intel(R) HD Graphics Unknown (Metal)

Using experimental devices can cause poor performance, crashes, and other nastiness.

Enable experimental device support? (y,n)[n]:

我应该选择哪个?还是没关系?

2 个答案:

答案 0 :(得分:0)

您正在运行什么版本的macOS?机器是哪一年?我怀疑对于较旧的计算机或macOS <10.14,您没有看到默认值,因为PlaidML遵循了Apple的deprecation of OpenGL/CL in 10.14 in favor of Metal

FWIW,在我的机器上,我看到了类似的选项,除了金属设备列在“默认配置设备”下。

对于每个选项简明(好吧,也许我被带走了)解释:

您可以在CPU或GPU上训练/运行ML模型。 CPU不太适合ML应用程序中常见的矩阵数学流水线。现代CPU具有Streaming SIMD Extensions(SIMD表示 S I n指令 M 多张 D ata)或SSE 。这些允许您执行一组更有限的类似矩阵的操作。例如,当添加两个向量而不是考虑每对元素并一一添加时,SIMD允许您一次添加许多数字。例如,compiling the following code with 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上运行的二进制文件),可以看到两种不同的编译。使用SSE:

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

您不需要深入了解这里发生的事情,但是请注意,SSE二进制文件中以v开头的指令。这些是AVX指令。 zmm0是一个AVX寄存器,可以容纳16个float(AVX-512提供512位寄存器,float是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 call ABI有所了解。简而言之,请忽略8 +[rsp + 64*N]使您a[16*N]成为a[16*(N+1)]的专属用户。 [rsp + (4*64 + 64*N)]跳过所有aa是64个floats,每个大小为4个字节),并使您从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]

其中有八个(N的范围从0到8,不包括在内)。它被包裹在一个循环中,该循环重复8次(8 * 8 = 64,数组长度)。您应该能够猜测这里发生了什么。它与上面的非常相似,除了我们一次处理一个数字而不是16。fldvmovups类似,faddvaddps类似。伪代码看起来更像我们实际编写的代码:

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 is an open standard用于编写数学计算并轻松地在各种硬件(包括GPU)上运行它们。 CPU还可以模拟OpenCL(诚然,速度要慢得多-记住CPU只能做16倍,GPU可以做很多)。

Metal是Apple的OpenGL / CL的替代品。它完成了类似的工作,但特定于macOS(且为封闭源)。

要评论的唯一区别是“Intel®HD Graphics 630”和“ AMD Radeon 460”。您的计算机有两个GPU。第一个是集成显卡。这里的集成意味着您的Intel CPU内嵌了一个小GPU。它的性能不如独立的GPU(一个独立于CPU often found in card form factors for desktops的GPU),但是它可以完成某些强度较低的图形任务(通常更省电)。您的AMD Radeon 460是独立的GPU。这可能是您完成此任务最强大的硬件。

因此,我谨记这些设备将是最快到最慢的设备:

  1. metal_amd_radeon_pro_460.0-离散GPU速度很快,Apple优化了Metal,使其在新Mac上可以很好地运行
  2. opencl_amd_amd_radeon_pro_555_compute_engine.0-它仍然使用离散GPU,但是OpenCL稍有被忽略,现在已在macOS上弃用,因此它可能不会很快。
  3. metal_intel(r)_hd_graphics_unknown.0-集成GPU比CPU更好,Apple对Metal进行了优化
  4. opencl_intel_intel(r)_hd_graphics_630.0-与其他OpenCL相同(除了这是集成的非离散GPU)
  5. llvm_cpu.0-使用CPU,但是LLVM非常擅长编写高效的SIMD代码。
  6. 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的设备只会使您的风扇旋转,并消耗大量的功率(并花费大量时间来完成/训练)。

答案 1 :(得分:0)

是的,您绝对需要实验支持才能使用PlaidML。之后,您要选择

3: metal_amd_radeon_pro_460.0

或任何带有“ metal”和“ radeon”的字样(或NVIVIA,如果您愿意的话)。使用Intel UHD图形(即使您可以选择6 : metal_intel(r)_hd_graphics_unknown.0也没什么意义,因为它不如独立的GPU。

Apple已不赞成使用OpenCL,而推荐使用Apple的Metal框架,最近,OpenCL的格子设置在plaidbench上出现Fail错误。例如,如果您使用opencl驱动程序,则在运行

时,将确保出现Fail错误

plaidbench keras mobilenet

您很有可能会使用Success驱动程序来获得metal