Что вызывает периодические скачки производительности, которые наблюдаются при выполнении вычислительно дорогостоящей обработки массивов на Nexus 4?
Я новичок в потоках (не убивайте меня за мою реализацию ниже:), и мне нужно сделать несколько проходов размытия пикселей в отдельном потоке (см. Ниже). Это не самая эффективная реализация размытия рамки (это из Gaussian Filter без использования ConvolveOp), но скачки производительности не происходят на планшете Nexus 7, но они происходят на телефоне Nexus 4.
Я разместил свой тестовый образец (работает на Android 4.2 - см. Ниже).
Я не думаю, что это вызвано тем, что GC бьет память (это не совпадает с шипами).
Я думаю, что это может быть связано с локальностью кэша или перегрузкой аппаратной памяти, но я не уверен.
Что вызвало бы шипы? Иногда они внезапно начинаются - например, всплеск 50%. Иногда они начинаются медленно - например, шипы увеличиваются / уменьшаются монотонно, с шипами следующим образом -> 5%, 10%, 20%, 10%, 5%.
Как я мог остановить их появление при выполнении тяжелой обработки массива?
Это не происходит на планшете Nexus 7, который я также тестировал (см. Результаты ниже)
Дополнительный вопрос: Каков наилучший способ спать и перезапускать мою тему правильно (новичок в темах)?
MainActivity.java
package com.example.test;
import android.os.Bundle;
import android.app.Activity;
public class MainActivity extends Activity {
private MainThread thread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
thread = new MainThread();
thread.setRunning(true);
thread.start();
setContentView(R.layout.activity_main);
}
@Override
protected void onResume() {
super.onResume();
thread.setRunning(true);
}
@Override
protected void onPause() {
super.onPause();
thread.setRunning(false);
}
}
MainThread.java
package com.example.test;
import android.util.Log;
public class MainThread extends Thread {
int[] pixels;
int kernel_rows = 2;
int kernel_cols = 2;
int width = 512;
int height = 512;
@Override
public void run() {
while (running) {
long start = System.currentTimeMillis();
for (int row = kernel_rows / 2; row < height - kernel_rows / 2; row++) {
for (int col = kernel_cols / 2; col < width - kernel_cols / 2; col++) {
float pixel = 0;
// iterate over each pixel in the kernel
for (int row_offset = 0; row_offset < kernel_rows; row_offset++) {
for (int col_offset = 0; col_offset < kernel_cols; col_offset++) {
// subtract by half the kernel size to center the
// kernel
// on the pixel in question
final int row_index = row + row_offset
- kernel_rows / 2;
final 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;
}
}
long stop = System.currentTimeMillis();
long delta = stop - start;
Log.d("DELTA", Long.toString(delta));
}
}
private boolean running;
public void setRunning(boolean running) {
this.pixels = new int[512 * 512];
this.running = running;
}
}
бревна
Телефон Nexus 4 (мс):
01-13 10:56:05.663: D/DELTA(13507): 76
01-13 10:56:05.773: D/DELTA(13507): 107
01-13 10:56:05.843: D/DELTA(13507): 77
01-13 10:56:05.923: D/DELTA(13507): 75
01-13 10:56:06.053: D/DELTA(13507): 127
01-13 10:56:06.133: D/DELTA(13507): 78
01-13 10:56:06.213: D/DELTA(13507): 81
01-13 10:56:06.293: D/DELTA(13507): 80
01-13 10:56:06.353: D/DELTA(13507): 77
01-13 10:56:06.433: D/DELTA(13507): 79
01-13 10:56:06.513: D/DELTA(13507): 79
01-13 10:56:06.624: D/DELTA(13507): 106
01-13 10:56:06.694: D/DELTA(13507): 76
Планшет Nexus 7 (мс):
01-13 11:01:03.283: D/DELTA(3909): 84
01-13 11:01:03.373: D/DELTA(3909): 85
01-13 11:01:03.453: D/DELTA(3909): 85
01-13 11:01:03.543: D/DELTA(3909): 84
01-13 11:01:03.623: D/DELTA(3909): 85
01-13 11:01:03.703: D/DELTA(3909): 84
01-13 11:01:03.793: D/DELTA(3909): 85
01-13 11:01:03.873: D/DELTA(3909): 84
01-13 11:01:03.963: D/DELTA(3909): 85
01-13 11:01:04.043: D/DELTA(3909): 84
1 ответ
Я думаю, что я, возможно, несколько смягчил этот эффект на Nexus 4. Все еще есть некоторая изменчивость в последовательности вычислений, но это терпимо - я думаю - не могу видеть слишком много огромных всплесков - вне запуска / завершения потока. Я сделал это с помощью Android NDK и c p_threads, чтобы порождать собственный поток, который в основном оставлен Java (или мне так сказали), пока приложение переднего плана не будет изменено или закрыто.
Вот код:
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
(который должен быть помещен в папку jni в корне проекта Android)
#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
(который должен быть помещен в папку jni в корне проекта Android)
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)
Резюме
Снижена стоимость кода на ~20-30% и уменьшена изменчивость на порядок.
Код компилируется путем выполнения ndk-build
команда из библиотеки NDK, предоставляемая Android в корневой папке (находится здесь: http://developer.android.com/tools/sdk/ndk/index.html).
Результаты
Нексус 4 (мс):
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
Нексус 7 (мс):
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