【问题标题】:Mxnet - slow array copy to GPUMxnet - 慢速数组复制到 GPU
【发布时间】:2019-12-07 04:48:51
【问题描述】:

我的问题:我应该如何在 mxnet 中执行快速矩阵乘法?

我的具体问题:数组复制到 GPU 很慢。有什么办法呢?

我创建随机数组,将它们复制到上下文中,然后相乘。

import mxnet as mx
import mxnet.ndarray as nd

from mxnet import profiler

profiler.set_config(aggregate_stats=True)

ctx = mx.cpu()

# create arrays on CPU
profiler.set_state('run')
a = nd.random.uniform(-1, 1, shape=(10000, 10000), ctx=mx.cpu())
b = nd.random.uniform(-1, 1, shape=(10000, 10000), ctx=mx.cpu())
nd.waitall()
profiler.set_state('stop')
print(profiler.dumps(reset=True))

# copy arrays to the context
profiler.set_state('run')
a_ctx = a.as_in_context(ctx)
b_ctx = b.as_in_context(ctx)
nd.waitall()
profiler.set_state('stop')
print(profiler.dumps(reset=True))

# multiply arrays
profiler.set_state('run')
c = nd.dot(a_ctx, b_ctx)
nd.waitall()
profiler.set_state('stop')
print(profiler.dumps(reset=True))

在这段代码中,我在 cpu 上执行所有操作,所以我的时间是(秒):

 0.246
 ~=0
 1.727

当我使用ctx=mx.gpu()时,时间是

 0.247
22.059
 0.828

所以瓶颈是从 CPU 到 GPU 的副本。它只是慢得可笑。有什么办法呢?

这是有关此阶段的准确信息:

Device Storage
=================
Name                          Total Count        Time (ms)    Min Time (ms)    Max Time (ms)    Avg Time (ms)
----                          -----------        ---------    -------------    -------------    -------------
Memory: gpu/0                           2      400000.0000      400000.0000      800000.0000      200000.0000

MXNET_C_API
=================
Name                          Total Count        Time (ms)    Min Time (ms)    Max Time (ms)    Avg Time (ms)
----                          -----------        ---------    -------------    -------------    -------------
MXImperativeInvokeEx                    2       22059.0703           0.0360       22059.0352       11029.5352
MXNDArrayGetShape                       2           0.0030           0.0000           0.0030           0.0015
MXNDArrayWaitAll                        1         105.9830         105.9830         105.9830         105.9830
MXNDArrayCreateEx                       2           0.0150           0.0060           0.0090           0.0075
MXNDArrayGetContext                     2           0.0020           0.0000           0.0020           0.0010
MXNet C API Concurrency                22           0.0000           0.0000           0.0010           0.0005
MXNDArrayGetDType                       2           0.0010           0.0000           0.0010           0.0005
MXNet C API Calls                      11           0.0140           0.0040           0.0140           0.0050

operator
=================
Name                          Total Count        Time (ms)    Min Time (ms)    Max Time (ms)    Avg Time (ms)
----                          -----------        ---------    -------------    -------------    -------------
CopyCPU2GPU                             4         318.4930          53.3060         105.9400          79.6233

如果需要更多信息,请告诉我。

【问题讨论】:

    标签: python performance gpu mxnet


    【解决方案1】:

    您可以从分析结果中看到CopyCPU2GPU 只需要 318 毫秒。 22 秒的额外开销与 GPU 上下文初始化和 malloc 有关。如果您只是在同一脚本中再次运行 GPU 复制代码,您应该会看到更快的结果。您可以像这样修改您的代码:

    # copy arrays to the context
    a_ctx = a.as_in_context(ctx)
    b_ctx = b.as_in_context(ctx)
    nd.waitall()
    profiler.set_state('run')
    a_ctx = a.as_in_context(ctx)
    b_ctx = b.as_in_context(ctx)
    nd.waitall()
    profiler.set_state('stop')
    print(profiler.dumps(reset=True))
    

    要考虑的另一件事是最小化 CPU->GPU 内存副本。例如,在您的具体示例中,您可以在 GPU 而不是 CPU 中创建随机数组:

    a = nd.random.uniform(-1, 1, shape=(10000, 10000), ctx=ctx)
    b = nd.random.uniform(-1, 1, shape=(10000, 10000), ctx=ctx)
    

    CUDA 内存分配/释放需要一些系统同步,这使得它变慢。所有 DL 框架都将内存管理掌握在自己手中,但会创建一个缓冲池来重用先前分配的缓冲区,并且仅在绝对必要时才进行内存分配/释放。例如,tensorflow 默认在单个分配中分配整个 GPU 内存,并在内部将其分配给张量。 MXNet 和 PyTorch 在必要时分配,但在释放时保留在缓冲池中,以便以后重用。

    MXNet/PyTorch 的这种行为意味着在第一次调用创建特定大小的张量时,调用会变慢。但是如果该张量被释放并创建了一个类似大小的新张量,这一次内存来自预分配的缓冲池,而不是使用 cudamalloc。你可以在这里阅读 PyTorch 的内存管理 (https://pytorch.org/docs/stable/notes/cuda.html#cuda-memory-management),它有点类似于 MXNet。

    【讨论】:

    • 谢谢!这似乎确实是一个初始化的事情:即使我一开始只执行nd.random.uniform(-1, 1, shape=(1, 1), ctx=ctx),其他一切都很快。
    • 你知道吗,有没有办法减少这个初始化时间?我希望这是不可能的,但以防万一。
    • 我相信延迟的很大一部分只是唤醒和初始化 NVIDIA 驱动程序,您可以使用 persistence daemon 修复它。其余的延迟与 cuda malloc 有关,每次运行时都必须发生。
    • 谢谢!与 cuda malloc 相关的延迟是什么意思?我在哪里可以读到它?
    • 更新了上面的答案。
    猜你喜欢
    • 2011-02-03
    • 2012-10-31
    • 1970-01-01
    • 2018-03-12
    • 2017-04-02
    • 2022-06-25
    • 2019-04-18
    • 2015-12-27
    • 1970-01-01
    相关资源
    最近更新 更多