我已经在RenderScript中实现了一个小的CNN,并想在不同的硬件上分析性能。在我的Nexus 7时代是合理的,但在NVIDIA盾,他们没有。
CNN (LeNet)是在一个队列中的9个层中实现的,计算是按顺序进行的。每一层都是单独计时的。
下面是一个示例:
conv1 pool1 conv2 pool2 resh1 ip1 relu1 ip2 softmax
nexus7 11.177 7.813 13.357 8.367 8.097 2.1 0.326 1.557 2.667
shield 13.219 1.024 1.567 1.081 0.988 14.588 13.323 14.318 40.347conv1和conv2 (卷积层)的时间分布对连接区的影响是相当大的。但在盾牌上,时间远远超出了2-4层的合理范围,似乎在接近尾声的时候聚集在一起。softmax层是一个相对较小的工作,所以40 is太大了。我的计时方法一定是错误的,否则其他事情正在发生。
运行这些层的代码如下所示:
double[] times = new double[layers.size()];
int layerindex = 0;
for (Layer a : layers) {
double t = SystemClock.elapsedRealtime();
//long t = System.currentTimeMillis(); // makes no difference
blob = a.forward(blob); // here we call renderscript forEach_(), invoke_() etc
//mRS.finish(); // makes no difference
t = SystemClock.elapsedRealtime() - t;
//t = System.currentTimeMillis() - t; // makes no difference
times[layerindex] += t; // later we take average etc
layerindex++;
}我的理解是,一旦forEach_()返回,作业就应该完成。无论如何,mRS.finish()应该提供一个最后的屏障。但放眼时代,唯一合理的解释是,工作仍然是在后台处理的。
这个应用程序非常简单,我只是运行测试从MainActivity和打印到logcat。Android将此应用程序作为发布版构建,并在通过USB连接的设备上运行。
(1)对RenderScript进程进行计时的正确方法是什么?(2)当forEach_()返回时,脚本生成的线程可以保证完成吗?(3)在我的测试应用程序中,我只是直接从MainActivity运行。这是一个问题吗(除了阻塞UI线程和使应用程序没有响应外)?如果这会影响时间或者导致奇怪,那么有什么合适的方法来设置这样的测试应用程序呢?
发布于 2016-05-06 20:52:52
我自己在RenderScript中实现了CNN,正如您解释的那样,如果您将每个进程作为不同的内核实现,则需要对每个层链接多个进程并多次调用它们。因此,我可以向您保证,forEach调用返回并不能真正保证流程已经完成。理论上,这只会安排内核,所有排队的请求实际上都会在系统确定最好的情况下运行,特别是当这些请求在tablet的GPU中被处理时。
通常,确保对内核真正运行有某种控制的唯一方法是在层间显式读取RS内核的输出,例如在内核的输出分配对象上使用.copyTo()。这个“强制”任何排队的RS作业尚未运行(该层的输出分配是依赖的),在那个时候执行。当然,这可能会引入数据传输开销,您的时间安排也不会完全准确--事实上,如果以这种方式计时,整个网络的执行时间肯定会低于各个层的总和。但据我所知,这是唯一可靠的方法来计时链中的单个内核,它将给您一些反馈,以找出瓶颈所在,并更好地指导您的优化,如果这就是您所追求的。
发布于 2016-05-13 22:29:55
也许有点离题:但是对于CNN来说,如果你可以用矩阵乘法作为基本计算块来构造你的算法,你实际上可以使用RenderScript IntrinsicBLAS,特别是BNNM和SGEMM。
优点:
缺点:
如果BLAS适合你的算法,这是值得一试的。它很容易使用:
import android.support.v8.renderscript.*;
// if you are not using support lib:
// import android.renderscript.*;
private void runBNNM(int m, int n, int k, byte[] a_byte, byte[] b_byte, int c_offset, RenderScript mRS) {
Allocation A, B, C;
Type.Builder builder = new Type.Builder(mRS, Element.U8(mRS));
Type a_type = builder.setX(k).setY(m).create();
Type b_type = builder.setX(k).setY(n).create();
Type c_type = builder.setX(n).setY(m).create();
// If you are reusing the input Allocations, just create and cache them somewhere else.
A = Allocation.createTyped(mRS, a_type);
B = Allocation.createTyped(mRS, b_type);
C = Allocation.createTyped(mRS, c_type);
A.copyFrom(a_byte);
B.copyFrom(b_byte);
ScriptIntrinsicBLAS blas = ScriptIntrinsicBLAS.create(mRS);
// Computes: C = A * B.Transpose
int a_offset = 0;
int b_offset = 0;
int c_offset = 0;
int c_multiplier = 1;
blas.BNNM(A, a_offset, B, b_offset, C, c_offset, c_multiplier);
}SGEMM也是如此:
ScriptIntrinsicBLAS blas = ScriptIntrinsicBLAS.create(mRS);
// Construct the Allocations: A, B, C somewhere and make sure the dimensions match.
// Computes: C = 1.0f * A * B + 0.0f * C
float alpha = 1.0f;
float beta = 0.0f;
blas.SGEMM(ScriptIntrinsicBLAS.NO_TRANSPOSE, ScriptIntrinsicBLAS.NO_TRANSPOSE,
alpha, A, B, beta, C);https://stackoverflow.com/questions/37080673
复制相似问题