我正在使用在Windows 10 + Nvidia Gtx 970上运行的ArrayFire编写测试程序。该程序将使用SGD解算器训练神经网络。因此,主要计算是更新网络参数的迭代。迭代在一个名为step()
的函数中。
该程序执行预期的操作,但它在第一分钟执行速度极慢。以下是该程序的输出。第一列是经过的时间。
ArrayFire v3.5.1 (CUDA, 64-bit Windows, build 0a675e8) Platform: CUDA Toolkit 8, Driver: CUDA Driver Version: 8000 [0] GeForce GTX 970, 4096 MB, CUDA Compute 5.2 time epochs training error 5 0.002 5.6124567 6 0.007 5.5981609 7 0.010 5.3560046 8 0.015 5.2485286 9 0.020 5.1370633 10 0.022 5.1081303 .... 52 0.148 3.2528560 53 0.150 3.2425120 54 0.153 3.2180901 55 0.155 3.2048657 56 0.157 3.1949191 57 0.158 3.1816899 58 0.160 3.1717312 59 0.162 3.1597322 60 0.165 3.1370639 60 0.498 2.1359600 61 0.548 2.0685355 61 0.882 1.7098215 62 0.943 1.6575973 62 1.277 1.4156345 63 1.343 1.3845720 63 1.677 1.1789854 64 1.733 1.1549067 64 2.067 1.0162785 .... 71 4.517 0.4732214 71 4.850 0.4522045 72 4.910 0.4501807 72 5.243 0.4355422 73 5.305 0.4307187
正如你所看到的,在第一分钟,它甚至没有完成1/5的时代。但过了一分钟,它突然加速,在大约4秒钟内完成了一个时代。
分析数据也说明了同样的事情:在第一分钟,函数step()
的平均执行时间约为500毫秒,但在第一分钟后,它下降到6毫秒。
Nvidia visual profiler显示内核在第一分钟内几乎处于空闲状态。
我不知道在第一分钟之后可能导致性能变化的原因。任何帮助表示赞赏。
答案 0 :(得分:2)
ArrayFire在运行时使用JIT编译来融合多个函数调用。因此,当您执行添加或任何其他元素操作时,ArrayFire将创建自定义内核并执行此内核。当您第一次生成此内核时,这会产生一些开销,但这些内核会被缓存,并且不需要编译其他调用。通常,在不需要额外的编译之前,它应该只需要几次迭代。奇怪的是,即使经过60次左右的迭代,内核也很慢。
使用基于内核和内核大小的内部启发式方法评估JIT内核。也许您的应用程序没有以最佳方式触发内核并导致额外的内核编译。您可以通过调用变量上的eval函数来强制进行评估。这是一个人为的例子:
array a = randu(10, 10);
array b = randu(10, 10);
for(int i = 0; i < 100; i++) {
a += b / 4;
b *= i;
eval(a, b);
}
在这里,您将在每次迭代时评估变量a和b的JIT树。这将在每次迭代时重用相同的内核,而不是为不同的迭代次数创建内核。
需要注意的是元素方面,以及像select和shift这样的条件函数是JITed。其他功能在使用之前强制评估其参数。此外,如果您经常评估,您将降低应用程序的性能。