1.美的负源风管机故障灯代码大全
2.谁有通达信pvi和NVI源码
3.钱龙软件的DMI指标怎么用?
4.Android性能优化:定性和定位Android图形性能问题——以后台录屏进程为例
美的风管机故障灯代码大全
E1TH1开路/短路(排气温度)E2TH2开路/短路(1#进盘)
E3TH3开路/短路(1#中盘)
E4TH4开路/短路(2#进盘)
E5TH5开路/短路(2#中盘)
E6TH6开路/短路(3#进盘)
E7TH7开路/短路(3#中盘)
E8TH8开路/短路(环境)
E9TH9开路/短路(总出盘)
EATH开路/短路(过冷进)
EBTH开路/短路(过冷出)
ECTH开路/短路(回气)
EF紧急运行
F0外机存贮器故障
F7环境温度超出运行允许范围
F8四通阀故障
F9冷媒泄漏
FE主从机通讯故障
H0数码过载
H1定速1过载
H2定速2过载
H3定速3过载
H4高压过高
H5高压传达感器故障
H6排气温度过高
L0过热度太小保护
L1低压过低
L2低压传达感器故障
扩展资料:
水冷中央空调降温原理是:根据"水蒸发吸收热量,蒸发面积影响蒸发效率"这一自然物理现象,码负当风机运行时冷气机腔内产生负压,原码使机外空气流进多孔湿润的负源湿帘表面进入腔内,空气与水份充分接触的码负同时使湿帘上的水蒸发,带走大量显热。原码微客服系统源码
使经过湿帘的负源空气干球温度降至接近于机外空气的湿球温度,即冷气机出口的码负干球温度比室外干球温度低5-℃(干热地区可达℃),空气愈干热,原码其温差愈大,负源降温效果越好,码负同时由于环保空调该机利用蒸发降温原理,因此,原码环保空调具有降温和增湿双重功能
谁有通达信pvi和NVI源码
正负成交量指标系统源码的负源我没有我有自己编的和系统数值不同但现实效果一样
{ NVI负成交量}
A:=(CLOSE-REF(CLOSE,1))/REF(CLOSE,1);
PVIX:=IF(V>REF(V,1),A,0);
NVIX:=IF(V<REF(V,1),A,0);
ZNL:=SUM(PVIX,);
FNLL:SUM(NVIX,0);
MA(FNLL,);
{ PVI正成交量}
A:=(CLOSE-REF(CLOSE,1))/REF(CLOSE,1);
PVIX:=IF(V>REF(V,1),A,0);
B:SUM(PVIX,0);
CC:MA(B,);
钱龙软件的DMI指标怎么用?
DMI指标又叫动向指标或趋向指标,其全称叫“Directional Movement Index,码负简称DMI”,是原码通过分析股票价格在涨跌过程中买卖双方力量均衡点的变化情况,即多空双方的力量的变化受价格波动的影响而发生由均衡到失衡的循环过程,从而提供对趋势判断依据的一种技术指标。
原理
DMI指标是通过分析股票价格在涨跌过程中买卖双方力量均衡点的变化情况,即多空双方的力量的变化受价格波动的影响而发生由均衡到失衡的循环过程,从而提供对趋势判断依据的一种技术指标。
DMI指标的基本原理是在于寻找股票价格涨跌过程中,股价藉以创新高价或新低价的功能,研判多空力量,进而寻求买卖双方的均衡点及股价在双方互动下波动的循环过程。在大多数指标中,绝大部分都是以每一日的收盘价的走势及涨跌幅的累计数来计算出不同的分析数据,其不足之处在于忽略了每一日的高低之间的波动幅度。比如某个股票的两日收盘价可能是一样的,但其中一天上下波动的幅度不大,而另一天股价的震幅却在%以上,那么这两日的行情走势的分析意义决然不同,这点在其他大多数指标中很难表现出来。而DMI指标则是把每日的高低波动的幅度因素计算在内,从而更加准确的反应行情的走势及更好的预测行情未来的发展变化。
计算方法
DMI指标的计算方法
DMI指标的计算方法和过程比较复杂,它涉及到DM、TR、DX等几个计算指标和+DI(即PDI,下同)、 -DI(即MDI,下同)、ADX和ADXR等4个研判指标的运算。
1、计算的基本程序
以计算日DMI指标为例,其运算的基本程序主要为:
(1)按一定的规则比较每日股价波动产生的最高价、最低价和收盘价,计算出每日股价的波动的真实波幅TR、+DM、- DM,在运算基准日基础上按一定的天数将其累加,以求n日的TR、+DM和 - DM值。
(2)将n日内的上升动向值和下降动向值分别除以n日内的真实波幅值,从而求出n日内的上升指标+DI和下降指标 - DI。
(3)通过n日内的上升指标+DI和下降指标 - DI之间的差和之比,计算出每日的动向值DX。
(4)按一定的天数将DX累加后平均,求得n日内的平均动向值ADX。
(5)再通过当日的ADX与前面某一日的ADX相比较,计算出ADX的评估数值ADXR。
2、计算的具体过程
(1)计算当日动向值。动向指数的当日动向值分为上升动向、下降动向和无动向三种情况,每日的当日动向值只能是三种情况的一种。
A、上升动向(+DM)+DM代表正趋向变动值即上升动向值,其数值等于当日的最高价减去前一日的最高价。上升动向值必须大于当日最低价减去前一日最低价的绝对值,否则+DM=0。
B、下降动向(- DM)?DM代表负趋向变动值即下降动向值,其数值等于当日的最低价减去前一日的最低价。下降动向值必须大于当日的最高价减去前一日最高价的绝对值,否则- DM=0。
C、无动向
无动向代表当日动向值为“零”的情况,即当日的+DM和- DM同时等于零。有两种股价波动情况下可能出现无动向。一是当当日的最高价低于前一日的最高价并且当日的最低价高于前一日的最低价,二是当上升动向值正好等于下降动向值。
指标源代码
TR := SUM(MAX(MAX(HIGH-LOW,ABS(HIGH-REF(CLOSE,1))),ABS(LOW-REF(CLOSE,1))),N);
HD := HIGH-REF(HIGH,1);
LD := REF(LOW,1)-LOW;
DMP:= SUM(IF(HD>0 AND HD>LD,HD,0),N);
DMM:= SUM(IF(LD>0 AND LD>HD,LD,0),N);
PDI: DMP*/TR;
MDI: DMM*/TR;
ADX: MA(ABS(MDI-PDI)/(MDI+PDI)*,M);
ADXR:(ADX+REF(ADX,M))/2
用法:市场行情趋向明显时,指标效果理想。
PDI(上升方向线) MDI(下降方向线) ADX(趋向平均值)
1.PDI线从下向上突破MDI线,显示有新多头进场,为买进参考信号;
2.PDI线从上向下跌破MDI线,显示有新空头进场,为卖出参考信号;
3.ADX值持续高于前一日时,市场行情可能维持原趋势;
4.ADX值递减,降到以下,且横向行进时,市场气氛为盘整;
5.ADX值从上升倾向转为下降时,表明行情即将反转概率增大。
参数:N 统计天数; M 间隔天数,cd源码输出一般为、6
ADXR线为当日ADX值与M日前的ADX值的均值。
(2)计算真实波幅(TR)
TR代表真实波幅,是当日价格较前一日价格的最大变动值。取以下三项差额的数值中的最大值(取绝对值)为当日的真实波幅:
A、当日的最高价减去当日的最低价的价差。
B、当日的最高价减去前一日的收盘价的价差。
C、当日的最低价减去前一日的收盘价的价差。
TR是A、B、C中的数值最大者
(3)计算方向线DI
方向线DI是衡量股价上涨或下跌的指标,分为“上升指标”和“下降指标”。在有的股市分析软件上,+DI代表上升方向线,
-DI代表下降方向线。其计算方法如下:
+DI=(DM÷TR)×
-DI=-(DM÷TR)×
要使方向线具有参考价值,则必须运用平滑移动平均的原理对其进行累积运算。以日作为计算周期为例,先将日内的+DM、?DM及TR平均化,所得数值分别为+DM、?DM和TR,具体如下:
+DI()=(+DM÷TR)×
-DI()=-(DM÷TR)×
随后计算第天的+DI、-DI或TR时,只要利用平滑移动平均公式运算即可。
例如:
当日的TR=/÷前一日TR+当日TR,上升或下跌方向线的数值永远介于0与之间。
(4)计算动向平均数ADX
依据DI值可以计算出DX指标值。其计算方法是将+DI和—DI间的差的绝对值除以总和的百分比得到动向指数DX。由于DX的波动幅度比较大,一般以一定的周期的平滑计算,得到平均动向指标ADX。具体过程如下:
DX=(DI DIF÷DI SUM) ×
其中,DI DIF为上升指标和下降指标的价差的绝对值DI SUM为上升指标和下降指标的总和
ADX就是DX的一定周期n的移动平均值。
(5)计算评估数值ADXR
在DMI指标中还可以添加ADXR指标,以便更有利于行情的研判。
ADXR的计算公式为:ADXR=(当日的ADX+前一日的ADX)÷2
和其他指标的计算一样,由于选用的计算周期的不同,DMI指标也包括日DMI指标、周DMI指标、月DMI指标、年DMI指标以及分钟DMI指标等各种类型。经常被用于股市研判的是日DMI指标和周DMI指标。虽然它们的计算时的取值有所不同,但基本的计算方法一样。另外,随着股市软件分析技术的发展,投资者只需掌握DMI形成的基本原理和计算方法,无须去计算指标的数值,更为重要的是利用DMI指标去分析、研判股票行情。
多空指标
多空指标包括(+DI多方、-DI空方)
+DI在-DI上方,股票行情以上涨为主;+DI在-DI下方,股票行情以下跌为主。
在股票价格上涨行情中,当+DI向上交叉-DI,是买进信号,相反,当+DI向下交叉-DI,是卖出信号。
-DI从以下上升到以上,股票价格很有可能会有一波中级下跌行情。
+DI从以下上升到以上,股票价格很有可能会有一波中级上涨行情。
+DI和-DI以为基准线上下波动时,该股票多空双方拉锯战,股票价格以箱体整理为主。
趋向指标
趋向指标包括ADX和ADXR,ADX和ADXR是+DI和-DI的引导指标,同时也是判断股票行情的趋势指标.
当ADX从上面下穿ADXR时所形成的交叉点叫做死叉,当ADX与ADXR形成死叉时股票上涨行情将终结,如果ADX和ADXR下行至左右并交织波动时,说明股票将横盘整理,没有上涨行情。
当ADX在以上反转向下,不管股票价格是上涨还是下跌,都即将反转。
当ADX从下面上穿ADXR时,所形成的交叉点叫做ADX金叉ADXR;当ADX与ADXR发生金叉时,预示着股票将出现一波上涨行情,ADX的ADXR运行至以上时,将可能产生一轮中级以上的行情,ADX和ADXR上行至以上时,那么市场将很有可能是翻倍以上的大行情。
当4根线间距收窄时,表明股票行情处于盘整中,DMI指标失真。
编辑研判标准
DMI指标是威尔德大师认为比较有成就和实用的一套技术分析工具。虽然其计算过程比较烦琐,但技术分析软件的运用可以使投资者省去复杂的计算过程,专心于掌握指标所揭示的真正含义、领悟其研判行情的独到的功能。
和其他技术指标不同的是,DMI指标的研判动能主要是判别市场的趋势。在应用时,DMI指标的研判主要是集中在两个方面,一个方面是分析上升指标+DI、下降指标DI和平均动向指标ADX之间的关系,另一个方面是verilog fft 源码对行情的趋势及转势特征的判断。其中,+DI和?DI两条曲线的走势关系是判断能否买卖的信号,ADX则是判断未来行情发展趋势的信号。
一、上升指标+DI和下降指标-DI的研判功能
1、当股价走势向上发展,而同时+DI从下方向上突破-DI时,表明市场上有新多买家进场,为买入信号,如果ADX伴随上升,则预示股价的涨势可能更强劲。
2、当股价走势向下发展时,而同时+DI从上向下突破-DI时,表明市场上做空力量在加强,为卖出信号,如果ADX伴随下降,则预示跌势将加剧。
3、当股价维持某种上升或下降行情时,+DI和-DI的交叉突破信号比较准确,但当股价维持盘整时,应将+DI和-DI交叉发出的买卖信号视为无效。
二、平均动向指标ADX的研判功能
ADX为动向值DX的平均数,而DX是根据+DI和-DI两数值的差和对比计算出来的百分比,因此,利用ADX指标将更有效地判断市场行情的发展趋势。
1、判断行情趋势
当行情走势向上发展时,ADX值会不断递增。因此,当ADX值高于前一日时,可以判断当前市场行情仍在维持原有的上升趋势,即股价将继续上涨,如果+DI和?DI同时增加,则表明当前上升趋势将十分强劲。
当行情走势向下发展时,ADX值会不断递减。因此,当ADX值低于前一日时,可以判断当前市场行情仍维持原有的下降趋势,即股价将继续下跌,如果+DI和-DI同时减少,则表示当前的跌势将延续。
2、判断行情是否盘整
当市场行情在一定区域内小幅横盘盘整时,ADX值会出现递减情况。当ADX值降至以下,且呈横向窄幅移动时,可以判断行情为牛皮盘整,上升或下跌趋势不明朗,投资者应以观望为主,不可依据+DI和-DI的交叉信号来买卖股票。
3、判断行情是否转势
当ADX值在高点由升转跌时,预示行情即将反转。在涨势中的ADX在高点由升转跌,预示涨势即将告一段落;在跌势中的ADX值从高位回落,预示跌势可停止。
分析方法
DMI指标的一般分析方法主要是针对+DI、-DI、ADX等三值之间的关系展开的,而在大多数股市技术分析软件上,DMI指标的特殊研判功能则主要是围绕+DI线(白色线)、-DI线(**线)、ADX线(红色线)和ADXR线(绿色线)等四线之间的关系及DMI指标分析参数的修改和均线先行原则等这三方面的内容而进行的。其中,+DI线在有的软件上是用PDI线表示,意为上升方向线;-DI线是用MDI表示,意为下降方向线.
一、四线交叉原则
1、当+DI线同时在ADX线和ADXR线及?DI线以下(特别是在线以下的位置时),说明市场处于弱市之中,股市向下运行的趋势还没有改变,股价可能还要下跌,投资者应持币观望或逢高卖出股票为主,不可轻易买入股票。这点是DMI指标研判的重点。
2、当+DI线和-DI线同处以下时,如果+DI线快速向上突破?DI线,预示新的主力已进场,股价短期内将大涨。如果伴随大的成交量放出,更能确认行情将向上,投资者应迅速短线买入股票。
3、当+DI线从上向下突破?DI线(或-DI线从下向上突破+DI线)时,此时不论+DI和-DI处在什么位置都预示新的空头进场,股价将下跌,投资者应短线卖出股票或以持币观望为主。
4、定时监控源码当+DI线、-DI线、ADX线和ADXR线等四线同时在线以下绞合在一起窄幅横向运动,说明市场处于波澜不兴,股价处于横向整理之中,此时投资者应以持币观望为主。
5、当+DI线、ADX线和ADXR线等三线同时在线以下的位置,而此时三条线都快速向上发散,说明市场人气旺盛,股价处在上涨走势之中,投资者可逢低买入或持股待涨。(这点中因为-DI线是下降方向线,其对上涨走势反应不灵,故不予以考虑)。
6、对于牛股来说,ADX在以上向下转折,仅仅回落到——之间,随即再度掉头向上攀升,而且股价在此期间走出横盘整理的态势。随着ADX再度回升,股价向上再次大涨,这是股价拉升时的征兆。这种情况经常出现在一些大涨的牛股中,此时DMI指标只是提供一个向上大趋势即将来临的参考。在实际操作中,则必须结合均线系统和均量线及其他指标一起研判。
编辑实战技巧
DMI指标的实战技巧主要集中在DMI指标的PDI、MDI、ADX和ADXR这四条曲线的交叉情况以及PDI曲线所处的位置和运行方向等两个方面(注:分析家软件上的PDI曲线和MDI曲线就是钱龙等软件上的+DI曲线和-DI曲线)。下面以分析家软件上的日参数为(,)的DMI指标为例,来揭示DMI指标的买卖和观望功能。(注:钱龙软件日指标参数与分析家软件上这个参数相对应,其使用方法大体相同)。
一、买卖功能
1、当DMI指标中的PDI、MDI、ADX和ADXR这四条曲线在附近一段狭小的区域内作窄幅盘整,如果PDI曲线先后向上突破MDI、ADX、ADXR曲线,同时股价也带量向上突破中长期均线时,则意味着市场上多头主力比较强大,股价短期内将进入强势拉升阶段,这是DMI指标发出的买入信号。
2、当DMI指标中的PDI、MDI、ADX和ADXR这四条曲线在──这段区域内作宽幅整理,如果PDI曲线先后向下跌破ADX和ADXR曲线时,投资者应密切注意行情会不会反转向下,一旦PDI曲线又向下跌破MDI曲线,同时股价也向下突破中长期均线,则意味着市场上空头主力比较强大,股价短期内还将下跌,这是DMI指标发出的卖出信号。
二、持股持币功能
1、当DMI指标中的PDI曲线分别向上突破MDI、ADX、ADXR后,一直在这三条曲线上运行,同时股价也依托中长期均线向上扬升,则意味着市场上多头力量依然占据优势,股价还将上涨,这是DMI指标比较明显的持股信号,只要PDI曲线没有向下跌破这三条曲线中的任何一条,投资者就可以坚决持股待涨。
2、当DMI指标中的PDI曲线分别向上突破MDI、ADX、ADXR后,如果经过一段时间的高位盘整,PDI曲线向下跌破ADX曲线但在ADXR处获得支撑,并重新调头上行,同时也在中期均线附近获得支撑,则表明市场强势依旧,股价还将上扬,这也是DMI指标的持股信号,投资者还可短线持股待涨。
3、当DMI指标中的PDI曲线向下跌破MDI、ADX、ADXR后,如果PDI曲线一直运行在这三条线下方,并且在以下区域作水平或向下运动,同时股价也被中长期均线压制下行时,则意味着市场上空头力量占绝对优势,股价将继续下跌,这是源码在线文档DMI指标比较明显的持币信号,只要PDI曲线没有向上突破这三条曲线中的任何一条,投资者就应坚决持币观望.
4、当DMI指标中的PDI曲线向下跌破MDI、ADX、ADXR后,如果PDI曲线一直运行在这三条线下方,同时股价还是被中长期均线压制时,则意味着市场上空头力量依然强大,股价还将下跌,这是DMI指标的持币信号,只要DMI曲线没有全部向上突破这三条曲线,投资者还应以持币观望为主.
实际运用时请注意:
第一,尽量避免使用DMI指标从事短线交易,在使用时可将指标周期不妨设置参数7天或天。从事短线炒做的投资者如果偏好参照DMI指标,也不要随意缩短指标周期,必要时可采用分时K线走势图。
第二,将+DI和-DI的交叉讯号视为辅助指标,只用来判断股价涨落的趋势,配合ADX数值来研判行情趋势。在把握买卖点时可以多参考MACD、KDJ等中短线指标。
短线实战技巧
第一, 当DMI指标中ADX的数值低于+DI的数值时,特别是低于以下时,所有的指标显示的买入卖出信号都是无效信号。此外ADXR的数值介于-之间时,也应停用任何一种指标。因此,当使用各类指标前,先用DMI趋向指标对股价的运行趋势进行判断是非常必要的一个环节。如此定义技术指标失效区的真正涵义是,股价此时进入了趋势不明的微利盘整中。由于后市的突破方向不明,因此采用的策略是:没有持股的不宜介入;持股获利的可先退出观望;持股套牢的,要定好止损位,一旦股价向下突破,立即做空出局,待股价企稳时再回补。
第二, DMI趋向指标的典型特性是预示一个趋势行情的转折。DMI指标中的ADX在以上发生向下转折,一般表示股价的持续运行趋势发生转折。此时,如+DI>-DI 则股价为见顶回落;如+DI<-di则表示股价有止跌企稳的迹象(注意:并不一定会有大的反弹)。通常adx由高处回落会下降至左右,但特殊情况下,adx仅下降至-之间,随即再度回头上升,这种情况称为"半空翻转",它经常是大行情来临的前兆。这种情况经常出现在一些疯涨的大黑马股,拉升前的洗盘阶段。此时仅凭dmi指标判断是困难的,必须结合均线系统和均量线加以研判。如在+di>-DI且ADX在以上转折时卖出持股,发生"半空翻转"后,可在ADX>ADXR时反身追入。以免错过后续的大行情。
第三, DMI指标通常不被用来直接指导买卖,而仅是作为一种趋势判断的工具。如果一定要用它来指导买卖,可于+D上穿-DI时买入,-DI上穿+DI时卖出。如果错过该买卖点,则可在ADX>ADXR点提供了最后的机会。
应用原则
1.DMI本身含有+DI、-DI、DX、ADX指标,这几项指标要配合看。除外,配合其它外部指标共同研判。
2.DI上升、下降的幅度均在0至之间。多方实力强,+DI值放大并趋近,股指可能会继续提高。反之,若空方实力强,-DI值放大并趋近- ,股指会继续下落。如果+DI变小并趋近0,反映了多方势头减弱。如果-DI变小并趋于0,反映空方势头减弱。股指分别会止升、止跌。投资者可根据+DI、-DI的变化趋向,摸清多空的实力,择机而动。
3.从相对强弱分析,如果+DI大于-DI,在图形上则表现为+DI线从下向上穿破-DI线,这反映了股市中多方力量加强,股市有可能高走一段,因此,投资者速买再速卖,不可买进惜售,待股价冲顶回落后会造成损失。
如果-DI大于+DI,在图形上则表现为-DI线从下向上穿透+DI线,反映股市中空头正在进场,股市有可能低走。因此,投资者应速卖股票,看准认底部后再买进股票。如果+DI和-DI线交叉且幅度不宽时。表明股市进入盘整行情。投资者要观察一段,待机行事。
4.对DX,投资者应注意:DX活动区间在0-内,如果DX趋向,表明多空某一方的力量趋于零。如果DX值大,表明多空双方实力相差悬殊;如DX值小,表明多空双方实力接近。如果DX趋向零,表明多空双方的实力近似相等。
一般讲,DX值在至间,表明多空双方实力大体相等,轮换主体位置的可能性大。投资者此时应把握自己的位置,看准时机,空头转多头,或相反。DX值穿破,表明多空双方力量拉开,多头或空头各方渐渐主动,或超卖,或超买。DX值穿破,表明多空双方力量均衡,多空双方都主动回撤,买卖不活。此2种情况,投资者既不可过于急躁,又不可过于谨慎,要择机而动,大胆心细。
5.如果DX、DI值同时上升,表明多头实力加强,市场有上升的劲头。投资者应速买而后速卖。如果DX、DI值同时下降,表明空方主力进场,市场下跌不可避免。投资者速卖后,待新底形成再买进。如果DX线位于+DI线上方并回落,表明行情虽在上升,但结束上升行情的时间已到,投资者不可再盲目追涨。如果 DX线位于-DI线上方并回落,表明行情虽在下跌,但下跌的底部已形成,熊市将结束,投资者可适当买进股票。
相关公式
TR := SUM(MAX(MAX(HIGH-LOW,ABS(HIGH-REF(CLOSE,1))),ABS(LOW-REF(CLOSE,1))),N);
HD := HIGH-REF(HIGH,1);
LD := REF(LOW,1)-LOW;
DMP:= SUM(IF(HD>0 AND HD>LD,HD,0),N);
DMM:= SUM(IF(LD>0 AND LD>HD,LD,0),N);
PDI: DMP*/TR;
MDI: DMM*/TR;
ADX: MA(ABS(MDI-PDI)/(MDI+PDI)*,M);
ADXR:(ADX+REF(ADX,M))/2
Android性能优化:定性和定位Android图形性能问题——以后台录屏进程为例
简介发现、定性与定位
总结
跟不上旋律节奏的VSYNC
严重异常耗时的dequeueBuffer
VirtualDisplay合成耗时
结论
FPS
初步定位问题
定性问题
定位问题
成果展示
参考
简介本文记录一次Android图形性能问题的分析过程——发现、定性和定位图形性能问题,以及探讨的性能优化方案。
环境:Android Q + MTK + ARM Mali-G。
所分析的性能问题(下称case):打开录屏应用并启动后台录屏,滑动前台应用(滑屏)。性能表现差:CPU、GPU负载显著升高、掉帧、用户明显卡顿感,帧率不足帧,帧渲染、合成耗时急剧飙升(渲染耗时平均为ms左右)。
经过优化后,相同环境和条件下,渲染帧率稳定在帧(提升一倍),渲染耗时平均为8.ms左右(为优化前的不到三分之一的消耗)。
关键词 Keywords: Screen Recording; Frame rate; FPS; GPU utilization; Jank; MediaProjection; VirtualDisplay; MediaCodec; Perfetto; Inferno; Surface; SurfaceTexture; VSYNC; SurfaceFlinger; HWC; Hardware composer; GPU; OpenGL;
发现、定性与定位FPS计算FPS的方法和工具 Android框架层通过hwui配合底层完成渲染。该框架本身提供了逐帧渲染分段耗时记录。通过dumpsys gfxinfo可以获取。
io.microshow.screenrecorder/io.microshow.screenrecorder.activity.MainActivity/android.view.ViewRootImpl@6b9b8a9?(visibility=0)DrawPrepare?Process?Execute3...................1................使用工具统计帧率与平均耗时(同时打印GPU负载),在开启后台录屏的情况下滑动屏幕,平均渲染耗时高达~ms,超出.ms一倍,导致帧率仅帧,显著低于帧。
Average?elapsed?.?msFPS:??│?9.?0.?.?2.#?GPU负载?LOADING?BLOCKING?IDLE?0?#?case的对比——未开启后台录屏Average?elapsed?9.?msFPS:??│?1.?0.?5.?1.通过gfx柱状图直观感受性能数据 直观地感受图形渲染性能,除了帧率感受、触控延时外,还可以通过将gfxinfo的分段耗时通过柱状图展示在屏幕上。
这是case性能问题的gfxinfo柱状图,可以看到红柱和绿柱都非常高,远远超越了流畅标准。其中,绿柱异常放大表明两个Vsync之间耗时显著增长,红柱异常放大表明应用层应用加速使用的DisplayLists大量增长、或图形层使用GLES调用GPU耗时显著增多导致的GPU执行绘制指令耗时变长。
初步定位问题本节记录初步的分析思路和定位过程。首先我们完成实验(启停后台录屏并滑动屏幕触发渲染)、观测以及记录,拿到了后台录屏启停情况下的FPS、分阶段耗时以及GPU负载(相关数据位于FPS小节)。
开发的工具输出的统计数据计算结果非常直观,一眼可见,后台录屏为Draw阶段带来额外的~8倍或~8ms耗时,给Process阶段带来额外的~2倍或~ms耗时。帧率从帧坠落到~帧。
耗时分析 可以看到,主要的额外耗时来自Draw和Process。接下来重点围绕着两part定位问题问题。
StageDescriptionCompDraw创建DisplayLists的耗时。Android的View如果支持硬件加速,绘制工作均通过DisplayLists由GPU绘制,可以处理为onDraw的耗时额外~8ms或~8倍Prepare准备没有额外耗时ProcessDisplayLists执行耗时。即硬件加速机制下提交给GPU绘制的工作耗时额外~ms或~2倍ExecuteFramebuffer前后缓冲区flip动作的耗时,上屏耗时额外不到~1msHz下,上述4个步骤合计耗时小于.ms为正常情况。case为~ms。主要增量来自Draw和Process。
经过上述初步分析、观测后,接下来的分析可以围绕Draw和Process开展。由于Android Draw部分涉及较广,包含App 渲染线程(DisplayLists)、UI线程(onDraw方法创建DisplayLists),以及图形栈耗时如SurfaceFlinger、RenderEngine等都可能增加Draw耗时。
这里一个技巧可以初步判断耗时来自App进程(渲染线程和UI线程)还是来自图形栈。如果能判断耗时来自App或图形栈,那么可以缩小分析范围、减少分析工作量。上述四大阶段的耗时统计分类比较宽,实际上还有更详细的分阶段耗时,它呈现在前文描述过的gfx统计信息柱状图上。gfx柱状图会以蓝色(RGB(,,))呈现onDraw方法创建和更新DisplayLists的耗时。如果case与正常情况对比后,这部分耗时(蓝柱大小对比)差异很小,即可说明额外的Draw耗时不是来自App的,极可能来自图形栈。Besides,结合过度绘制分析,判断case与正常情况下是否有更多的额外绘制次数可以协同判断。
——根据上述指导思想,排查出了case的额外Draw耗时与App onDraw无关,多出来的DisplayLists来自App以外的进程,可能是图形栈如SurfaceFlinger。
定性问题本小节介绍问题追踪过程,通过一些方法定位到各阶段的耗时原因,并定性地得出case性能问题的性质。从本小节开始,围绕Perfetto进行分析。这里贴出perfetto的总览,我将关键的信息排序到顶部。前四行分别为SF负责图形的线程、提交到GPU等待完成的工作、Vsync-App、Vsync-sf,最后两行为case中出现卡顿掉帧的App的主线程(UI)和渲染线程(RenderThread)。
跟不上旋律节奏的VSYNC容易看到,Vsync-sf非常不规律。Vsync-sf是触发SurfaceFlinger一次合成工作的基于Hardware VSYNC虚拟出来的一个信号。它相对于真实硬件信号(HW_VSYNC)一个规律的偏移(在case设备上,Vsync-app与Vsync-sf都被配置为8.3ms,即硬件VSYNC到达后,虚拟的Vsync-app和Vsync-sf延时8.3ms后发出,分别触发App绘制、SurfaceFlinger合成。
而case的Vsync-sf交错、残次、不齐、无规律,显然工况不佳。它将导致SurfaceFlinger不能按照预期的时间间隔将合成的帧提交到Framebuffer(经过Flip后,被提交的Framebuffer将上屏成为显示器的下一帧图像),出现掉帧/丢帧。
As we can see,case的VSYNC-sf出现严重的漂移(见图,第二行的VSYNC-sf残次不齐、跟不上规律、难看且混乱),这导致了丢帧。(但VSYNC-sf的失控仅表示与丢帧的相关性,并不直接表明因果性。)
VSYNC-sf为什么会出现偏差? 出于功耗的考虑,VSYNC-sf合VSYNC-app并不是一定会触发的。如果app或sf并没有更新画面的需求,那么死板固定地调度它们进行绘制和合成是不必的。编程上,负责触发VSYNC-sf和VSYNC-app的两个EventThread会在requestNextVsync调用后才会将下一个VSYNC-sf或VSYNC-app发出。因此,当(各自EventThread的)requestNextVsync没有调用时,VSYNC-app和VSYNC-sf也就出现漂移。BufferQueueLayer::onFrameAvailable会在应用提交后调用,该方法通过调用SF的signalLayerUpdate触发产生下一个VSYNC-sf。
换而言之,出于功耗,或别的什么原因(比如耗时导致的延期,人家是线程实现的消息队列),SurfaceFlinger的SFEventThread有可能不调用requestNextVsync,这将导致Vsync-sf在窗口期内短暂消失——但是也不会出现参差不齐的情况。结合case的VSYNC信号报告来看,VSYNC-sf信号异常切实地提示了性能问题——它的不规律现象表明前后Vsync之间有异常耗时,而非低功耗机制被激活或无屏幕刷新(case性能问题复现时一直在滑前台应用的屏,它每ms都有画面更新的需求)。
VSYNC-sf虽然出现了偏差,但是它与卡顿问题仅有相关性(或者说它是性能问题的结果),并非因果关系。猜测是其他卡顿问题导致了SF延缓了对VSYNC的request,导致其信号出现漂移。VSYNC-sf信号偏差实质上指导意义重大,因为它能提示我们,问题发生在比App更底层的地方(前文分析的结论),且比SurfaceFlinger提交到Framebuffer更上层的位置(VSYNC-sf用于触发合成,合成完成后提交到屏幕双缓冲区)。
这样,将case性能问题的上下界都确定了,问题分析范围从原先的整个图形栈,有效的缩小到了SurfaceFlinger渲染和合成阶段了。
严重异常耗时的dequeueBuffer通读Perfetto,可以看到,出了难看的Vsync-sf以外,还可以看到刺眼的超长耗时的draw(App UI线程)以及耗时变态长的dequeueBuffer(App 渲染线程)调用。相对于正常情况,perfetto报告提示的case的draw方法成倍增长的耗时非常容易被误认为耗时“居然来自一开始就排除掉的App进程",这与前文提出的”问题范围“是不能自洽的——它们是相反的结论,肯定哪里不对。仔细分析才能发现,draw方法确实是消耗了更多墙上时间(但是不意味着消耗了更多CPU时间,因为等待过程是sleep的),但是draw方法是因为等待渲染线程的dequeueBuffer造成的耗时,而dequeueBuffer的严重异常耗时却是被底层的图形栈拖累的。
我们看到,draw严重耗时,渲染线程dequeueBuffer消耗掉~ms的时间。As we all known,Android的Graphics buffer是生产者消费者模型,当作为消费者的SF来不及处理buffer并释放,渲染线程也就需要额外耗时等待buffer就绪。上面还有一段"Waiting GPU Completion"的trace没有贴上来(下图),这段耗时比不开启后台录屏的case下高得多(~3ms对比~ms),说明了一定的GPU性能问题或SF的性能问题,甚至有可能是Display有问题(HWC release耗时过长也会导致SF释放buf、生产者渲染线程dequeueBuffer额外等待)。
这里的机制比较复杂,不熟悉底层Graphics buffer的流水线模型就不好理解。In one world, dequeueBuffer申请的buffer不是凭空new出来的,而是在App-SurfaceFlinger-Framebuffer这一流水线中循环使用的。流水线中的buffer不是无限的,而是有穷的几个。当底层的伙计,如SF和HWC,使用了buffer但是没有来得及释放时(它们的工作没做完之前不会释放buffer),流水线(可以理解成头尾相接的单向队列(ring buffer))没有可用的buffer,此时dequeueBuffer就不得不进入等待,出现耗时看上去很长的问题。实际上,dequeueBuffer耗时的唯一原因几乎仅仅只有一个:底层消费太慢了,流水线没有剩余buffer,因此需要等待。
这个模型抽象理解非常简单。下图,右边消费者是底层图形栈——它每消费完一个buffer就会释放掉,每释放一个buffer应用层能用的buffer就加1。左边生产者是App渲染线程——它调用dequeueBuffer申请一个buffer以将它的画面绘制到这个buffer上。buffer送入BufferQueue后由右边的消费者(图形栈)进行消费(合成、上屏显示),然后释放buffer。当图形栈来不及release buffer时,dequeueBuffer的调用者(App渲染线程)将由于无可用buffer,就必须挂起等待了,在perfetto上就留下长长的一段”耗时“(实际上是墙上时间,大部分都没有占用CPU)。
以上,这就是为什么说App渲染线程dequeueBuffer严重耗时中的耗时为什么要打引号,为什么要说是被图形层拖累了。
下图可以看到,刨去dequeueBuffer的严重异常耗时,执行渲染的部分耗时相对于正常的case几乎没有差异,这可以断言渲染线程的惨烈耗时主要就是被dequeueBuffer浪费了。
从GPU Completion来看,此时GPU正在为SF工作,因为在图中看到(不好意思没有截全,下图你是看不出来的),dequeueBuffer总是在SF的GPU Completion结束之后结束的,这就表明SF正在通过GPU消费buffer(调用GPU进行合成后提交,然后标记buffer允许被渲染线程dequeue)。dequeueBuffer获取到就绪的buffer此时此刻取决于SF的消费能力——因为case中它是短板。(当然图形层的buffer可用不止SurfaceFlinger需要释放,因为SF释放后buffer实质上流转到更底层的HWC,等它将Buffer提交到屏幕后才会释放,这里释放后才能给App再次使用(上面哪个模型图把SF和HWC合并为流水线的图形层buffer消费者)。
从perfetto报告看HWC release非常及时、余量充足,SF的GPU Completion则较紧密地接着dequeueBuffer返回,基本断言是SF太慢了——排除HWC的责任。(下图看不出来,当时没有截图到HWC的release情况。)
到这里,除了再次确认排除了前台App的问题外,还可以断言问题来自SurfaceFlinger过分耗时。此外将问题范围的下界从整个SF合成流程(上文的Vsync-sf)缩小到了排除HWC的范围。
结论:渲染耗时一切正常,问题出现在SF消费buffer(合成图形)失速了,导致没有可用的buffer供渲染线程使用。从下图的SF的工况(第三列)来看,情况确实如此。
既然一口咬定是SF的锅,那就瞧瞧SF。先看SF的INVALIDATE,这没啥好看的,异常case和正常case都是~2.5ms。主要看refresh,正常case ~6.8ms,异常case ~.8ms。refresh包含SF的合成四件套,包括rebuildLayerStack、CalcuateWorkingSet、Prepare、doComposition。Perfetto报告直接表明,case的后台录屏导致的额外一次合成和配套工作是主要的耗时增量。
之所以会执行两次合成,是因为后台录屏工具编程上通过Android SDK提供的MediaProjection配合VirtualDisplay实现一个虚拟的镜像的屏幕。SurfaceFlinger会将画面输送一份到这个虚拟的Display以实现屏幕图像传送到录屏工具,虚拟的屏幕要求额外的一次合成。从上图可以直接得出结论,case带来的额外工作消耗就是对该录屏用的VirtualDisplay的合成工作(doComposition)带来的。
VirtualDisplay合成耗时由于问题范围已经缩小到了很小的一个范围,在SurfaceFlinger的Refresh过程中,case相对正常应用有巨大的差异耗时,几乎完全来自于对VirtualDisplay的合成耗时(doComposition)。同时也可以看到,两次合成(一次是设备的物理屏幕,一次是case的后台录屏工具创建的虚拟屏幕)中,虚拟屏幕的耗时远远高于物理屏幕(4倍以上)。
通过查看ATRACE的tag(上图,Perfetto中SurfaceFlinger中主线程的各个trace point都是用ATRACE打的tag),结合dumpsys SurfaceFlinger,能直接看到的线索是:
虚拟屏显著耗时,且合成工作通过GLES调用GPU完成
物理屏合成耗时很小,它通过HWC合成
结合图中提示的trace tag、耗时,可以得出结论,使用GPU合成的虚拟屏中因GPU合成耗时很长,导致它显著高于物理屏HWC合成耗时。如果GPU合成能够和HWC合成一样快,或者干脆让虚拟屏也使用HWC合成,那么可以预期SurfaceFlinger的合成工作的消耗将显著降低。
结论本小节综合上述三个小节的分析,对节”定性问题“下一个结论。
耗时的本质已经被看透,录屏工具申请创建的VirtualDisplay没有通过HWC进行合成,而是通过GPU进行合成,它耗时很长导致界面卡顿。In one word,case使用的VirtualDisplay的合成方式不够高效。
HWC是Hardward Composer。它接收图形数据,类似于往桌面(真的桌面,不是电脑和手机的桌面)上面叠放照片和纸张——即合成过程。这个工作能将界面上几个窗口叠加在一起后送到屏幕上显示。通过GLES调动GPU也能干这活,不过HWC执行合成的动作是纯硬件的——它很快,比GPU快几倍。
定位问题前面虽然定性了问题原因是合成方式不够高效,但是没有得出其中的原理——为什么虚拟屏不使用高效的HWC进行合成。本节通过介绍HWC的原理、SurfaceFlinger控制合成方式、虚拟屏Surface特性等来介绍图形栈中合成方式的处理模式。掌握了相关管理后,探讨一些尽量通用的共性的解决方案实现性能优化。最后着重介绍多套优化方案中的一种直面根本原因的解决方法——MediaCodec.MediaFormat创建的支持HWC合成的Surface方案。
SurfaceFlinger如何决定使用HWC还是GPU合成? SurfaceFlinger合成主要可以依靠两条路径。其中之一是”纯硬“的HWC合成(在dumpsys SurfaceFlinger中可以看到Composition type为DEVICE),另一个是通过OpenGL让GPU进行合成(Composition type为CLIENT)。
除非是功耗上的设计,否则SurfaceFlinger总是会优先检查本次合成是否支持使用HWC。编程上,在合成阶段之一的prepare过程中,SurfaceFlinger通过prepareFrame在RenderSurface与Hardware Composer(即HWC)的HIDL服务通信,完成hwc layer的创建。但是,layer能够成功创建不意味着一定支持HWC合成。SurfaceFlinger通过getChangedCompositionTypes向HWC查询不支持HWC合成的Layer。该方法返回的layer如果被标记为CLIENT合成,那么这部分Layer无法由HWC进行合成,而只能通过GPU进行合成——case的VirtualDisplay就是这个情况。
部分layer可能不能由HWC合成的原因(除功耗策略、其他软件策略外):
HWC layer达到上限 Hardware Composer支持的layer数量是有限的。查阅公开资料可知,HWC合成动作属于硬件提供的能力,它们的合成能力受到硬件本身的限制。Google官方资料对Android设备的要求是,HWC最少应该支持4个Layer,分别用于一个常规页面上最常见的4个层:壁纸、状态栏、导航栏和应用窗口。 在case设备中,经过测试,该平台的HWC最多支持7个能进行HWC合成的layer,从第8个layer开始,完完全全只能使用CLIENT合成亦即SurfaceFlinger调用RenderEngine通过OpenGL调动GPU进行合成。 正是由于HWC合成layer有上限,因此在弹出多个弹窗、叠加过于复杂时,即使界面简单也有可能出现比较明显的卡顿。
VirtualDisplay的Surface格式不受HWC支持 HWC的硬件合成能力对buffer(Surface封装)内保存的图像的格式有要求。比如,HWC不能处理缩放,仅支持一部分的格式,大多数都还有其他因素会导致不支持,如旋转、部分Alpha等等。In one word,图像格式的数量是远远多于HWC支持的类型数的。当HWC碰到不支持合成的Surface时,就会在前文提过的getChangedCompositionTypes中通知SurfaceFlinger,由SurfaceFlinger转为使用GPU合成。
结合上述几种情况,设计实验验证。其中通过在物理屏上弹窗来增加Layer以获取HWC Layer上限。确认case无法使用HWC合成不是Layer上限导致的问题后,通过对比来验证Surface格式问题。Surface是对native层的buffer的封装,其类型广泛、实现复杂,一个一个试是不现实的。通过对比性能强劲的类似实现可以一探究竟。Android adb提供一个出厂自带的录屏命令screenrecord、用于测试双屏显示功能的虚拟辅助屏幕(开发者模式-模拟辅助屏)、著名远程窥屏工具scrcpy等三个工具是一系列重要参考。
经过测试,screenrecord和scrcpy创建的VirtualDisplay支持HWC合成——这是优化目标。首先看看它们的实现。
编程上,虚拟辅助屏幕采用了与case一模一样的实现——通过创建VirtualDisplay让图形层额外合成一次屏幕到该虚拟屏幕中。虚拟屏幕本质上将画面发送给录屏功能实现,而非进行显示来完成录屏。
通读screenrecord源码,逻辑上,它与虚拟辅助屏、case录屏应用是相同的——VirtualDisplay录屏。但是编程上略有差异:
screenrecord直接通过binder与SurfaceFlinger通信,获取了raw VirtualDisplay,而