I think I may have somewhat mitigated this effect on the Nexus 4. There is still some variability in calculation consistency but it is bearable - I think - can't see too many huge spikes - outside of thread startup/shutdown. I've done this using the Android NDK and c p_threads to spawn a native thread that is mostly left alone by Java (or so I'm told) until the foreground application is changed or closed.
Here is the code:
MainActivity.java
package com.example.test;
import android.os.Bundle;
import android.app.Activity;
public class MainActivity extends Activity {
static {
System.loadLibrary("native");
}
private native void init();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Initializes and spawns native thread
init();
setContentView(R.layout.activity_main);
}
}
native.c
(which should be put into a jni folder at the root of the Android project)
#include <time.h>
#include <pthread.h>
#include <jni.h>
#include <android/log.h>
#define APPNAME "DELTA"
int* pixels;
int kernel_rows = 2;
int kernel_cols = 2;
int width = 60;
int height = 39;
int running = 1;
// from android samples
/* return current time in milliseconds */
static double now_ms(void) {
struct timespec res;
clock_gettime(CLOCK_REALTIME, &res);
return 1000.0 * res.tv_sec + (double) res.tv_nsec / 1e6;
}
// initialize thread/begin it
jint Java_com_example_testa_MainActivity_init(JNIEnv* env, jobject javaThis) {
int i1 = 1;
pthread_t thread;
void *run();
pthread_create(&thread, NULL, run, &i1);
pthread_join(thread, NULL);
return 0;
}
// thread function
void *run(int *x) {
// init pixels within thread
pixels = (int*) malloc(sizeof(int) * width * height);
// loop until stopped - java won't interfere
// unless closed/switch application (or so I'm told)
while (running) {
double start = now_ms();
int row, col, row_offset, col_offset;
for (row = kernel_rows / 2; row < height - kernel_rows / 2; row++) {
for (col = kernel_cols / 2; col < width - kernel_cols / 2; col++) {
float pixel = 0;
// iterate over each pixel in the kernel
for (row_offset = 0; row_offset < kernel_rows; row_offset++) {
for (col_offset = 0; col_offset < kernel_cols;
col_offset++) {
// subtract by half the kernel size to center the
// kernel
// on the pixel in question
int row_index = row + row_offset - kernel_rows / 2;
int col_index = col + col_offset - kernel_cols / 2;
pixel += pixels[row_index * width + col_index] * 1.0f
/ 4.0f;
}
}
pixels[row * width + col] = (int) pixel;
}
}
double end = now_ms();
double delta = end - start;
__android_log_print(ANDROID_LOG_VERBOSE, APPNAME, "%f", delta);
}
pthread_exit(0);
}
Android.mk
(which should be put into a jni folder at the root of the Android project)
LOCAL_PATH := $(call my-dir)
MY_PATH := $(LOCAL_PATH)
include $(call all-subdir-makefiles)
include $(CLEAR_VARS)
LOCAL_PATH := $(MY_PATH)
LOCAL_MODULE := native
LOCAL_LDLIBS := -llog
LOCAL_SRC_FILES := native.c
include $(BUILD_SHARED_LIBRARY)
Summary
Reduced cost of code by ~20-30% and decreased variability by an order of magnitude.
The code is compiled by executing the ndk-build
command from the NDK library provided by Android at the root folder (found here: http://developer.android.com/tools/sdk/ndk/index.html).
Results
Nexus 4 (ms):
01-14 13:41:21.132: V/DELTA(23679): 56.554199
01-14 13:41:21.192: V/DELTA(23679): 58.568604
01-14 13:41:21.252: V/DELTA(23679): 59.484131
01-14 13:41:21.302: V/DELTA(23679): 56.768066
01-14 13:41:21.362: V/DELTA(23679): 54.692383
01-14 13:41:21.412: V/DELTA(23679): 51.823730
01-14 13:41:21.472: V/DELTA(23679): 55.668945
01-14 13:41:21.522: V/DELTA(23679): 56.920654
01-14 13:41:21.582: V/DELTA(23679): 56.371094
01-14 13:41:21.642: V/DELTA(23679): 58.507568
01-14 13:41:21.702: V/DELTA(23679): 59.697754
01-14 13:41:21.752: V/DELTA(23679): 53.990723
01-14 13:41:21.812: V/DELTA(23679): 55.669189
Nexus 7 (ms):
01-14 13:41:25.685: V/DELTA(2916): 65.867920
01-14 13:41:25.745: V/DELTA(2916): 65.986816
01-14 13:41:25.815: V/DELTA(2916): 66.685059
01-14 13:41:25.885: V/DELTA(2916): 67.033936
01-14 13:41:25.945: V/DELTA(2916): 65.703857
01-14 13:41:26.015: V/DELTA(2916): 66.653076
01-14 13:41:26.085: V/DELTA(2916): 66.922119
01-14 13:41:26.145: V/DELTA(2916): 67.030029
01-14 13:41:26.215: V/DELTA(2916): 67.014893
01-14 13:41:26.285: V/DELTA(2916): 67.034912
01-14 13:41:26.345: V/DELTA(2916): 67.089844
01-14 13:41:26.415: V/DELTA(2916): 65.860107
01-14 13:41:26.485: V/DELTA(2916): 65.642090
01-14 13:41:26.545: V/DELTA(2916): 65.574951
01-14 13:41:26.615: V/DELTA(2916): 65.991943