1.XR VR AR monado oculus quest pico性能分析工具综述
2.Flink Collector Output 接口源码解析
3.Androidä¸dipï¼dpï¼spï¼ptåpxçåºå«
4.Kubernetes —— Pod 自动水平伸缩源码剖析(上)
5.Alluxio 客户端源码分析
6.聊聊获取屏幕高度这件事
XR VR AR monado oculus quest pico性能分析工具综述
本文综述XR、VR、AR领域的性能分析方法,重点介绍Monado、Oculus Quest、Pico等工具的大盘收盘源码性能分析技术。Monado性能分析工具包括Metrics源码库,其指标定义与写入功能通过环境变量`XRT_METRICS_FILE`实现运行。
Metrics源码库位于gitlab.freedesktop.org,提供指标数据读取和可视化功能。使用cmd.py脚本读取指标pb文件,可视化指标信息。
渲染分析工具RenderDoc通常通过hook现现函数捕获帧数据,以识别应用程序帧渲染过程。对于OpenXR应用程序,RenderDoc API允许捕获xrBeginFrame和xrEndFrame之间的应用程序帧,无需修改应用程序代码。
Monado提供了PerCetto和Tracy两种性能追踪后端。PerCetto是Monado性能追踪的基础,通过一个轻量级的C语言封装实现与Perfetto SDK的集成,用于应用特定的追踪。
Tracy工具则针对Linux和Windows系统,支持实时数据流查看,仅能同时跟踪一个应用。而Perfetto则支持Linux和安卓系统,同时执行多个进程和系统级跟踪。
Monado还提供了其他性能分析工具,如Compositor的FPS指标、Frame Times、Readback等功能,帮助优化OXR_DEBUG_GUI工作流程。此外,Monado支持使用Android GPU Inspector进行GPU性能分析。
此外,Oculus提供了OVR Metrics Tool,结合RenderDoc和Logcat VrApi日志,实现Oculus应用程序的性能监控。Snapdragon Profiler和ovrgpuprofiler提供GPU性能数据。OVR Metrics Tool提供报告模式和性能HUD模式,支持高级性能指标显示。
Pico Metrics Tool是Pico设备上的性能监控工具,提供实时监控和指标更新功能。源码分享源码不同版本更新了性能监控和实时分析工具的特性与性能指标。
总结,这些工具通过跟踪、指标、日志分析等手段,为XR、VR、AR应用提供性能优化与分析支持。通过Perfetto、Tracy、RenderDoc等工具,开发者能够深入了解系统性能瓶颈,优化应用表现。Pico Metrics Tool等实时监控工具则帮助用户直观了解设备运行状况,提升用户体验。
Flink Collector Output 接口源码解析
Flink Collector Output 接口源码解析
Flink中的Collector接口和其扩展Output接口在数据传递中起关键作用。Output接口增加了Watermark功能,是数据传输的基石。本文将深入解析collect方法及相关重要实现类,帮助理解数据传递的逻辑和场景划分。Collector和Output接口
Collector接口有2个核心方法,Output接口则增加了4个功能,WatermarkGaugeExposingOutput接口则专注于显示Watermark值。主要关注collect方法,它是数据发送的核心操作,Flink中有多个Output实现类,针对不同场景如数据传递、Metrics统计、广播和时间戳处理。Output实现类分类
Output类可以归类为:同一operatorChain内的数据传递(如ChainingOutput和CopyingChainingOutput)、跨operatorChain间(RecordWriterOutput)、统计Metrics(CountingOutput)、广播(BroadcastingOutputCollector)和时间戳处理(TimestampedCollector)。示例应用与调用链路
通过一个示例,我们了解了Kafka Source与Map算子之间的数据传递使用ChainingOutput,而Map到Process之间的传递则用RecordWriterOutput。在不同Output的选择中,objectReuse配置起着决定性作用,影响性能和安全性。 总结来说,ChainingOutput用于operatorChain内部,源码dnf源码RecordWriterOutput处理跨chain,CountingOutput负责Metrics,BroadcastingOutputCollector用于广播,TimestampedCollector则用于设置时间戳。开启objectReuse会影响选择的Output类型。阅读推荐
Flink任务实时监控
Flink on yarn日志收集
Kafka Connector更新
自定义Kafka反序列化
SQL JSON Format源码解析
Yarn远程调试源码
State Processor API状态操作
侧流输出源码
Broadcast流状态源码解析
Flink启动流程分析
Print SQL Connector取样功能
Androidä¸dipï¼dpï¼spï¼ptåpxçåºå«
1ãæ¦è¿°
è¿å»ï¼ç¨åºåé常
以åç´ ä¸ºåä½è®¾è®¡è®¡ç®æºç¨æ·çé¢ãä¾å¦ï¼å¾ç大å°ä¸ºÃåç´ ãè¿æ ·å¤ççé®é¢å¨äºï¼å¦æå¨ä¸ä¸ªæ¯è±å¯¸ç¹æ°ï¼dpiï¼æ´é«çæ°æ¾ç¤ºå¨ä¸è¿è¡è¯¥ç¨åºï¼å
ç¨æ·çé¢ä¼æ¾å¾å¾å°ãå¨æäºæ åµä¸ï¼ç¨æ·çé¢å¯è½ä¼å°å°é¾ä»¥çæ¸ å 容ãç±æ¤æ们éç¨ä¸å辨çæ å ³ç度éåä½æ¥å¼åç¨åºå°±è½å¤è§£å³è¿ä¸ªé®é¢ãAndroid
åºç¨å¼åæ¯æä¸åç度éåä½ã
2ã度éåä½å«ä¹
dip: device independent pixels(设å¤ç¬ç«åç´ ). ä¸å设å¤æä¸åçæ¾ç¤ºææ,è¿ä¸ªå设å¤ç¡¬ä»¶æå ³ï¼ä¸è¬æ们为äºæ¯æWVGAãHVGAåQVGA æ¨è使ç¨è¿ä¸ªï¼ä¸ä¾èµåç´ ã
dp: dipæ¯ä¸æ ·ç
px: pixels(åç´ ). ä¸å设å¤æ¾ç¤ºææç¸åï¼ä¸è¬æ们HVGA代表xåç´ ï¼è¿ä¸ªç¨çæ¯è¾å¤ã
pt: pointï¼æ¯ä¸ä¸ªæ åçé¿åº¦åä½ï¼1ptï¼1/è±å¯¸ï¼ç¨äºå°å·ä¸ï¼é常ç®åæç¨ï¼
sp: scaled pixels(æ¾å¤§åç´ ). 主è¦ç¨äºåä½æ¾ç¤ºbest for textsizeã
inï¼è±å¯¸ï¼ï¼é¿åº¦åä½ã
mmï¼æ¯«ç±³ï¼ï¼é¿åº¦åä½ã
3ã度éåä½çæ¢ç®å ¬å¼
å¨androidæºç å TypedValue.javaä¸ï¼æ们çå¦ä¸å½æ°ï¼
å¤å¶ä»£ç 代ç å¦ä¸:
public static float applyDimension(int unit, float value,
DisplayMetrics metrics)
{
switch (unit) {
case COMPLEX_UNIT_PX:
return value;
case COMPLEX_UNIT_DIP:
return value * metrics.density;
case COMPLEX_UNIT_SP:
return value * metrics.scaledDensity;
case COMPLEX_UNIT_PT:
return value * metrics.xdpi * (1.0f/);
case COMPLEX_UNIT_IN:
return value * metrics.xdpi;
case COMPLEX_UNIT_MM:
return value * metrics.xdpi * (1.0f/.4f);
}
return 0;
}
该å½æ°åè½ï¼æ¯æååä½æ¢ç®ä¸ºåç´ ã
metrics.densityï¼é»è®¤å¼ä¸ºDENSITY_DEVICE / (float) DENSITY_DEFAULT;
metrics.scaledDensityï¼é»è®¤å¼ä¸ºDENSITY_DEVICE / (float) DENSITY_DEFAULT;
metrics.xdpiï¼é»è®¤å¼ä¸ºDENSITY_DEVICE;
DENSITY_DEVICEï¼ä¸ºå±å¹å¯åº¦
DENSITY_DEFAULTï¼é»è®¤å¼ä¸º
4ãå±å¹å¯åº¦ï¼è¡¨ç¤ºæ¯è±å¯¸æå¤å°ä¸ªæ¾ç¤ºç¹ï¼ä¸å辨çæ¯ä¸¤ä¸ªä¸åçæ¦å¿µã
Android主è¦æ以ä¸å ç§å±ï¼å¦ä¸è¡¨
å¹
Tyep
宽度
Pixels
é«åº¦
Pixels
尺寸
Range(inches)
å±å¹å¯åº¦
QVGA
2.6-3.0
low
WQVGA
3.2-3.5
low
FWQVGA
3.5-3.8
low
HVGA
3.0-3.5
Medium
WVGA
3.3-4.0
High
FWVGA
3.5-4.0
High
WVGA
4.8-5.5
Medium
FWVGA
5.0-5.8
Medium
å¤æ³¨
ç®åandroidé»è®¤çlow= ï¼Medium =ï¼ High =
5ã综ä¸æè¿°
æ®px = dip * density / ï¼åå½å±å¹å¯åº¦ä¸ºæ¶ï¼px = dip
æ ¹æ® google ç建议ï¼TextView çåå·æå¥½ä½¿ç¨ sp ååä½ï¼èä¸æ¥çTextViewçæºç å¯ç¥Androidé»è®¤ä½¿ç¨spä½ä¸ºåå·åä½ãå°dipä½ä¸ºå ¶ä»å ç´ çåä½ã
èµææ¥æºï¼/question/.html?entry=qb_ihome_tag
Kubernetes —— Pod 自动水平伸缩源码剖析(上)
ReplicaSet 控制器负责维持指定数量的 Pod 实例正常运行,这个数量通常由声明的工作负载资源对象如 Deployment 中的.spec.replicas字段定义。手动伸缩适用于对应用程序进行预调整,如在电商促销活动前对应用进行扩容,活动结束后缩容。然而,这种方式不适合动态变化的应用负载。
Kubernetes 提供了 Pod 自动水平伸缩(HorizontalPodAutoscaler,简称HPA)能力,允许定义动态应用容量,容量可根据负载情况变化。例如,当 Pod 的平均 CPU 使用率达到 %,且最大 Pod 运行数不超过 个时,HPA 会触发水平扩展。
HPA 控制器负责维持资源状态与期望状态一致,即使出现错误也会继续处理,直至状态一致,称为调协。控制器依赖 MetricsClient 获取监控数据,包括 Pod 的 CPU 和内存使用情况等。
MetricsClient 接口定义了获取不同度量指标类别的监控数据的能力。实现 MetricsClient 的客户端分别用于集成 API 组 metrics.k8s.io,处理集群内置度量指标,自定义度量指标和集群外部度量指标。
HPA 控制器创建并运行,依赖 Scale 对象客户端、HorizontalPodAutoscalersGetter、Metrics 客户端、HPA Informer 和 Pod Informer 等组件。Pod 副本数计算器根据度量指标监控数据和 HPA 的理想资源使用率,决策 Pod 副本容量的伸缩。
此篇介绍了 HPA 的基本概念和相关组件的创建过程,后续文章将深入探讨 HPA 控制器的调协逻辑。感谢阅读,欢迎指正。源码庄源码
Alluxio 客户端源码分析
Alluxio是一个用于云分析和人工智能的开源数据编排技术,作为分布式文件系统,采用与HDFS相似的主从架构。系统中包含一个或多个Master节点存储集群元数据信息,以及Worker节点管理缓存的数据块。本文将深入分析Alluxio客户端的实现。
创建客户端逻辑在类alluxio.client.file.FileSystem中,简单示例代码如下。
客户端初始化包括调用FileSystem.Context.create创建客户端对象的上下文,在此过程中需要初始化客户端以创建与Master和Worker连接的连接池。若启用了配置alluxio.user.metrics.collection.enabled,将启动后台守护线程定时与Master节点进行心跳传输监控指标信息。同时,客户端初始化时还会创建负责重新初始化的后台线程,定期从Master拉取配置文件的哈希值,若Master节点配置发生变化,则重新初始化客户端,期间阻塞所有请求直到重新初始化完成。
创建具有缓存功能的客户端在客户端初始化后,调用FileSystem.Factory.create进行客户端创建。客户端实现分为BaseFileSystem、MetadataCachingBaseFileSystem和LocalCacheFileSystem三种,其中MetadataCachingBaseFileSystem和LocalCacheFileSystem对BaseFileSystem进行封装,提供元数据和数据缓存功能。BaseFileSystem的调用主要分为三大类:纯元数据操作、读取文件操作和写入文件操作。针对元数据操作,直接调用对应GRPC接口(例如listStatus)。接下来,将介绍客户端如何与Master节点进行通信以及读取和写入的流程。
客户端需要先通过MasterInquireClient接口获取主节点地址,当前有三种实现:PollingMasterInquireClient、SingleMasterInquireClient和ZkMasterInquireClient。其中,PollingMasterInquireClient是针对嵌入式日志模式下选择主节点的实现类,SingleMasterInquireClient用于选择单节点Master节点,ZkMasterInquireClient用于Zookeeper模式下的主节点选择。因为Alluxio中只有主节点启动GRPC服务,其他节点连接客户端会断开,PollingMasterInquireClient会依次轮询所有主节点,直到找到可以连接的扒皮源码源码节点。之后,客户端记录该主节点,如果无法连接主节点,则重新调用PollingMasterInquireClient过程以连接新的主节点。
数据读取流程始于BaseFileSystem.openFile函数,首先通过getStatus向Master节点获取文件元数据,然后检查文件是否为目录或未写入完成等条件,若出现异常则抛出异常。寻找合适的Worker节点根据getStatus获取的文件信息中包含所有块的信息,通过偏移量计算当前所需读取的块编号,并寻找最接近客户端并持有该块的Worker节点,从该节点读取数据。判断最接近客户端的Worker逻辑位于BlockLocationUtils.nearest,考虑使用domain socket进行短路读取时的Worker节点地址一致性。根据配置项alluxio.worker.data.server.domain.socket.address,判断每个Worker使用的domain socket路径是否一致。如果没有使用域名socket信息寻找到最近的Worker节点,则根据配置项alluxio.user.ufs.block.read.location.policy选择一个Worker节点进行读取。若客户端和数据块在同一节点上,则通过短路读取直接从本地文件系统读取数据,否则通过与Worker节点建立GRPC通信读取文件。
如果无法通过短路读取数据,客户端会回退到使用GRPC连接与选中的Worker节点通信。首先判断是否可以通过domain socket连接Worker节点,优先选择使用domain socket方式。创建基于GRPC的块输入流代码位于BlockInStream.createGrpcBlockInStream。通过GRPC进行连接时,每次读取一个chunk大小并缓存chunk,减少RPC调用次数提高性能,chunk大小由配置alluxio.user.network.reader.chunk.size.bytes决定。
读取数据块完成后或出现异常终止,Worker节点会自动释放针对该块的写入锁。读取异常处理策略是记录失败的Worker节点,尝试从其他Worker节点读取,直到达到重试次数上限或没有可用的Worker节点。
若无法通过本地Worker节点读取数据,则客户端尝试发起异步缓存请求。若启用了配置alluxio.user.file.passive.cache.enabled且存在本地Worker节点,则向本地Worker节点发起异步缓存请求,否则向负责读取该块数据的Worker节点发起请求。
数据写入流程首先向Master节点发送CreateFile请求,Master验证请求合法性并返回新文件的基本信息。根据不同的写入类型,进行不同操作。如果是THROUGH或CACHE_THROUGH等需要直接写入底层文件系统的写入类型,则选择一个Worker节点处理写入到UFS的数据。对于MUST_CACHE、CACHE_THROUGH、ASYNC_THROUGH等需要缓存数据到Worker节点上的写入类型,则打开另一个流负责将每个写入的块缓存到不同的Worker上。写入worker缓存块流程类似于读取流程,若写入的Worker与客户端在同一个主机上,则使用短路写直接将块数据写入Worker本地,无需通过网络发送到Worker上。数据完成写入后,客户端向Master节点发送completeFile请求,表示文件已写入完成。
写入失败时,取消当前流以及所有使用过的输出流,删除所有缓存的块和底层存储中的数据,与读取流程不同,写入失败后不进行重试。
零拷贝实现用于优化写入和读取流程中WriteRequest和ReadResponse消息体积大的问题,通过配置alluxio.user.streaming.zerocopy.enabled开启零拷贝特性。Alluxio通过实现了GRPC的MethodDescriptor.Marshaller和Drainable接口来实现GRPC零拷贝特性。MethodDescriptor.Marshaller负责对消息序列化和反序列化的抽象,用于自定义消息序列化和反序列化行为。Drainable扩展java.io.InputStream,提供将所有内容转移到OutputStream的方法,避免数据拷贝,优化内容直接写入OutputStream的过程。
总结,阅读客户端代码有助于了解Alluxio体系结构,明白读取和写入数据时的数据流向。深入理解Alluxio客户端实现对于后续阅读其他Alluxio代码非常有帮助。
聊聊获取屏幕高度这件事
说起获取屏幕高度,或许你有所理解,但这个高度范围究竟指的是应用显示区域的高度,还是手机屏幕的高度呢?我们先来回顾一下平时使用获取高度的方法:
以上三种方法的效果一致,只是写法略有不同。
或许你使用的是这种方法:
这个方法在系统版本大于等于Android 4.2时,会使用getRealMetrics(getRealSize)来获取屏幕高度。那么这里发生了什么?为什么会这样呢?
其实在Android 4.0时,引入了虚拟导航键。如果你继续使用getMetrics之类的方式获取高度,获取的高度会去除导航栏的高度。
由于在4.0和4.2之间并没有getRealMetrics这个方法,所以当时甚至需要添加适配代码:
现在应该没有人还在适配4.4甚至5.0以下的机型了吧?所以历史的包袱可以放下了。
上面方法名都是getScreenHeight,但这个高度范围到底和你需要的是否一致呢?这需要开发时注意。我的习惯是ScreenHeight指应用显示的高度,不包括导航栏(非全屏下),RealHeight指包含导航栏和状态栏的高度(getRealMetrics)。
PS:以前也使用过AndroidUtilCode这个工具库,里面将前者方法名定义为getAppScreenHeight,后者为getScreenHeight。也是很直观的方法。
下文中我会以自己的习惯,使用ScreenHeight和RealHeight来代表两者。
我印象中华为手机很早就使用了虚拟导航键,如下图(来源):
比较特别的是,当时华为的导航栏还可以显示和隐藏,注意图中左下角的箭头。点击可以隐藏,上滑可以显示。即使这样,使用getScreenHeight也可以准确获取高度,隐藏了ScreenHeight就等于RealHeight。
上述的这一切在“全面屏”时代到来之前,没有什么问题。
小米MIX的发布开启了全面屏时代(年底),以前的手机都是:9的,记得雷布斯在发布会上说过,他们费了很大的力气说服了谷歌去除了:9的限制(从Android 7.0开始)。
全面屏手机是真的香,不过随之也带来适配问题。首当其冲的就是刘海屏,各家有各自的获取刘海区域大小的方法。主要原因还是国内竞争的激烈,各家为了抢占市场,先于谷歌定制了自己的方案。这一点让人想起了万恶的动态权限适配。
其实在刘海屏之下,还隐藏一个导航栏的显示问题,也就是本篇的重点。全面屏追求更多的显示区域,随之带来了手势操作。在手势操作模式下,导航栏是隐藏状态。
本想着可以和上面提到的华为一样,隐藏获取的就是RealHeight,显示就是减去导航栏高度的ScreenHeight。然而现实并不是这样,下表是我收集的一些全面屏手机各高度的数据。
ScreenHeight一栏中括号内表示显示导航栏时获取的屏幕高度。
大致的规律总结如下:
其中vivo手机,屏幕高度加状态栏高度大于真实高度( + > )。本以为差值是刘海高度,但查看vivo文档后发现,vivo刘海固定dp(px),也还是对不上。
一加6最奇怪,三种设置模式。使用侧边全屏手势时底部有一个小条,NavigationBar高度变为。( + = + = )也就是说这种模式也属于有导航栏的情况。
这时如果你需要获取准确的ScreenHeight,只有通过RealHeight - NavigationBar来实现了。
所以首先需要判断当前导航栏是否显示,再来决定是否减去NavigationBar高度。
先看看老牌的判断方法如下:
此方法通过比较ScreenHeight和RealHeight是否相等来判断。如果对比上面表中的数据,那只有OPPO Find X可以判断成功。也有一些方法通过ScreenHeight和RealHeight差值来计算导航栏高度。显然这些方法已无法再使用。
所以搜索了一下相关信息,得到了下面的代码:
可以看到包含了华为、小米、vivo、oppo、三星甚至诺基亚的判断。这就是适配的现实状况,不要妄想寻找什么通用方法,老老实实一个个判断吧。毕竟幺蛾子就是这些厂家搞出来的,厂家魔改教你做人。
这种方法在上面的测试机中都亲测准确有效。
不过这个判断方法不够严谨,比如其他品牌手机使用此方法,那么结果都是false。用这样的结果来计算高度显得不够严谨。
根据前面提到问题发生的原因是全面屏带来的(7.0及以上)。所以我们可以先判断是否是全面屏手机(屏幕长宽比例超过1.以上),然后判断是否显示导航栏,对于不确定的机型,我们还是使用原先的ScreenHeight。尽量控制影响范围。
我整理的代码如下(补充了一加、锤子手机判断):
有人会问,这些key都是哪里来的?毕竟我在厂商文档也没有翻到。
我能想到的办法是查看SettingsProvider,它是提供设置数据的Provider,分有Global、System、Secure三种类型,上面代码中可以看到不同品牌存放在的类型都不同。我们可以通过adb命令查看所有数据,根据navigation等关键字去寻找。比如查看Secure的数据:
或者:
这样如果有上面兼容不到的机型,可以使用这个方法适配。也欢迎你的补充反馈。
费了这么大的劲获取到了准确的高度,可能你会说,还不如直接获取ContentView的高度:
这个结果和上述计算的高度一致,唯一的限制是需要在onWindowFocusChanged之后调用,否则高度为0。这个我们可以根据实际情况自行选用。
第二种情况就是状态栏强制为黑色。这里我怀疑因为这个设置,导致在有刘海的手机上,ScreenHeight不包含状态栏高度。
最糟糕的是第三种,隐藏后状态栏在刘海外。例如Redmi K在开启后,ScreenHeight为,RealHeight为,而关闭时为和。这下连万年不变的RealHeight也变化了,这太不real了,大家自行体会。不过目前发现未影响适配方案,不知其他手机如何。
对于是否隐藏刘海,其实也是有各家的判断的,比如小米:
getSystem源码如下:
它不受资源覆盖的影响,我们可以通过它将值转换回来。
本篇看似聊的获取高度这件事,其实伴随导航栏的发展演进,核心是是如何判断导航栏是否显示。
通过上面的介绍,总结一下就是在“全面屏时代”,如果你想获取屏幕高度,就不要使用ScreenHeight了。否则会出现UI展示上的问题。而且这种问题,线上也不会崩溃,难以发现。以前在支付宝中就发现过PopupWindow弹出高度不正确的问题,过了好久才修复了。
至于屏幕宽度,也不清楚随着折叠屏、环绕屏的到来会不会造成影响。但愿不要吧,碎片化越来越严重了。
最后,如果本文对你有启发有帮助,点个赞可好?