2020-08-17

App性能测试

性能测试在软件的质量保证中起着重要的作用,它包括的测试内容丰富多样。中国软件评测中心将性能测试概括为三个方面:应用在客户端性能的测试、应用在网络上性能的测试和应用在服务器端性能的测试。通常情况下,三方面有效、合理的结合,可以达到对系统性能全面的分析和瓶颈的预测。通过性能测试工具得到测试对象的各项性能指标,将各项性能指标与行业中的标准值或者竞品值去做对比,以得到一个综合的结论与优化改进的方案。这里将具体分享App客户端性能测试:

性能测试三要素

  1. 性能指标

  2. 测试场景

  3. 测试工具

image

性能指标

常见的性能指标有:

  1. 内存

  2. CPU

  3. 流量

  4. 电量

  5. 启动速度

  6. FPS流畅度

  7. 与服务器交互的网络速度

这里从2个角度分析:

  1. 为什么选这个指标?

  2. 指标常用单位有哪些?

1. 内存

为什么要选内存呢?需要知道Android的OOM和Low Memory Killer,iOS的Memory Leak和Memory Waring。

Android内存管理机制:

  1. OOM:Out Of Memory,顾名思义是说内存不够用或者耗尽了,进程会被强制终止。安卓框架限制了每个应用进程所占用的最大内存值。关注内存的一个目的就是避免内存使用过大,出现OOM。主要关注内存使用较多时的场景,例如得物App中视频播放或者feed流滑动过程中缓存大量图片。

  2. Low Memory Killer:Low Memory Killer在用户空间中指定了一组内存临界值,当其中的某个值与进程描述中的oom_adj值在同一范围时,该进程将被Kill掉。如果你的APP某个进程需要一直保存存活,你需要保持你的进程优先级足够高,并且占用比较小,因为Low Memory Killer在工作时,同一优先级的进程会先kill那个占用最大的。性能测试时主要关注待机时的内存是不是够小。

这里再补充一点:Low Memory Killer的工作可能致系统变卡。为什么呢?因为它kill了一些进程,然而现在市面的很多APP为了保活都会自启,刚刚被kill,立刻又起来。启动占用大量内存(还有CPU),又触发Low Memory Killer。频繁的被kill和启动形成了恶性循环,so…系统变的很卡.

iOS内存管理机制:

ios系统同样对每个程序都有最大的内存分配限制,如果超过了这个阈值,会被系统强制关闭,造成crash,通常出现在bugly上的错误码是SEGV_ACCERR。在调试的时候,会显示类似这样的信息

EXC_RESOURCE RESOURCE_TYPE_MEMORY (limit=650 MB, unused=0x0)

对于不同的RAM大小,限制不同:

  • 256MB: 49% - 51%

  • 512MB: 53% - 63%

  • 1024MB: 57% - 68%

  • 2048MB: 68% - 69%

  • 3072MB: 66%

  • 4096MB: 77%

Memory Waring:若某个程序的内存占用过大时,系统会向该app发送Memory Warning消息。程序员可以在收到内存警告的时候在程序内部做些内存释放处理( 释放其他可复现的资源&&保存数据)。 另外可能在内存紧张时设备的cpu也非常紧张或正在超频,那你即使在程序中对系统内存警告进行了处理,基本上也于事无补了,会crash。因为系统的内存警告有一定的滞后性,你收到后再处理,可能已经晚了,再加上cpu没空处理,再延后些就更不行了,所以需要尽量控制程序的内存占用不易过大。

Memory Leak:内存泄漏也是造成app内存过高的主要原因,如果iPhone手机的性能都很强,如果一个app会因为内存过高被系统强制杀掉,大部分都是存在内存泄漏。内存泄漏对于开发和测试而言表现得并不明显,如果它不泄漏到一定程度是用户是无法察觉的,但是这也是优秀的APP必须杜绝的一大问题。

内存指标:

内存指标有VSS、RSS、PSS、USS。差别如下:

  • VSS- Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)

  • RSS- Resident Set Size 实际使用物理内存(包含共享库占用的内存)

  • PSS- Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)

  • USS- Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)

一般来说内存占用大小有如下规律:VSS >= RSS >= PSS >= USS。

image

测试中比较常见的的选择是PSS total,这种算法共享库内存按比例分配,对APP来说比较公平。依据APP关注点,也可选择其他指标例如USS,或者将其他指标也一起统计,用于分析。

如何获取内存使用率

Android:

可以使用Android系统提供的 adb shell dumpsys meminfo 命令获取,如下图为得物APP的内存使用情况:

image

约占用216MB运行内存,手机运行内存共8GB,比例为2.63%

iOS:

iOS不同与Android系统,我们只能在iOS提供的源码中寻找内存使用信息,在Mach层找到了可以获取app内存的API,mach_task_basic_info 结构体存储了 Mach task 的内存使用信息,其中 resident_size 就是应用使用的物理内存大小,virtual_size 是虚拟内存大小。

#define MACH_TASK_BASIC_INFO     20 /* always 64-bit basic info */struct mach_task_basic_info {

 mach_vm_size_t virtual_size; /* virtual memory size (bytes) */

 mach_vm_size_t resident_size;/* resident memory size (bytes) */

 mach_vm_size_t resident_size_max;/* maximum resident memory size (bytes) */

 time_value_t user_time;/* total user run time for

                                               terminated threads */

 time_value_t system_time;/* total system run time for

                                               terminated threads */

 policy_t policy; /* default policy for new threads */

 integer_t suspend_count;/* suspend count for task */};

最后得到获取当前 App Memory 的使用情况:

- (CGFloat)usedMemory {

 task_vm_info_data_t vmInfo;

 mach_msg_type_number_t count = TASK_VM_INFO_COUNT;

 kern_return_t kernReturn = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t)&vmInfo,&count);

 if (kernReturn != KERN_SUCCESS) {

 return NSNotFound;

    }

 return (CGFloat)(vmInfo.phys_footprint / 1024.0 / 1024.0);}

设备已使用的内存

+ (CGFloat)deviceUsedMemory {

 size_t length = 0;

 int mib[6] = {0};

 int pagesize = 0;

    mib[0] = CTL_HW;

    mib[1] = HW_PAGESIZE;

length = sizeof(pagesize);

 if (sysctl(mib, 2, &pagesize, &length, NULL, 0) < 0) {

 return  0;

    }

 mach_msg_type_number_t count = HOST_VM_INFO_COUNT;

 vm_statistics_data_t vmstat;

 if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vmstat, &count) != KERN_SUCCESS) {

 return  0;

    }

 int wireMem = vmstat.wire_count * pagesize;

 int activeMem = vmstat.active_count * pagesize;

 return (CGFloat)(wireMem + activeMem) / 1024.0 / 1024.0;}

设备可用内存

+ (CGFloat)deviceAvailableMemory {

 vm_statistics64_data_t vmStats;

 mach_msg_type_number_t infoCount = HOST_VM_INFO_COUNT;

 kern_return_t kernReturn = host_statistics(mach_host_self(),

                                               HOST_VM_INFO,

                                               (host_info_t)&vmStats,

                                               &infoCount);

 if (kernReturn != KERN_SUCCESS) {

 return NSNotFound;

   }

 return (CGFloat)(vm_page_size * (vmStats.free_count + vmStats.inactive_count)  / 1024.0 / 1024.0);}

2. CPU

CPU使用率:

很多场景下我们使用App,可能会碰到手机会出现发热发烫的现象。这是因为CPU使用率过高、CPU过于繁忙,会使得整个系统无法响应用户,整体性能降低,手机耗电严重,用户体验变得相当差,也容易引起ANR(Application Not Responding 应用程序无响应)等等一系列问题。如果让用户发现你的APP用起来发烫,那就等着他的吐槽和卸载吧。也就是说CPU性能,我们需要关注APP使用中CPU消耗情况,通常会使用CPU使用率这个指标。

如何获取CPU使用率

在 Linux 系统下,CPU 利用率分为用户态、系统态和空闲态,他们分别代表的含义为:用户态表示 CPU 处于用户态执行的时间,系统态表示系统内核执行的时间,空闲态表示空闲系统进程执行的时间。

image

一个 App 的 CPU 使用率 = CPU 执行非系统空闲进程时间 / CPU 总的执行时间。

Android:

Linux下没有直接可以调用系统函数知道CPU占用和内存占用。那么如何知道CPU和内存信息呢?

使用proc文件系统计算cpu使用率

我们可以通过proc伪文件系统来实现,proc伪文件就不介绍了,只说其中4个文件。

  1. /proc/stat, 存放系统的CPU时间信息

  2. /proc/meminfo, 存放系统的内存信息

  3. /proc//status, 存放进程的CPU时间信息以及一些综合信息

  4. /proc//stat, 保存着进程的CPU信息

通过​adb shell​指令进入Android设备的终端,进行cpu相关信息的查看:

image

在系统cpu信息中,我们看到了有8个cpu信息,说明该Android设备是8核的cpu,其中cpu0,cpu1...cpu7代表了每个核的Jiffies ,第一行cpu 就代表总的的Jiffes。

image

cpu后跟了一个9元组其中 每一列的数值的详细的含义如下:

  • user :从系统启动开始累计到当前时刻,用户态的jiffies ,不包含 nice值为负进程;

  • nice :从系统启动开始累计到当前时刻,nice值为负的进程所占用的jiffies;

  • system :从系统启动开始累计到当前时刻,系统态的jiffies;

  • idle :从系统启动开始累计到当前时刻,除硬盘IO等待时间以外其它等待的jiffies;

  • iowait : 从系统启动开始累计到当前时刻,硬盘IO等待的jiffies;

  • irq : 从系统启动开始累计到当前时刻,硬中断的jiffies;

  • softirq :从系统启动开始累计到当前时刻,软中断的jiffies;

  • stealstolen:从系统启动开始累积到当前时刻,在虚拟环境运行时花费在其他操作系统的时间。

  • guest:从系统启动开始累积到当前时刻,在Linux内核控制下的操作系统虚拟cpu花费的时间。

系统cpu使用率的计算

系统总cpu的Jiffies就是上面所有项加起来的总和:

​totalCpuTime=user+system+nice+idle+iowait+irq+softirq+stealstolen+guest​

计算系统某个时刻的CPU使用率为(采样两个足够短的时间间隔的Cpu快照):

cpu_usage = (totalCpuTime_end - totalCpuTime_begin)- (idle_begin -idle_begin)/(totalCpuTime_end - totalCpuTime_begin)*100

常用的单位有:消耗XX jiffies/分钟;半/1小时共增加XX jiffies。

进程cpu使用率的计算

当我们想要获取某个应用的cpu性能时,除了通过​/proc/stat​获取系统cpu信息外,还需要知道该应用进程的cpu数据

image

其中我们只关心红框标记的数据,依次为:

  • utime=1089 该任务在用户态运行的时间,单位为jiffies

  • stime=385 该任务在核心态运行的时间,单位为jiffies

  • cutime=29 累计的该任务的所有的waited-for进程曾经在用户态运行的时间,单位为jiffies

  • cstime=52 累计的该任务的所有的waited-for进程曾经在核心态运行的时间,单位为jiffies

进程的总cpu时间​processCpuTime = utime + stime + cutime + cstime​,该值包括其所有线程的cpu时间。

该进程的cpu使用率为(采样两个足够短的时间间隔的Cpu快照与进程快照来计算进程的Cpu使用率):

cpu_usage = (processCpuTime_end – processCpuTime_begin) / (totalCpuTime_end – totalCpuTime_begin)*100
线程cpu使用率的计算

使用​/proc//task//stat​可以获取app中某线程cpu的相关信息,同上述计算方法一样即可获取该线程cpu的使用率这里不再多做介绍。

cpu_usage = 100*( threadCpuTime_end – threadCpuTime_begin) / (totalCpuTime_end – totalCpuTime_begin)
使用top命令获取cpu使用率

top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器。top是一个动态显示过程,即可以通过用户按键来不断刷新当前状态。如果在前台执行该命令,它将独占前台,直到用户终止该程序为止。top命令提供了实时的对系统处理器的状态监视。它将显示系统中CPU最“敏感”的任务列表。该命令可以按CPU使用、内存使用和执行时间对任务进行排序。

image
使用dumpsys cpuinfo命令获取cpu使用率

Android提供的dumpsys工具可以用于查看感兴趣的系统服务信息与状态,dumpsys cpuinfo可以用来查看安卓系统当前的cpu使用情况。

image

iOS:

iOS系统不同于Android系统那么开源,我们无法直接进入iphone的终端对cpu数据进行查看。但是应用作为进程运行时,包含了多个不同的线程,这样如果我们能知道app里所有线程占用 CPU 的情况,也就能知道整个app的 CPU 占用率。幸运的是我们同样在Mach 层中 thread_basic_info 结构体发现了我们想要的东西,thread_basic_info 结构体定义如下:

struct  thread_basic_info {

 time_value_t user_time;/* user run time */

 time_value_t system_time;/* system run time */

 integer_t cpu_usage;/* scaled cpu usage percentage */

 policy_t policy; /* scheduling policy in effect */

 integer_t run_state;/* run state (see below) */

 integer_t flags;/* various flags (see below) */

 integer_t suspend_count;/* suspend count for thread */

 integer_t sleep_time; /* number of seconds that thread

                                           has been sleeping */};

其中cpu_usage即为该线程的CPU使用率,接下来我们需要获取app的所有线程,iOS内核提供了 thread_info API 调用获取指定 task 的线程列表,然后可以通过 thread_info API 调用来查询指定线程的信息,thread_info API 在 thread_act.h 中定义

kern_return_t task_threads(

 task_t target_task,

 thread_act_array_t *act_list,

 mach_msg_type_number_t *act_listCnt);

task_threadstarget_task 任务中的所有线程保存在 act_list 数组中。

现在我们能够取得app的所有线程,并且能够取得每个线程的CPU使用率,这样获取app的CPU使用率的代码就呼之欲出,直接上代码:

- (CGFloat)usedCpu {

 kern_return_t kr = { 0 };

 task_info_data_t tinfo = { 0 };

 mach_msg_type_number_t task_info_count = TASK_INFO_MAX;

    kr = task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)tinfo, &task_info_count);

 if (kr != KERN_SUCCESS) {

 return  0.0f;

    }

 task_basic_info_t basic_info = { 0 };

 thread_array_t thread_list = { 0 };

 mach_msg_type_number_t thread_count = { 0 };

 thread_info_data_t thinfo = { 0 };

 thread_basic_info_t basic_info_th = { 0 };

    basic_info = (task_basic_info_t)tinfo;

 // get threads in the task

    kr = task_threads(mach_task_self(), &thread_list, &thread_count);

 if (kr != KERN_SUCCESS) {

 return  0.0f;

    }

 long tot_sec = 0;

 long tot_usec = 0;

 float tot_cpu = 0;

 for (int i = 0; i < thread_count; i++) {

 mach_msg_type_number_t thread_info_count = THREAD_INFO_MAX;

        kr = thread_info(thread_list[i], THREAD_BASIC_INFO, (thread_info_t)thinfo, &thread_info_count);

 if (kr != KERN_SUCCESS) {

 return  0.0f;

        }

        basic_info_th = (thread_basic_info_t)thinfo;

 if ((basic_info_th->flags & TH_FLAGS_IDLE) == 0) {

            tot_sec = tot_sec + basic_info_th->user_time.seconds + basic_info_th->system_time.seconds;

            tot_usec = tot_usec + basic_info_th->system_time.microseconds + basic_info_th->system_time.microseconds;

            tot_cpu = tot_cpu + basic_info_th->cpu_usage / (float)TH_USAGE_SCALE;

        }

    }

    kr = vm_deallocate( mach_task_self(), (vm_offset_t)thread_list, thread_count * sizeof(thread_t) );

 if (kr != KERN_SUCCESS) {

 return  0.0f;

    }

 return (CGFloat)tot_cpu * 100;}

有了获取CPU使用率的方法后我们只要再加个定时器去实时查询,并将得到的结果显示在界面上即可:

 - (void)startMonitoringWithNoticeBlock:(void(^)(CGFloat value))noticeBlock {

 self.noticeBlock = noticeBlock;

_timer = [NSTimer scheduledTimerWithTimeInterval:1.0  target:self  selector:@selector(noticeCPUValue) userInfo:nil  repeats:YES];}

- (void)noticeCPUValue {

 if (self.noticeBlock) {

 self.noticeBlock([self usedCpu]);

    }}

3. 电量

手机电池资源有限,电量的重要性就不必说了。现在很多手机都有电量排行,如果你的APP总是排在前面,小心被卸载哦。电量通常的单位是:mAs或者mAh。通常导致电量消耗过快的原因有两种:CPU使用率过高和GPU运算量过大。

4. 流量

手机的一个特点就是有移动网络。移动网络下的流量消耗需要特别关注,wifi下的流量优先级略低。流量单位:kb,M。

网络流量测试是针对大部分应用而言的,可能还有部分应用会关注网速、弱网之类的测试。

流量测试包括以下测试项:

  • 应用首次启动流量提示;

  • 应用后台连续运行2小时的流量值;

  • 应用高负荷运行的流量峰值。

5. 启动速度

软件的响应时间和响应速度是APP最直观带给用户的最直观的用户体验,如果一个软件,迟迟加载不出来,会直接影响到软件的日活、留存。因此对于一个软件,对响应速度测试是非常有必要的。

主要测试点:

  • 首次启动的时间:从APP启动到出现第一个可操作的页面的间隔时间;

  • 冷启动(非首次启动,当启动应用前,后台没有该应用的进程,这时系统会重新创建一个新的进程分配给该应用)

  • 热启动(当应用已经打开,按下home键等回到桌面或者打开其他程序时,再重新打开该APP,后台已经存在该应用进程)

可用性原则里面有个2秒原则:一个松散的原则,即用户没有必要对某些系统响应等待2秒以上的时间,比如应用程序转换和开始的响应时间。对于启动APP,进入某页面,这些操作时间都应不超过2秒,且越短用户体验越好。

当然,2秒并不是绝对的,对于一些用户感知明显的功能,例如垃圾扫描,病毒查杀,可能需要更多的时间,但是操作进行期间,需要给用户适当的感知和预期,避免用户因等待过久而离开。当然,用户是期望能够又准又快。

6.FPS流畅度

FPS即屏幕每秒的刷新率,范围在0-60之间,60最佳。FPS是测量用于保存、显示动态视频的信息数量,每秒钟帧数愈多,所显示的动作就会愈流畅,优秀的app都要保证FPS 在 55-60 之间,这样才会给用户流畅的感觉,反之,用户则会感觉到卡顿。

关键指标:

关于FPS有两个关键指标60帧每秒以及16.67毫秒,这两个关键指标是怎么得来的呢?

当你的图片切换不够快的时候,就会被人眼看穿,反馈给用户的就是所谓的卡顿现象。

想要让大脑觉得动作是连续的,至少是每秒10-12帧的速度,而想达到流畅的效果,至少需要每秒24帧。这也是为什么电影片源通常都是24帧的原因,不过60帧每秒的流畅度是最佳的,我们的目标就是让程序的流畅度能接近60帧每秒,当然超过60帧速的话大部分人还是会受不了的。

综上所述,APP需要尽可能的超过24帧/秒,接近60帧/秒的速度,并且在使用的过程中保持这个速率,因此这意味着我们的程序需要在16.67ms内处理一幅画面内的所有事,并保持住这个状态。

计算公式:1000ms / 60 frames ≈ 16.67 ms/frames

Android:

同样的我们可以使用命令 ​adb shell dumpsys gfxinfo com.shizhuang.duapp​获取到得物App的fps数据

image

以上数据为得物app首页推荐tab下的fps信息

可以看到在本次dump搜索的1809帧信息里有380帧的耗时超过了16.67ms,卡顿概率为21.01%

每帧的详细信息如下:

image
  • Draw: 表示在Java中创建显示列表部分中,OnDraw()方法占用的时间

  • Prepare: 准备时间

  • Process: 表示渲染引擎执行显示列表所花的时间,view越多,时间就越长

  • Execute: 表示把一帧数据发送到屏幕上排版显示实际花费的时间,其实是实际显示帧数据的后台缓存区与前台缓冲区交换后并将前台缓冲区的内容显示到屏幕上的时间

将上面的四个时间加起来就是绘制一帧所需要的时间,如果超过了16.67就表示掉帧了

iOS:

从一个像素到最后真正显示在屏幕上,iPhone 究竟在这个过程中做了些什么?想要了解背后的运作流程,首先需要了解屏幕显示的原理。iOS 上完成图形的显示实际上是 CPU、GPU 和显示器协同工作的结果,具体来说,CPU 负责计算显示内容,包括视图的创建、布局计算、图片解码、文本绘制等,CPU 完成计算后会将计算内容提交给 GPU,GPU 进行变换、合成、渲染后将渲染结果提交到帧缓冲区,当下一次垂直同步信号(简称 V-Sync)到来时,最后显示到屏幕上。下面是显示流程的示意图:

image

在 iPhone 中使用的是双缓冲机制,即上图中的 FrameBuffer 有两个缓冲区,双缓冲区的引入是为了提升显示效率,但是与此同时,他引入了一个新的问题,当视频控制器还未读取完成时,比如屏幕内容刚显示一半时,GPU 将新的一帧内容提交到帧缓冲区并把两个缓冲区进行交换后,视频控制器就会把新的一帧数据的下半段显示到屏幕上,造成画面撕裂现象,V-Sync 就是为了解决画面撕裂问题,开启 V-Sync 后,GPU 会在显示器发出 V-Sync 信号后,去进行新帧的渲染和缓冲区的更新。

搞清楚了 iPhone 的屏幕显示原理后,下面来看看在 iPhone 上为什么会出现卡顿现象,上文已经提及在图像真正在屏幕显示之前,CPU 和 GPU 需要完成自身的任务,而如果他们完成的时间错过了下一次 V-Sync 的到来(通常是1000/60=16.67ms),这样就会出现显示屏还是之前帧的内容,这就是界面卡顿的原因(离屏渲染就是典型的卡顿问题)。不难发现,无论是 CPU 还是 GPU 引起错过 V-Sync 信号,都会造成界面卡顿。

了解了iOS上导致界面卡顿的原因后,我们可以在app中开辟一条单独的子线程,让这条子线程去实时检测主线程的 RunLoop 情况,实时计算 kCFRunLoopBeforeSourceskCFRunLoopAfterWaiting两个状态之间的耗时是否超过某个阀值,如果超过阈值即认定主线程发生了卡顿。下面是代码实现:

static  void  runLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){

    MyClass *object = (__bridge MyClass*)info;

 // 记录状态值

    object->activity = activity;

 // 发送信号

 dispatch_semaphore_t semaphore = moniotr->semaphore;

    dispatch_semaphore_signal(semaphore);}

- (void)registerObserver{

    CFRunLoopObserverContext context = {0,(__bridge void*)self,NULL,NULL};

    CFRunLoopObserverRef observer = CFRunLoopObserverCreate(kCFAllocatorDefault,

                                                            kCFRunLoopAllActivities,

                                                            YES,

 0,

                                                            &runLoopObserverCallBack,

                                                            &context);

    CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);

 // 创建信号

    semaphore = dispatch_semaphore_create(0);

 // 在子线程监控时长

    dispatch_async(dispatch_get_global_queue(0, 0), ^{

 while (YES)

        {

 // 假定连续5次超时50ms认为卡顿(当然也包含了单次超时250ms)

 long st = dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 50*NSEC_PER_MSEC));

 if (st != 0)

            {

 if (activity==kCFRunLoopBeforeSources || activity==kCFRunLoopAfterWaiting)

                {

 if (++timeoutCount < 5)

 continue;

 // 检测到卡顿,进行卡顿上报

                }

            }

timeoutCount = 0;

        }

    });}              

当检测到卡顿后可以进一步收集卡顿现场,如堆栈信息等,然后进行数据分析。

7.与服务器交互的网络速度

当应用程序通过API与服务器进行交互时,响应时间对于性能至关重要。对于服务器性能,需要测试的内容:

1. 往返于服务器的数据

当App向服务器请求数据时,服务器需要下发给App合适的数据格式,请求时间不宜过长。在某些应用中,从服务端请求到的数据格式,客户端不能直接使用,需转换格式后才能显示,因为增加了数据格式转换的时候,用户感知上会觉得响应时间变长了。

2. 减少API的调用次数

应该尽量减少应用程序向服务器请求数据的次数。在某些情况下,客户端某个页面可能需要调用多个API才能进行完整的数据展示。为了更好的性能,应该使用较少的api来处理这个问题。

3. 服务器停机时间

由于任何原因,如果服务器关闭或不可访问,我们可以保存数据在本机数据库。每当服务器关闭时,我们可以显示存储在本地数据库中的数据。另一个解决方案可能是故障转移数据库服务器,即如果其中一个服务器关闭或处于维护阶段,备份服务器应该可用于切换。故障转移/备份服务器应与主服务器进行连续复制和同步。

测试场景

性能要测哪些场景呢?用户实际使用中,场景是多种多样的。那么要如何选择合适的场景呢?

原则:用户在该场景停留越久,该场景越重要;场景被用户使用到的频率越高,该场景优先级越高。

场景优先级评估:

  1. 运营数据评估

根据已有的运营数据,统计用户数据,确定场景优先级

  1. ACC测试建模

当我们没有获得运营数据时,可以考虑使用ACC建模思路来帮助区分性能场景优先级:

  • 分析产品的核心价值;

  • 分析产品的主要模块、系统;

  • FENIX每个系统提供何种能力实现产品价值;

ACC建模思路:

image

测试工具

测试工具的本质是获取性能数据,当然一些工具在使用和观察数据上有差别。工具分2类:现有工具,其他获取方案。这里列举下目前我们常用的工具共选择和参考:

image

如果是新手或者只需测试几次,可考虑手工进行性能测试,建议选择方便直观易用的工具。测试内存和CPU使用率,推荐使用APT(http://code.tencent.com/apt.htmleclipse插件,可实时监控Android手机上多个应用的CPU、内存数据曲线,直观方便。)

如果是需要长期测试的内容,需要考虑自动化测试。目前所有的Android端App的性能测试其实都是基于ADB的,而iOS相对比较封闭,只能依赖iOS的底层Api获取相互性能数据,现在公司架构组的同学已经搭建了APM系统,用来收集iOS和Android端得物APP的性能参数了,可以在此基础上进行数据分析。

这里需要特别提下,在获取CPU 时间片(jiffies)数据时需要注意,测试工具应该做尽量少的事情(不要同时用dumpsys meminfo获取内存会增加该进程的CPU消耗),减少对被测app性能的影响,选择性能消耗小的方式。建议:工具获取方式从系统文件/proc/(pid)/stat读取CPU jiffies,不做其他测试内存等等事情。

总结

最后,测试数据如果是单次、单个是没意义的,我们通常用两种方法做对比:历史版本对比、竞品对比。

当你要着手给APP做性能测试时,记得分析APP性能测试的三要素:性能指标、测试场景、测试工具,体系化构建你的性能测试任务,让APP性能保持优秀,运行更顺畅。

你可能感兴趣的:(2020-08-17)