jjzjj

android - 在 Nexus 4 上进行计算量大的数组处理时,会出现周期性的性能峰值是什么原因造成的?

coder 2023-12-24 原文

我是线程的新手(不要因为我下面的实现而杀了我:),我需要在一个单独的线程上对像素进行多次模糊处理(见下文)。它不是方框模糊的最有效实现(来自 Gaussian Filter without using ConvolveOp),但性能峰值不会出现在 Nexus 7 平板电脑上,但会出现在 Nexus 4 手机上。

我已经发布了我的测试示例(在 Android 4.2 上运行 - 见下文)。

我不认为这是由 GC 抖动内存引起的(它与峰值不一致)。

我认为这可能与缓存局部性或硬件内存抖动有关 - 但我不确定。

什么会导致尖峰?有时它们会突然发作 - 例如峰值 50%。有时它们起病缓慢 - 例如尖峰单调增加/减少,尖峰如下 -> 5%, 10%, 20%, 10%, 5%.

在进行繁重的数组处理时如何阻止它们发生?

这不会发生在我也测试过的 Nexus 7 平板电脑上(见下面的结果)

附带问题:正确休眠和重新启动我的线程的最佳方法是什么(线程新手)?


主 Activity .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);

    }

}

主线程.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

最佳答案

我想我可能已经在某种程度上减轻了 Nexus 4 上的这种影响。计算一致性仍然存在一些可变性,但它是可以忍受的 - 我认为 - 看不到太多巨大的尖峰 - 在线程启动/关闭之外。我已经使用 Android NDK 和 c p_threads 完成了此操作,以生成一个 native 线程,该线程主要由 Java(或者我被告知)单独保留,直到更改或关闭前台应用程序。

代码如下:


主 Activity .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);

    }

}

原生.c


(应该放在Android项目根目录下的jni文件夹中)

#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);

}

安卓.mk


(应该放在Android项目根目录下的jni文件夹中)

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%,可变性降低了一个数量级。

代码是通过在根文件夹(可在此处找到:http://developer.android.com/tools/sdk/ndk/index.html)中从 Android 提供的 NDK 库执行 ndk-build 命令编译的。


结果


Nexus 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

Nexus 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

关于android - 在 Nexus 4 上进行计算量大的数组处理时,会出现周期性的性能峰值是什么原因造成的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14299288/

有关android - 在 Nexus 4 上进行计算量大的数组处理时,会出现周期性的性能峰值是什么原因造成的?的更多相关文章

  1. ruby-on-rails - 使用一系列等级计算字母等级 - 2

    这里是Ruby新手。完成一些练习后碰壁了。练习:计算一系列成绩的字母等级创建一个方法get_grade来接受测试分数数组。数组中的每个分数应介于0和100之间,其中100是最大分数。计算平均分并将字母等级作为字符串返回,即“A”、“B”、“C”、“D”、“E”或“F”。我一直返回错误:avg.rb:1:syntaxerror,unexpectedtLBRACK,expecting')'defget_grade([100,90,80])^avg.rb:1:syntaxerror,unexpected')',expecting$end这是我目前所拥有的。我想坚持使用下面的方法或.join,

  2. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  3. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  4. ruby - 如何计算 Liquid 中的变量 +1 - 2

    我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

  5. ruby - 使用 Ruby,计算 n x m 数组的每一列中有多少个 true 的简单方法是什么? - 2

    给定一个nxmbool数组:[[true,true,false],[false,true,true],[false,true,true]]有什么简单的方法可以返回“该列中有多少个true?”结果应该是[1,3,2] 最佳答案 使用转置得到一个数组,其中每个子数组代表一列,然后将每一列映射到其中的true数:arr.transpose.map{|subarr|subarr.count(true)}这是一个带有inject的版本,应该在1.8.6上运行,没有任何依赖:arr.transpose.map{|subarr|subarr.in

  6. arrays - 计算数组中的匹配元素 - 2

    给定两个大小相等的数组,如何找到不考虑位置的匹配元素的数量?例如:[0,0,5]和[0,5,5]将返回2的匹配项,因为有一个0和一个5共同;[1,0,0,3]和[0,0,1,4]将返回3的匹配项,因为0有两场,1有一场;[1,2,2,3]和[1,2,3,4]将返回3的匹配项。我尝试了很多想法,但它们都变得相当粗糙和令人费解。我猜想有一些不错的Ruby习惯用法,或者可能是一个正则表达式,可以很好地回答这个解决方案。 最佳答案 您可以使用count完成它:a.count{|e|index=b.index(e)andb.delete_at

  7. Ruby 的数字方法性能 - 2

    我正在使用Ruby解决一些ProjectEuler问题,特别是这里我要讨论的问题25(Fibonacci数列中包含1000位数字的第一项的索引是多少?)。起初,我使用的是Ruby2.2.3,我将问题编码为:number=3a=1b=2whileb.to_s.length但后来我发现2.4.2版本有一个名为digits的方法,这正是我需要的。我转换为代码:whileb.digits.length当我比较这两种方法时,digits慢得多。时间./025/problem025.rb0.13s用户0.02s系统80%cpu0.190总计./025/problem025.rb2.19s用户0.0

  8. ruby - Ruby 性能中的计时器 - 2

    我正在寻找一个用ruby​​演示计时器的在线示例,并发现了下面的代码。它按预期工作,但这个简单的程序使用30Mo内存(如Windows任务管理器中所示)和太多CPU有意义吗?非常感谢deftime_blockstart_time=Time.nowThread.new{yield}Time.now-start_timeenddefrepeat_every(seconds)whiletruedotime_spent=time_block{yield}#Tohandle-vesleepinteravalsleep(seconds-time_spent)iftime_spent

  9. ruby-on-rails - 在现有数据库上进行 Rails 迁移 - 2

    我正在创建一个新的Rails3.1应用程序。我希望这个新应用程序重用现有数据库(由以前的Rails2应用程序创建)。我创建了新的应用程序定义模型,它重用了数据库中的一些现有数据。在开发和测试阶段,一切正常,因为它在干净的表数据库上运行,但是当尝试部署到生产环境时,我收到如下消息:PGError:ERROR:column"email"ofrelation"users"alreadyexists***[err::localhost]:ALTERTABLE"users"ADDCOLUMN"email"charactervarying(255)DEFAULT''NOTNULL但是我在迁移中有这

  10. ruby-on-rails - 如果条件与 &&,是否有任何性能提升 - 2

    如果用户是所有者,我有一个条件来检查说删除和文章。delete_articleifuser.owner?另一种方式是user.owner?&&delete_article选择它有什么好处还是它只是一种写作风格 最佳答案 性能不太可能成为该声明的问题。第一个要好得多-它更容易阅读。您future的自己和其他将开始编写代码的人会为此感谢您。 关于ruby-on-rails-如果条件与&&,是否有任何性能提升,我们在StackOverflow上找到一个类似的问题:

随机推荐