Mxnet - 慢速数组复制到 GPU
Mxnet - slow array copy to GPU
我的问题:我应该如何在 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
如果需要更多信息,请告诉我。
您可以从分析结果中看到 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 内存 allocation/deallocation 需要一些系统同步,这会使其变慢。所有 DL 框架都将内存管理掌握在自己手中,但会创建一个缓冲池来重用以前分配的缓冲区,并且仅在绝对必要时才执行内存 allocation/deallocation。例如,tensorflow 默认在一次分配中分配整个 GPU 内存,并在内部将其分配给张量。 MXNet 和 PyTorch 在必要时分配,但在释放时保留在缓冲池中,以便以后重复使用。
MXNet/PyTorch 的这种行为意味着在第一次调用创建特定大小的张量时,调用会变慢。但是如果释放该张量并创建一个类似大小的新张量,这一次内存来自预先分配的缓冲池而不是使用 cudamalloc。你可以在这里阅读 PyTorch 的内存管理(https://pytorch.org/docs/stable/notes/cuda.html#cuda-memory-management),它有点类似于 MXNet。
我的问题:我应该如何在 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
如果需要更多信息,请告诉我。
您可以从分析结果中看到 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 内存 allocation/deallocation 需要一些系统同步,这会使其变慢。所有 DL 框架都将内存管理掌握在自己手中,但会创建一个缓冲池来重用以前分配的缓冲区,并且仅在绝对必要时才执行内存 allocation/deallocation。例如,tensorflow 默认在一次分配中分配整个 GPU 内存,并在内部将其分配给张量。 MXNet 和 PyTorch 在必要时分配,但在释放时保留在缓冲池中,以便以后重复使用。
MXNet/PyTorch 的这种行为意味着在第一次调用创建特定大小的张量时,调用会变慢。但是如果释放该张量并创建一个类似大小的新张量,这一次内存来自预先分配的缓冲池而不是使用 cudamalloc。你可以在这里阅读 PyTorch 的内存管理(https://pytorch.org/docs/stable/notes/cuda.html#cuda-memory-management),它有点类似于 MXNet。