Cómo sincronizar correctamente el código RenderScript de Android en Nvidia Shield
He implementado un pequeño CNN en RenderScript y quiero perfilar el rendimiento en diferentes hardware. En mi Nexus 7 los tiempos tienen sentido, pero en el escudo de NVIDIA no lo hacen.
La CNN (LeNet) se implementa en 9 capas que residen en una cola, el cómputo se realiza en secuencia. Cada capa se cronometra individualmente.
- Tiempo inactivo de actividad para HistoryRecord?
- Android - Cómo contar el tiempo durante un largo período
- Cómo administrar la expiración de prueba sin conexión para una aplicación de Android?
- Problema de análisis de tiempo en Android
- La mejor manera de determinar la aplicación actual (en ejecución) actual (utilizando un servicio?)
Aquí hay un ejemplo:
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.347
La distribución de los tiempos es aproximadamente correcta para el nexo, con conv1 y conv2 (capas de convolución) tomando la mayor parte del tiempo. Pero en el escudo, los tiempos caen mucho más allá de lo que es razonable para las capas 2-4 y parecen reunirse hacia el final. La capa softmax es un trabajo relativamente pequeño, por lo que 40ms es demasiado grande. Mi método de cronometraje debe ser defectuoso, o algo está sucediendo.
El código que ejecuta las capas se ve así:
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++; }
Es mi entendimiento que una vez que forEach_ () devuelve, el trabajo se supone que está terminado. En cualquier caso, mRS.finish () debería proporcionar una barrera final. Pero mirando los tiempos, la única explicación razonable es que los trabajos todavía se procesan en el fondo.
La aplicación es muy simple, sólo ejecutar la prueba de MainActivity e imprimir a logcat. Android Studio genera la aplicación como una versión y la ejecuta en el dispositivo que está conectado mediante USB.
(1) ¿Cuál es la forma correcta de procesar tiempo RenderScript? (2) ¿Es cierto que cuando forEach_ () devuelve, los hilos generados por el script están garantizados para ser hecho? (3) En mi aplicación de prueba, simplemente ejecuto directamente desde MainActivity. ¿Es esto un problema (aparte de bloquear el hilo de la interfaz de usuario y hacer que la aplicación no responda)? Si esto influye en el momento o causa la rareza, ¿cuál es una forma adecuada de configurar una aplicación de prueba como ésta?
- que muestran la hora actual en TimePicker en android
- Tener problemas para conseguir Joda-tiempo para ejecutar en Android.
- SetMinDate (...) para DatePicker no funciona cuando se invoca una segunda vez
- ¿Cómo se calcula el tiempo del rezo musulmán en una aplicación androide?
- El tiempo de conexión expiro
- Capturar / Interceptar los intentos de Android durante el tiempo de ejecución
- Sincronizar la hora del sistema en 2 teléfonos
- Android OS 2.2 Permisos: No tengo ni idea de por qué este simple código no funciona. ¿Qué estoy haciendo mal?
He implementado CNNs en RenderScript yo mismo, y como usted explica, requiere encadenar múltiples procesos y llamar a forEach_*()
varias veces para cada capa si los implementa cada uno como un kernel diferente. Como tal, puedo asegurarle que el forEach llamada que vuelve no garantiza realmente que el proceso ha terminado. En teoría, esto sólo programará el kernel y todas las solicitudes en cola se ejecutarán cuando el sistema determine que es mejor, especialmente si se procesan en la GPU de la tableta.
Por lo general, la única manera de asegurarse de que tiene algún tipo de control sobre un kernel realmente ejecutado es leyendo explícitamente la salida del kernel RS entre capas, como por ejemplo .copyTo()
en el objeto de asignación de salida de ese kernel . Esto "obliga" a cualquier trabajo de RS en cola que aún no se ha ejecutado (en el que depende la asignación de salida de esa capa), para ejecutarse en ese momento. Por supuesto, que puede introducir gastos de transferencia de datos y su calendario no será totalmente precisa – de hecho, el tiempo de ejecución de la red completa seguramente será menor que la suma de las capas individuales si cronometrada de esta manera. Pero por lo que sé, es la única forma fiable de tiempo kernels individuales en una cadena y le dará algunos comentarios para saber dónde están los cuellos de botella, y para orientar mejor su optimización, si eso es lo que buscas.
Tal vez un poco fuera de tema: pero para CNN, si se puede estructurar el algoritmo utilizando matriz-matriz de multiplicación como bloques básicos de cálculo que puede utilizar RenderScript IntrinsicBLAS , especialmente BNNM y SGEMM .
Pros:
- Implementación de alto rendimiento de 8bit Matrix Multiplication (BNNM), disponible en N Preview .
- Volver apoyo de nuevo a Android 2.3 a través de RenderScript lib de soporte , cuando se utiliza Build-Tools 24.0.0 rc3 y superior.
- Aceleración de GPU de alto rendimiento de SGEMM en Nexus5X y 6P con N Previsualizar construir NPC91K.
- Si sólo utiliza RenderScript Intrinsics, puede codificar todo en java.
Contras:
- Su algoritmo puede necesitar ser refactorizado, y debe basarse en la multiplicación de matriz 2D.
- Aunque disponible en Android 6.0, pero el rendimiento de BNNM en 6.0 no es satisfactorio. Por lo tanto, es mejor utilizar lib de soporte para BNNM y establecer targetSdkVersion como 24.
- La aceleración de la GPU SGEMM actualmente sólo está disponible en Nexus5X y Nexus6P. Y actualmente requiere que el ancho y la altura de las Matrices sean múltiplos de 8.
Vale la pena intentar si BLAS se ajusta a su algoritmo. Y es fácil de usar:
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 es similar:
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);
- Enviar datos a través de wifi (no internet) cuando los datos móviles
- Cordova: Error de comando con código de salida EACCES