界面fps能達(dá)到60 且每幀間隔在16.7ms左右,用戶流暢度體感為最高。出現(xiàn)掉幀(幀速減少), 抖幀(幀速不均勻),則認(rèn)為會影響到用戶流暢度體驗(yàn)。目前
百度
開發(fā)同學(xué)提供了可以設(shè)置一段時間內(nèi)幀長的Demo,經(jīng)過產(chǎn)品同學(xué)多輪、多人的試驗(yàn)結(jié)論:
連續(xù)5幀幀長為30ms時,能夠在網(wǎng)頁瀏覽的界面滑動時明確感覺到抖動。
單幀幀長為70ms時,能夠在網(wǎng)頁瀏覽的界面滑動時明確感覺到界面頓住。
騰訊
PerfDog Jank計算方法:
1.??????同時滿足以下兩條件,則認(rèn)為是一次卡頓Jank.
a)??????當(dāng)前幀耗時>前三幀平均耗時2倍。
b)??????當(dāng)前幀耗時>兩幀電影幀耗時(1000ms/24*2=84ms)。
2.??????同時滿足兩條件,則認(rèn)為是一次嚴(yán)重卡頓BigJank.
a)??????當(dāng)前幀耗時>前三幀平均耗時2倍。
b)??????當(dāng)前幀耗時>三幀電影幀耗時(1000ms/24*3=125ms)。
1)??????BigJank:1s內(nèi)嚴(yán)重卡頓次數(shù)
2)??????Jank(/10min):平均每10分鐘卡頓次數(shù)。
3)??????BigJank(/10min):平均每10分鐘嚴(yán)重卡頓次數(shù)
目前市面上有些做卡頓評估的方案,但是計算方法取值太過離散,不具備連續(xù)性。我們需要一個能評測當(dāng)前幀率的公式,計算出每幀的評分。來對界面流暢度有個完善的評估。
幾個重要指標(biāo)
1.平均幀率 (平均幀率不能檢測出短時間內(nèi)突然卡頓后又恢復(fù)的情況)
2.幀率方差 (方差只能檢測幀速的波動,如果幀速一直是30,方差也會為0)
3.掉幀卡頓比率? (若某幀時長超過了30ms 則認(rèn)為丟失了一幀)
4.降幀次數(shù) (平均每秒相鄰兩個FPS點(diǎn)下降大于8幀的次數(shù))
5. Ft?當(dāng)前幀耗時
FS = FrameSpeed?= 16.7/Ft : 手機(jī)滿幀率是一秒60幀,平均每幀16.7ms,?假定滿幀速=16.7/16.7 = 1 , 當(dāng)前幀耗時越多,速度越慢,最低趨近于0. 故幀速取值區(qū)間為(0, 1]
由于用戶感知卡頓與幀速和幀速波動值有關(guān), 所以計算流暢值的公式應(yīng)該與用戶當(dāng)前幀速,與幀速的導(dǎo)數(shù)dx相關(guān), 由于導(dǎo)數(shù)的取值范圍為(-∞, +∞), 因此我們使用上下兩幀幀速差值的平方來評估速率波動,整體公式為: 其中 為當(dāng)前幀幀速FS。

如果當(dāng)前幀為連續(xù)的第四幀滿幀速(遇到連續(xù)的4幀,幀速為1)則把當(dāng)前幀流暢值恢復(fù)為100
式中?a=4 ?b=1 t = 0.85 ?t=>(0,1]
公式說明:
其中a?為當(dāng)前幀速的權(quán)重值,??b為當(dāng)前幀與上一幀速波動的權(quán)重值
由于:

使用試驗(yàn)數(shù)據(jù)
[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]
當(dāng)a=1 b=1 時計算出流暢值為
[100, 100, 64, 58, 55, 53, 52, 65, 82, 24, 16, 12, 11, 10, 14, 57, 78, 89, 14, 20]
當(dāng)a=2 b=1 時流暢值為
[100, 100, 60, 54, 53, 52, 52, 76, 92, 27, 14, 11, 10, 10, 42, 80, 93, 97, 17, 47]
當(dāng)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()
