当前位置: 首页 > news >正文

得物App安卓冷启动优化-Application篇

前言

冷启动指标是App体验中相当重要的指标,在电商App中更是对用户的留存意愿有着举足轻重的影响。通常是指App进程启动到首页首帧出现的耗时,但是在用户体验的角度来看,应当是从用户点击App图标,到首页内容完全展示结束。

将启动阶段工作分配为任务并构造出有向无环图的设计已经是现阶段组件化App的启动框架标配,但是受限于移动端的性能瓶颈,高并发度的设计使用不当往往会让锁竞争、磁盘IO阻塞等耗时问题频繁出现。如何百尺竿头更进一步,在启动阶段有限的时间里,将有限的资源最大化利用,在保障业务功能稳定的前提下尽可能压缩主线程耗时,是本文将要探讨的主题。

本文将介绍我们是如何通过对启动阶段的系统资源做统一管控,按需分配和错峰加载等手段将得物App的线上启动指标降低10%,线下指标降低34%,并在同类型的电商App中提升至Top3

一、指标选择

传统的性能监控指标,通常是以Application的attachBaseContext回调作为起点,首页decorView.postDraw任务执行作为结束时间点,但是这样并不能统计到dex加载以及contentProvider初始化的耗时。

因此为了更贴近用户真实体验,在启动速度监控指标的基础上,我们添加了一个线下的用户体感指标,通过对录屏文件逐帧分析,找到App图标点击动画开始播放(图标变暗)作为起始帧,首页内容出现的第一帧作为结束帧,计算出结果作为启动耗时。

例:启动过程为03:00 - 03:88,故启动耗时为880ms。

1.png

二、Application优化

App在不同的业务场景下可能会落到不同的首页(社区/交易/H5),但是Application运行的流程基本是固定的,且很少变更,因此Application优化是我们的首要选择。

得物App的启动框架任务在近几年已经先后做过多轮优化,常规的抓trace寻找耗时点并异步化已经不能带来明显的收益,得从锁竞争,CPU利用率的角度去挖掘优化点,这类优化可能短期收益不会特别明显,但从长远来看能够提前规避很多劣化问题。

1.WebView优化

App在首次调用webview的构造方法时会拉起系统对webview的初始化流程,一般会耗时200+ms,如此耗时的任务常规思路都是直接丢到子线程去执行,但是chrome内核中加入了非常多的线程检查,使得webview只能在构造它的线程中使用。

01.png

为了加速H5页面的启动,App通常会选择在Application阶段就初始化webview并缓存,但是webview的初始化涉及跨进程交互和读文件,因此CPU时间片,磁盘资源和binder线程池中任何一种不足都会导致其耗时膨胀,而Application阶段任务繁多,恰恰很容易出现以上资源短缺的情况。

02.png

因此我们将webview拆分成三个步骤,分散到启动的不同阶段来执行,这样可以降低因为竞争资源导致的耗时膨胀问题,同时还可以大幅度降低出现ANR的几率。

04.png

1.1 任务拆分

a. provider预加载

WebViewFactoryProvider是用于和webview渲染进程交互的接口类,webview初始化的第一步就是加载系统webview的apk文件,构建出classloader并反射创建了WebViewFactoryProvider的静态实例,这一操作并没有涉及线程检查,因此我们可以直接将其交给子线程执行。

10.png

b. 初始化webview渲染进程

这一步对应着chrome内核中的WebViewChromiumAwInit.ensureChromiumStartedLocked()方法,是webview初始化最耗时的部分,但是和第三步是连续执行的。走码分析发现WebViewFactoryProvider暴露给应用的接口中,getStatics这个方法会正好会触发ensureChromiumStartedLocked方法。

至此,我们就可以通过执行WebSettings.getDefaultUserAgent()来达到仅初始化webview渲染进程的目的。

150.png

c. 构造webview

即new Webview()

1.2 任务分配

为了最大程度缩短主线程耗时,我们的任务安排如下:

a. provider预加载,可以异步执行,且没有任何前置依赖,因此放在Application阶段最早的时间点异步执行即可。b. 初始化webview渲染进程,必须在主线程,因此放到首页首帧结束之后。c. 构造webview,必须在主线程,在第二步完成时post到主线程执行。这样可以确保和第二步不在同一个消息中,降低ANR的几率。

160.png

1.3 小结

尽管我们已经将webview初始化拆分为了三个部分,但是耗时占比最高的第二步在低端机或者极端情况还是可能触达ANR的阈值,因此我们做了一些限制,例如当前设备会统计并记录webview完整初始化的耗时,仅当耗时低于配置下发的阈值时,开启上述的分段执行优化。

App如果是通过推送、投放等渠道打开,一般打开的页面大概率是H5营销页,因此这类场景不适用于上述的分段加载,所以需要hook主线程的messageQueue,解析出启动页面的intent信息,再做判断。

受限于开屏广告功能,我们目前只能对无开屏广告的启动场景开启此优化,后续将计划利用广告倒计时的间隙执行步骤2,来覆盖有开屏广告的场景。

170.png

2.ARouter优化

在当下组件化流行的时代,路由组件已经几乎是所有大型安卓App必备的基础组件,目前得物使用的是开源的ARouter框架。

ARouter 框架的设计是它默认会将注解中注册path路径中第一个路由层级 (例如 "/trade/homePage"中的trade)作为该路由信息所的Group, 相同Group路径的路由信息会合并到最终生成的同一个类 的注册函数中进行同步注册。在大型项目中,对于复杂业务线同一个Group下可能包含上百个注册信息,注册逻辑执行过程耗时较长,以得物为例,路由最多的业务线在初始化路由上的耗时已经来到了150+ms。

190.png

路由的注册逻辑本身是懒加载的,即对应Group之下的首个路由组件被调用时会触发路由注册操作。然而ARouter通过SPI(服务发现)机制来帮助业务组件对外暴露一些接口,这样不需要依赖业务组件就可以调用一些业务层的视线,在开发这些服务时,开发者一般会习惯性的按照其所属的组件为其设置路由path,这使得首次构造这些服务的时候也会触发同一个Group下的路由加载。

而在Application阶段肯定需要用到业务模块的服务中的一些接口,这就会提前触发路由注册操作,虽然这一操作可以在异步线程执行,但是Application阶段的绝大部分工作都需要访问这些服务,所以当这些服务在首次构造的耗时增大时,整体的启动耗时势必会随之增长。

2.1 ARouter Service路由分离

ARouter采用SPI设计的本意是为了解耦,Service的作用也应该只是提供接口,所以应当新增一个空实现的Service专门用于触发路由加载,而原先的Service则需要更换一个Group,后续只用于提供接口,如此一来Application阶段的其他任务就不需要等待路由加载任务的完成。

001.png

2.2 ARouter支持并发装载路由

我们在实现了路由分离之后,发现现有的热点路由装载耗时总和是大于Application耗时,而为了保证在进入闪屏页之前完成对路由的加载,主线程不得不sleep等待路由装载完毕。

分析可知ARouter的路由装载方法加了类锁,因为他需要将路由装载到仓库类中的map,这些map是线程不安全的HashMap,相当于所有的路由装载操作其实都是在串行执行,而且存在锁竞争的情况,最终导致耗时累加大于Application耗时。

002.png

分析trace可知耗时主要来自频繁调用装载路由的loadInto操作,再分析这里锁的作用,可知加类锁是主要是为了确保对仓库WareHouse中map操作的线程安全。

003.png

因此我们可以将类锁降级对GroupMeta这个class对象加锁(这个class是ARouter apt生成的类,对应apk中的ARouter$$Provider$$xxx类),来确保路由装载过程中的线程安全,至于在此之前对map操作的线程安全问题,则完全可以通过将这些map替换为concurrentHashMap解决,在极端并发情况下会有一些线程安全问题,也可以按照图中添加判空来解决。

009.png

010.png

至此,我们就实现了路由的并发装载,随后我们根据木桶效应对要预载的service进行合理分组,再放到协程中并发执行,确保最终整体耗时最短。

011.png

012.png

3.锁优化

Application阶段执行的任务多为基础SDK的初始化,其运行的逻辑通常相对独立,但是SDK之间会有依赖关系(例如埋点库会依赖于网络库),且大部分都会涉及读文件,加载so库等操作,Application阶段为了压缩主线程的耗时,会尽可能地将耗时操作放到子线程中并发运行,充分利用CPU时间片,但是这也不可避免的会导致一些锁竞争的问题。

3.1 Load so锁

System.loadLibrary()方法用于加载当前apk中的so库,这个方法对Runtime对象加了锁,相当于一个类锁。

基础SDK在设计上通常会将load so的操作写到类的静态代码块中,确保在SDK初始化代码执行之前就准备好了so库。如果这个基础SDK恰巧是网络库这类基础库,会被很多其他SDK调用,就会出现多个线程同时竞争这个锁的情况。那么在最坏的情况下,此时IO资源紧张,读so文件变慢,并且主线程是锁等待队列中最后一个,那么启动耗时将远超预期。

034.png

为此,我们需要将loadSo的操作统一管控并收敛到一个线程中执行,强制他们以串行的方式运行,这样就可以避免以上情况的出现。值得一提的是,前面webview的provider预加载的过程中也会加载webview.apk中的so文件,因此需要确保preloadProvider的操作也放到这个线程。

so的加载操作会触发native层的JNI_onload方法,一些so可能会在其中执行一些初始化工作,因此我们不能直接调用System.loadLibrary()方法来进行so加载,否则可能会重复初始化出现问题。

我们最终采用了类加载的方式,即将这些so加载的代码全部挪到相关类的静态代码块中,然后再去触发这些类的加载即可,利用类加载的机制确保这些so的加载操作不会重复执行,同时这些类加载的顺序也要按照这些so使用的顺序来编排。

78.png

除此之外,so的加载任务不建议和其他需要IO资源的任务并发执行,在得物App中实测这两种情况下该任务的耗时相差巨大。

4.启动框架优化

目前常见的启动框架设计是将启动阶段的工作分配到一组任务节点中,再由这些任务节点的依赖关系构造出一个有向无环图,但是随着业务迭代,一些历史遗留的任务依赖已经没有存在的必要,但是他会拖累整体的启动速度。

启动阶段大部分工作都是基础SDK的初始化,他们之间往往有着复杂的依赖关系,而我们在做启动优化时为了压缩主线程的耗时,通常都会找出主线程的耗时任务并丢到子线程去执行,但是在依赖关系复杂的Application阶段,如果只是将其丢到异步执行未必能有预期的收益。

99.png

我们在做完webview优化之后发现启动耗时并没有和预期一样直接减少了webview初始化的耗时,而是只有预期的一半左右,经分析发现我们的主线程任务依赖着子线程的任务,所以当子线程任务没有执行完时,主线程会sleep等待。

并且webview之所以放在这个时间点初始化不是因为有依赖限制这它,而是因为这段时间主线程正好有一段比较长的sleep时间可以利用起来,但是异步的任务工作量是远大于主线程的,即便是七个子线程并发在跑,其耗时也是大于主线程的任务。

因此想进一步扩大收益,就得对启动框架中的任务依赖关系做优化。

66.png

671.jpeg

以上第一张图为优化之前得物App启动阶段任务的有向无环图,红框表示该任务在主线程执行。我们着重关注阻塞主线程任务执行的任务。

可以观察到主线程任务的依赖链路上存在几个出口和入口特别多的任务,出口多表明这类任务通常是非常重要的基础库(例如图中的网络库),而入口多表明这个任务的前置依赖太多,他开始执行的时间点波动较大。这两点结合起来就说明这个任务执行结束的时间点很不稳定,并且将直接影响到后续主线程的任务。

这类任务优化的思路主要是:

  • 拆解任务自身,将可以提前执行或者延后执行的操作分出去,但是分出去之前要考虑到对应的时间段还有没有时间片余量,或者会不会加重IO资源竞争的情况出现;
  • 优化该任务的前置任务,让该任务执行结束的时间点尽可能提早,就可以降低后续任务等待该任务的耗时;
  • 移除非必要的依赖关系,例如埋点库初始化只是需要注册一个监听器到网络库,并非发起网络请求。(推荐)

可以看到我们在优化之后的第二张有向无环图里,任务的依赖层级明显变少,入口和出口特别多的任务也都基本不再出现。

044.png

320.png

对比优化前后的trace,也可以看到子线程的任务并发度明显提高,但是任务并发度并不是越高越好,在时间片本身就不足的低端机上并发度越高表现可能会越差,因为更容易出锁竞争,IO等待之类的问题,因此要适当留下一定空隙,并在中低端机上进行充分的性能测试之后再上线,或者针对高中低端机器使用不同的任务编排。

三、首页优化

1.通用布局耗时优化

系统解析布局是通过inflate方法读取布局xml文件并解析构建出view树,这一过程涉及IO操作,很容易受到设备状态影响,因此我们可以在编译期通过apt解析布局文件生成对应的view构建类。然后在运行时提前异步执行这些类的方法来构建并组装好view树,这样可以直接优化掉页面inflate的耗时。

601.png

602.png

2.消息调度优化

在启动阶段我们通常会注册一些ActivityLifecycleListener来监听页面生命周期,或者是往主线程post了一些延时任务,如果这些任务中有耗时操作,将会影响到启动速度,因此可以通过hook主线程的消息队列,将页面生命周期回调和页面绘制相关的msg移动到消息队列的队头,这样就可以加快首页首帧内容展示的速度。

102.png

详情可期待本系列后续内容。

四、稳定性

性能优化对App只能算作锦上添花,稳定性才是生命红线,而启动优化改造的又都是执行时机非常早的Application阶段,稳定性风险程度非常高,因此务必要在准备好崩溃防护的前提下做优化,即便有不可避免的稳定性问题,也要将负面影响降到最低。

1.崩溃防护

由于启动阶段执行的任务都是重要的基础库初始化,因此发生崩溃时将异常识别并吃掉的意义不大,因为大概率会导致后续崩溃或功能异常,因此我们主要的防护工作都是发生问题之后的止血

配置中心SDK的设计通常都是从本地文件中读出缓存的配置使用,待接口请求成功后再刷新。所以如果当启动阶段命中了配置之后发生了crash,是拉不到新配置的。这种情况下只能清空App缓存或者卸载重装,会造成非常严重的用户流失。

109.png

崩溃回退

对所有改动点加上try-catch保护,捕捉到异常之后上报埋点并往MMKV中写入崩溃标记位,这样该设备在当前版本下都不会再开启启动优化相关的变更,随后再抛出原异常让他崩溃掉。至于native crash则是在Crash监控的native崩溃回调里执行同样操作即可。

1100.png

运行状态检测

Java Crash我们可以通过注册unCaughtExceptionHandler来捕捉到,但是native crash则需要借助crash监控SDK来捕捉,但是crash监控未必能在启动最早的时间点初始化,例如Webview的Provider的预加载,以及so库的预加载都是早于crash监控,而这些操作都涉及native层的代码。

为了规避这种场景下的崩溃风险,我们可以在Application的起始点埋入MMKV标记位,在结束点改为另一个状态,这样一些执行时间早于配置中心的代码就可以通过获取这个标记位来判断上一次运行是否正常,如果上次启动发生了一些未知的崩溃(例如发生在crash监控初始化之前的native崩溃),那么通过这个标记位就可以及时关闭掉启动优化的变更。

结合崩溃之后自动重启的操作,在用户视角其实是观察不到闪退的,只是会感觉到启动的耗时约是平时的1-2倍。

0456.png

配置有效期

线上的技改变更通常都会配置采样率,结合随机数实现逐渐放量,但是配置下发SDK的设计通常都是默认取上次的本地缓存,在发生线上崩溃等故障时,尽管及时回滚了配置,但是缓存的设计会导致用户还会因为缓存遭遇至少一次的崩溃。

为此,我们可以为每一个开关配置加一个配套的过期时间戳,限制当前放量的开关只在该时间戳之前生效,这样在遇到线上崩溃等故障时确保可以及时止血,而且时间戳的设计也可以避免线上配置生效的滞后性导致的crash。

457.png

用户视角下,添加配置有效期前后对比:

678.jpeg

五、总结

至此,我们已经对安卓App中比较通用的冷启动耗时案例做了分析,但是启动优化最大的痛点往往还是App自身的业务代码,应当结合业务需求合理的进行任务分配,如果一味的靠预加载,延迟加载和异步加载是不能从根本上解决耗时问题的,因为耗时并没有消失只是转移,随之而来的可能是低端机启动劣化或功能异常。

做性能优化不仅需要站在用户的视角,还要有全局观,如果因为启动指标算是首页首帧结束就把耗时任务都丢到首帧之后,势必会造成用户后续的体验有卡顿甚至ANR。所以在拆分任务时不仅需要考虑是否会和与其并发的任务竞争资源,还需要考虑启动各个阶段以及启动后一段时间内的功能稳定性和性能是否会受之影响,并且需要在高中低端机器上都验证下,至少要确保都没有劣化的表现。

1.防劣化

启动优化绝不是一次性的工作,它需要长时间的维护和打磨,基础库的一次技改可能就会让指标一夜回到解放前,因此防劣化必须要尽早落地。

通过在关键点添加埋点,可以做到在发现线上指标劣化时迅速定位到劣化代码大概位置(例如xxActivity的onCreate)并告警,这样不仅可以帮助研发迅速定位问题,还可以避免线上特定场景指标劣化线下无法复现的情况,因为单次启动的耗时波动范围最高能有20%,如果直接去抓trace分析可能连劣化的大概范围都难以定位。

例如两次启动做trace对比时,其中一次因为遇到IO阻塞导致某次读文件的操作都明显变慢,而另一次IO正常,这就会误导开发者去分析这些正常的代码,而实际导致劣化的代码可能因为波动正好被掩盖。

2.展望

对于通过点击图标启动的普通场景,默认会在Application执行完整的初始化工作,但是一些层级比较深的功能,例如客服中心,编辑收货地址这类,即使用户以最快速度直接进入这些页面,也是需要至少1s以上的操作时间,所以这些功能相关的初始化工作也是可以推迟到Application之后的,甚至改为懒加载,视具体功能的重要性而定。

通过投放,push来做召回/拉新的启动场景通常占比较少,但是其业务价值要远大于普通场景。由于目前启动耗时主要来源于webview初始化以及一些首页预载相关的任务,如果启动落地页并不需要所有基础库(例如H5页面),那么这些我们就可以将它不需要的任务统统延迟加载,这样启动速度可以得到大幅度增长,做到真正意义上的秒开。

*文/Jordas

本文属得物技术原创,更多精彩文章请看:得物技术官网

未经得物技术许可严禁转载,否则依法追究法律责任!

相关文章:

得物App安卓冷启动优化-Application篇

前言 冷启动指标是App体验中相当重要的指标,在电商App中更是对用户的留存意愿有着举足轻重的影响。通常是指App进程启动到首页首帧出现的耗时,但是在用户体验的角度来看,应当是从用户点击App图标,到首页内容完全展示结束。 将启…...

【实战教程】PHP与七牛云的完美对接

前言: 随着互联网的迅速发展,越来越多的网站和应用程序需要处理大量的图片、视频和其他文件。为了有效地存储和管理这些文件,并提供快速的内容分发服务,开发者们常常依赖于云存储和CDN服务提供商。 七牛云是一家领先的云存储和C…...

Go 接口:nil接口为什么不等于nil?

一、Go 接口的地位 Go 语言核心团队的技术负责人 Russ Cox 也曾说过这样一句话:“如果要从 Go 语言中挑选出一个特性放入其他语言,我会选择接口”,这句话足以说明接口这一语法特性在这位 Go 语言大神心目中的地位。 为什么接口在 Go 中有这么…...

(UM1724) STM32 Nucleo-64 boards User manual

STM32 Nucleo-64 评估板用户手册 0. 前言1. 介绍[^1]2. Ordering information2.1 包装编号说明3. 开发环境4. 跳线端子 ON/OFF 的约定5. 快速入门6. 硬件布局与配置6.10 扩展连接器6.11 ARDUINO 连接器6.12 ST morpho 连接器7. Nucleo-64 评估板信息0. 前言 【相关博文】 【STM…...

SourceInsight - Relation Windows

磨刀不误砍柴工,你使用的工具决定了你的下限。我平时使用较多的代码编辑工具就是SourceInsight,这个工具速度快,操作方便,但处理非常大的项目的性能不是很理想,比如你要是添加整个Linux Kernel的源代码的话。 在使用SI…...

二维数组处理

输入整型二维数组a(5行5列),完成如下要求: 输出二维数组a。 将a的第2行和第4行元素对调后,形成新的二维数组a并按行输出,每个元素之间隔一个空格。(行号从0开始计算)。 用对角线(指二维数组左…...

线性表——(2)线性表的顺序存储及其运算的实现

归纳编程学习的感悟, 记录奋斗路上的点滴, 希望能帮到一样刻苦的你! 如有不足欢迎指正! 共同学习交流! 🌎欢迎各位→点赞 👍 收藏⭐ 留言​📝 看到美好,感受美好&a…...

数据结构 -- 图论之最小生成树

目录 1.最小生成树算法 1.Kruskal算法 2.Prim算法 1.最小生成树算法 定义:最小生成树算法:连通图有n个顶点组成,那么此时的图的每一个点都能相互连接并且边的个数为n-1条,那么此时该图就是最小生成树. 下面量算法有几个共同的特点: 1.只能使用图中权值最小的边来构造生成树 …...

【已解决】游戏缺少xinput1_3.dll的详细解决方案与详情解析

在现代科技日新月异的时代,电脑已经成为我们生活和工作中不可或缺的工具。然而,由于各种原因,电脑可能会出现一些问题,其中之一就是xinput1_3.dll文件的缺失。本文将详细介绍xinput1_3.dll丢失对电脑的影响以及丢失的原因&#xf…...

华天动力-OA8000 MyHttpServlet 文件上传漏洞复现

0x01 产品简介 华天动力OA是一款将先进的管理思想、 管理模式和软件技术、网络技术相结合,为用户提供了低成本、 高效能的协同办公和管理平台。 0x02 漏洞概述 华天动力OA MyHttpServlet 存在任意文件上传漏洞,未经身份认证的攻击者可上传恶意的raq文件…...

小航助学题库蓝桥杯题库c++选拔赛(23年8月)(含题库教师学生账号)

需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统(含题库答题软件账号) 需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统(含题库答题软件账号)...

[Ubuntu 18.04] RK3399搭建NFS服务实现共享目录

NFS(Network File System)是一种分布式文件系统协议,允许远程计算机通过网络访问存储在另一台计算机上的文件。它使得多台计算机可以共享文件,并且可以在不同计算机之间实现文件的透明访问和共享。 以下是 NFS 服务器的一些特点和介绍: 文件共享:NFS 服务器允许将存储在…...

Java---抽象类讲解

文章目录 1. 抽象类概述2. 抽象类特点3. 抽象类的成员特点4. 抽象类猫狗应用 1. 抽象类概述 在Java中,一个没有方法体的方法应该定义为抽象方法;而类中如果有抽象方法,该类必须定义为抽象类。 2. 抽象类特点 1. 抽象类和抽象方法必须使用abst…...

CNAS认可是什么?CNAS软件测试报告如何获取?

一、CNAS认可是什么?   CNAS认可是指中国合格评定国家认可委员会的认可程序。CNAS是中国最高级别的认可机构,负责审核和认可符合国家标准的实验室、检测机构和认证机构。通过CNAS认可,机构可以获得国际公认的认可证书,证明其测试结果和认证…...

Tomcat 修改版本号

lib 目录下增加文件 /lib/org/apache/catalina/util/ServerInfo.properties ServerInfo.properties文件里面只需要输入server.info显示的版本号 其他可配置信息 server.infonginx server.number22.0 server.builtMay 11 2023 08:22:10 UTC 显示效果...

Python算法——霍夫曼编码树

Python中的霍夫曼编码树 霍夫曼编码是一种用于数据压缩的技术,通过构建霍夫曼编码树(Huffman Tree)来实现。这篇博客将详细讲解霍夫曼编码树的原理、构建方法和使用方式,并提供相应的Python代码实现。 霍夫曼编码原理 霍夫曼编…...

hql面试题之上海某资深数仓开发工程师面试题-求不连续月份的月平均值

1.题目 A,B两组产品的月平均值,月平均值是当月的前三个月值的一个平均值,注意月份是不连续的,如果当月的前面的月份不存在,则为0。如A组2023-04的月平均值为2023年1月的数据加2023-02月的数据的平均值,因为没有其他月…...

VT驱动开发

VT技术(编写一个VT框架) 1.VT技术介绍 1.技术介绍 1.VT技术 VT技术是Intel提供的虚拟化技术,全称为Intel Virtualization Technology。它是一套硬件和软件的解决方案,旨在增强虚拟化环境的性能、可靠性和安全性。VT技术允许在一台物理计算机上同时运…...

火柴人版王者-Java

主类 package com.sxt; import com.sxt.beast.Beast; import java.awt.Component; import java.awt.Graphics; import java.awt.Image; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter…...

docker 中的–mount 和-v 参数有啥区别

docker 中的–mount 和-v 参数有啥区别 --mount 和 -v 是 Docker 中用于挂载卷(Volumes)的两种不同的方式。 --mount 参数: 这是一种更为灵活和强大的挂载方式,允许你指定多个选项。 使用 --mount 参数,你可以指定挂…...

变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析

一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

uniapp中使用aixos 报错

问题: 在uniapp中使用aixos,运行后报如下错误: AxiosError: There is no suitable adapter to dispatch the request since : - adapter xhr is not supported by the environment - adapter http is not available in the build 解决方案&…...

Map相关知识

数据结构 二叉树 二叉树,顾名思义,每个节点最多有两个“叉”,也就是两个子节点,分别是左子 节点和右子节点。不过,二叉树并不要求每个节点都有两个子节点,有的节点只 有左子节点,有的节点只有…...

精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南

精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...

基于 TAPD 进行项目管理

起因 自己写了个小工具,仓库用的Github。之前在用markdown进行需求管理,现在随着功能的增加,感觉有点难以管理了,所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD,需要提供一个企业名新建一个项目&#…...

Git 3天2K星标:Datawhale 的 Happy-LLM 项目介绍(附教程)

引言 在人工智能飞速发展的今天,大语言模型(Large Language Models, LLMs)已成为技术领域的焦点。从智能写作到代码生成,LLM 的应用场景不断扩展,深刻改变了我们的工作和生活方式。然而,理解这些模型的内部…...

【Elasticsearch】Elasticsearch 在大数据生态圈的地位 实践经验

Elasticsearch 在大数据生态圈的地位 & 实践经验 1.Elasticsearch 的优势1.1 Elasticsearch 解决的核心问题1.1.1 传统方案的短板1.1.2 Elasticsearch 的解决方案 1.2 与大数据组件的对比优势1.3 关键优势技术支撑1.4 Elasticsearch 的竞品1.4.1 全文搜索领域1.4.2 日志分析…...