界面FPS流畅度(卡顿值)评估公式

2022-11-27  本文已影响0人  泗水亭长matthew

界面fps能达到60 且每帧间隔在16.7ms左右,用户流畅度体感为最高。出现掉帧(帧速减少), 抖帧(帧速不均匀),则认为会影响到用户流畅度体验。目前

百度

开发同学提供了可以设置一段时间内帧长的Demo,经过产品同学多轮、多人的试验结论:

连续5帧帧长为30ms时,能够在网页浏览的界面滑动时明确感觉到抖动。

单帧帧长为70ms时,能够在网页浏览的界面滑动时明确感觉到界面顿住。

腾讯

PerfDog Jank计算方法:

1.      同时满足以下两条件,则认为是一次卡顿Jank.

a)      当前帧耗时>前三帧平均耗时2倍。

b)      当前帧耗时>两帧电影帧耗时(1000ms/24*2=84ms)。

2.      同时满足两条件,则认为是一次严重卡顿BigJank.

a)      当前帧耗时>前三帧平均耗时2倍。

b)      当前帧耗时>三帧电影帧耗时(1000ms/24*3=125ms)。

1)      BigJank:1s内严重卡顿次数

2)      Jank(/10min):平均每10分钟卡顿次数。

3)      BigJank(/10min):平均每10分钟严重卡顿次数

目前市面上有些做卡顿评估的方案,但是计算方法取值太过离散,不具备连续性。我们需要一个能评测当前帧率的公式,计算出每帧的评分。来对界面流畅度有个完善的评估。

几个重要指标

1.平均帧率 (平均帧率不能检测出短时间内突然卡顿后又恢复的情况)

2.帧率方差 (方差只能检测帧速的波动,如果帧速一直是30,方差也会为0)

3.掉帧卡顿比率  (若某帧时长超过了30ms 则认为丢失了一帧)

4.降帧次数 (平均每秒相邻两个FPS点下降大于8帧的次数)

5. Ft 当前帧耗时

FS = FrameSpeed = 16.7/Ft : 手机满帧率是一秒60帧,平均每帧16.7ms, 假定满帧速=16.7/16.7 = 1 , 当前帧耗时越多,速度越慢,最低趋近于0. 故帧速取值区间为(0, 1]

由于用户感知卡顿与帧速和帧速波动值有关, 所以计算流畅值的公式应该与用户当前帧速,与帧速的导数dx相关, 由于导数的取值范围为(-∞, +∞), 因此我们使用上下两帧帧速差值的平方来评估速率波动,整体公式为: 其中 为当前帧帧速FS。

如果当前帧为连续的第四帧满帧速(遇到连续的4帧,帧速为1)则把当前帧流畅值恢复为100

式中 a=4  b=1 t = 0.85  t=>(0,1]

公式说明:

其中a 为当前帧速的权重值,  b为当前帧与上一帧速波动的权重值

由于:

使用试验数据

[16.7, 16.7, 32, 32, 32, 32, 32, 16.7, 16.7, 70, 170, 170, 170, 170, 16.7, 16.7, 16.7, 16.7, 120, 16.7]

当a=1 b=1 时计算出流畅值为

[100, 100, 64, 58, 55, 53, 52, 65, 82, 24, 16, 12, 11, 10, 14, 57, 78, 89, 14, 20]

当a=2 b=1 时流畅值为

[100, 100, 60, 54, 53, 52, 52, 76, 92, 27, 14, 11, 10, 10, 42, 80, 93, 97, 17, 47]

当a=3 b=1 时流畅值为

[100, 100, 58, 53, 52, 52, 52, 82, 95, 27, 13, 10, 10, 9, 57, 89, 97, 99, 16, 60]

[100.0, 100.0, 61.3, 61.3, 60.2, 58.1, 57.9, 87.1, 91.5, 32.1, 16.7, 18.0, 15.5, 14.4, 66.9, 79.2, 88.9, 95.6, 17.6, 91.8]

import os, sys, math

import queue, random

import matplotlib.pyplot as plt

import numpy as np

class SmoothAlgrithm:

    frameFullTime = 1000/60

    speedQueue = [1.0, 1.0, 1.0]

    smoothQueue = [1.0, 1.0, 1.0]

    smoothLayout = {}

    totalSmooth = 0

    duration = 0

    calDuration = 0

    frameCount = 0

    originSmoothArr = []

    curSmoothArr = []

def __init__(self):

self.smoothLayout = {'A':0, 'B':0, 'C':0, 'D':0, 'E':0}

def getFrameSmooth(self, frameTime):

self.duration += frameTime

if (frameTime < self.frameFullTime):

            frameTime = self.frameFullTime

self.calDuration += frameTime

        speed = self.frameFullTime / frameTime

        avgSpeed = sum(self.speedQueue) / len(self.speedQueue)

        avgSmooth = sum(self.smoothQueue) / len(self.smoothQueue)

if (avgSpeed == 1.0 and speed == 1.0):

            score = 1

            smooth = 100

else:

            score = (4*math.pow(speed, 0.90) - pow((speed - avgSpeed), 2) + avgSmooth) / 5

            smooth = (score*100)

self.speedQueue.insert(0, speed)

del self.speedQueue[-1]

self.smoothQueue.insert(0, score)

del self.smoothQueue[-1]

if (smooth > 80):

self.smoothLayout['A'] =  self.smoothLayout['A'] + 1

elif (smooth > 60):

self.smoothLayout['B'] =  self.smoothLayout['B'] + 1

elif (smooth > 40):

self.smoothLayout['C'] =  self.smoothLayout['C'] + 1

elif (smooth > 20):

self.smoothLayout['D'] =  self.smoothLayout['D'] + 1

else:

self.smoothLayout['E'] =  self.smoothLayout['E'] + 1

self.frameCount += 1

self.originSmoothArr.append(int(smooth))

self.totalSmooth += smooth / speed

self.curSmoothArr.append(int(smooth / speed))

return smooth

def setFrameArr(self, arr):

        smooths = []

for ft in arr:

            smooth = int(self.getFrameSmooth(ft)*10)/10

            smooths.append(smooth)

print(smooths)

def calAvgSmooth(self):

        frameNumber = self.duration / self.frameFullTime

print('Origin smooth:', int(sum(self.originSmoothArr)/self.frameCount))

print('cur smooth:', int(sum(self.curSmoothArr)/frameNumber))

        calFrames = self.calDuration / self.frameFullTime

print('cal smooth:', int(sum(self.curSmoothArr)/calFrames))

def calFPS(self):

print('cur fps:', int(self.frameCount*1000/self.duration))

maxFrameTime = 200

def randomFrametime():

    rdnum = random.randint(0, 100)

if (rdnum < 90): #70%的满帧

return 16;

    rdnum = random.randint(0, 350) #20% 16.7-35

if (rdnum > 167):

return rdnum / 10.0

    rdnum = random.randint(350, maxFrameTime*10) / 10.0 #10 分布在35-200

return rdnum

def randPrduceSmooth():

    smoothAlg = SmoothAlgrithm()

    ftSmoothTuple = [(0,0, []) for x in range(0, maxFrameTime+1)]

for i in range (0, maxFrameTime*1000):

        ft = randomFrametime()

        smooth = smoothAlg.getFrameSmooth(ft)

        idx = int(ft)

        ftSmoothTuple[idx][-1].append(smooth)

for idx in range(16, maxFrameTime+1):

        ftSmooth = ftSmoothTuple[idx]

        count = len(ftSmooth[-1]) or 1

        avgFtSmooth = sum(ftSmooth[-1]) / count

        avgFtSmooth = int(avgFtSmooth*10)/10.0

        ftSmoothTuple[idx] = (avgFtSmooth, count, ftSmooth[-1])

print((idx, count, avgFtSmooth))

    x = np.arange(16,maxFrameTime)

    y = [ftSmoothTuple[idx][0] for idx in range(16,maxFrameTime)]

    plt.plot(x,y,"ob")

    plt.ylabel('smooth')

    plt.xlabel('frametime')

    plt.show()

smoothAlg = SmoothAlgrithm()

#arr = [2, 11.0, 13.4, 16.7, 32, 32, 32, 32, 32, 16.7, 16.7, 70, 170, 170, 170, 170, 16.7, 16.7, 16.7, 16.7, 120, 16.7]

arr = [1 for x in range(0, 60)]

smoothAlg.setFrameArr(arr)

print("framecount: ", len(arr),  "framenumber:",  int(sum(arr)/smoothAlg.frameFullTime), "  duration:", sum(arr))

print("\n orignarr:", smoothAlg.originSmoothArr)

print("\n curarr:", smoothAlg.curSmoothArr)

smoothAlg.calFPS()

smoothAlg.calAvgSmooth()

上一篇下一篇

猜你喜欢

热点阅读