Unity的AssetBundle资源运行内存管理的再次深入思考

news2024/11/25 12:39:22

  大家好,我是阿赵。
  这篇文章我想写了很久,是关于Unity项目使用AssetBundle加载资源时的内存管理的。这篇文章不会分享代码,只是分享思路,思路不一定正确,欢迎讨论。
  对于Unity引擎的资源内存管理,我猜很多朋友都存在一定的疑惑。疑惑的点有非常多,包括资源怎样才能避免冗余,怎样才能不会在内存里面创建重复的资源内存,怎样才能在合适的时机把不需要的资源内存清理干净,什么时候能把AssetBundle本身清理掉。
  在这方面,我觉得Unity官方一直没有给用户一个很好的指导。或者说,使用Unity进行开发的游戏研发厂商们对引擎的使用,有点超出了Unity自己的预想,导致对于复杂的游戏类型,Unity本身已经不能用单一的策略去处理,只能提供自己的API,让厂商自己想办法做管理策略了。
  关于AssetBundle的使用基础,网上有很多文章介绍,我这里重点不是介绍基础用法,所以我会直接跳过介绍。然后我这里得出了很多测试结论,测试的过程我也不想逐个说明,因为基本上网上都有很多同类的测试。所以接下来,我会直接说出测试结果,然后提出我自己的解决方案。

一、资源对内存的占用分析

  通过AssetBundle加载的资源,在游戏运行时会产生多种内存,比较明显的有以下几种:

1、AssetBundle本身的内存占用

  如果是通过AssetBundle.LoadFromFile类方法加载,在没有LoadAsset之前,AssetBundle本身占用的内存很少,只是一个头文件的数据引用。
  如果是通过AssetBundle.LoadFromMemory类方法加载,那么文件的二进制是完全读取进入内存的,所以AssetBundle文件越大,占用的内存就越大。
  由于AssetBundle是互相依赖的,比如一个模型的预设AssetBundle,可能会依赖其他材质球、贴图、动画之类的AssetBundle,所以在加载主AssetBundle时,需要加载一堆依赖的AssetBundle。
  值得注意的是,对于同一个AssetBundle,运行时是不可能重复加载的,如果尝试重复加载同一个AssetBundle会有报错。所以同一个AssetBundle是不存在同时占用多份内存的情况。

2、Asset资源的内存占用

  这里的Asset资源,指的是通过AssetBundle.LoadAsset类方法,把AssetBundle里面的具体资源读取出来之后资源,比如预设Object、网格、材质球、贴图、动画、shader之类。
  Asset的资源占用才是占用内存的主要部分。当资源被LoadAsset出来之后,它会从二进制文件反序列化变成网格数据、贴图数据等。资源本身的复杂程度越高,占用的内存就越大。
  值得注意的是Asset的资源内存是可能重复的,而且出现重复的条件很多。比如有以下这些情况:
1.一张贴图同时被2个模型使用了,不同模型单独生成了AssetBundle,但这张作为共用依赖的贴图没有单独作为一个AssetBundle,而是同时包含在A、B两个模型的AssetBundle里面。那么运行时同时加载A、B两个模型,虽然看起来它们使用了同一张贴图,实际上内存里面是有2张重复的贴图的内存的。
2.AssetBundle.Unload(false)卸载之后,该AssetBundle内的资源还留在内存里,再重新加载同一个AssetBundle并LoadAsset资源,内存里面的资源就会有重复多份。

3、实例化对象的内存占用

  这里指的实例化,是指通过Object.Instantiate把预设的Object在场景里面实例化成GameObject。GameObject在场景里面肯定是有内存占用的,但它对自身的资源比如网格、贴图等,只是一个引用关系,并不会复制多一份资源本身。所以实例化GameObject占用的内存不会很大。

二、资源管理在理想中想达到的目的

  通过上面的分析,Asset内存占用是最大的,我们必须首先要避免重复,然后不用的时候要清除内存。
  实例化GameObject的内存不大,但也应该在删除GameObject的时候及时清除。
  AssetBundle的占用内存虽然同样不大,不过如果项目很庞大,加载的AssetBundle的数量很多,那么可能也会占用一定量的内存。我们肯定希望在一个AssetBundle不再使用的时候,把它Unload掉。
  所以最理想的状态就是,当我们加载一个资源,会顺便加载该资源依赖的所有AssetBundle,然后当我们确定某个资源不再被使用了之后,就把它们在场景里面删除,卸载Asset本身的内存,然后卸载资源的AssetBundle和依赖的其他AssetBundle。这样内存就干净了。
  这个看起来很清晰合理的目的,在Unity里面实现起来却并不那么容易,存在一些问题,下面会逐步分析。

三、清理资源内存的API

  为了方便理解管理策略,先来明确一下几个不同类型的资源内存的释放API:

1、AssetBundle资源卸载

AssetBundle资源的卸载,是通过AssetBundle.Unload方法实现,可以传入true或者false。
如果传入true会把该AssetBundle里面所有已经加载的资源都卸载掉,是最干净的清理方式,这样不管之前LoadAsset过什么资源,都会直接被清理掉了。如果该AssetBundle的资源还在使用中,那么这些资源都将会丢失。
如果传入false,那么单纯AssetBundle本身的引用内存会被清理掉,但LoadAsset的资源会保留。所以使用这种方式卸载,就算场景里面还有使用资源,也不会丢失。相对的,清理不那么干净,需要自己判断正在使用的资源什么时候释放。

2、Asset资源内存释放

  要卸载Asset的资源,有2个方法

1.Resources.UnloadAsset(Object assetToUnload)

  这个方法可以有针对性的释放某个指定的Asset对象资源。

2.Resources.UnloadUnusedAssets()

  这个方法是Unity自己管理的,Unity会遍历所有的资源,把所有没有被引用的资源卸载掉。这个方法消耗比较大,可能会导致游戏卡顿。

3、实例化资源内存卸载

  而GameObject本身的内存,删除之后会根据GC回收。通过Resources.UnloadUnusedAssets()也能把不再使用的内存回收。

四、需要解决的问题

  AssetBundle打包资源冗余的问题,之前我也写过好几篇文章去分析怎样做才是粒度最合理又没有冗余的。这里就不再重复,主要是说运行中加载卸载的问题。
  从最理想的情况下来看,如果我们能判断得很准确,知道哪些资源已经完全没有用到了,那么直接调用AssetBundle.Unload(true),资源肯定就可以清理得很干净,也不需要管Asset的内存,也不需要Resources.UnloadUnusedAssets。
  不过如果对判断资源使用情况的准确度不是那么的自信,一般是不敢直接AssetBundle.Unload(true)的,因为一个不好,判断错了,那么还在使用的资源就会突然间全部丢失了。所以一般的处理情况是AssetBundle.Unload(false)来清理AssetBundle,然后通过Resources.UnloadUnusedAssets来清理已经不再使用的Asset资源内存。
  我在网上看了一些文章的经验分享,有不少的建议是,对于直接加载读取的资源,可以读取出来Asset的Object之后,把Object缓存,然后立刻对该AssetBundle资源AssetBundle.Unload(false),而对作为依赖加载的AssetBundle做计数器处理。
  我个人认为,这个做法,对于主AssetBundle是prefab类的对象,是可以的。因为这些预制体都是需要实例化到场景里面,只需要在游戏里面做对象池,就可以很容易的知道资源是否还有在使用,也很容易做计数器。
  但如果是对于纯资源类的AssetBundle,比如图片,那就不太行了。比如我从AssetBundle里面加载一张图片,然后立刻Unload(false),那么这张图片的内存在什么时候清理呢?
  如果通过代码将图片存起来反复使用,那么很难判断图片现在还是否存活,举个例子,我把一张图片加载完之后赋予给了一个模型,然后模型本身被主动删除了或者切换场景被删除了,我们是不知道图片还在模型身上被删除的,除非每次模型被Destroy的时候,都遍历身上所有的Component,然后逐个资源类型去判断。那么被代码引用着的这张图片就永远不会回收。如果不用代码存起来,反复需要读取这张图片时,由于之前的AssetBundle被unload(false)了,如果重新加载AssetBundle,那么这张相同的图片,就会在内存里面生成多份不同的内存。
  通过上面的分析可以看出,实际上我们需要解决的问题只有一个,就是怎样很准确的知道我们的资源是否还在存活,是否还被引用,是否可以释放。我们需要一个很准确的依据。
  Unity本身肯定是知道的,因为在使用Resources.UnloadUnusedAssets的时候,Unity会把没用引用的资源都回收掉。但可惜的是,Unity没有提供直接的接口给我们查询。

五、弱引用计数

  C#有一个弱引用(WeakReference)的机制,可以判断一个对象是否存活。
  具体的用法是:

WeakReference weakRefObj = new WeakReference(obj);

然后通过weakRefObj.IsAlive可以判断该对象是否存活,然后通过weakRefObj.Target可以取到之前存进去的那个资源对象。
当一个对象在内存里面已经不存在的时候,这个弱引用对象的IsAlive会变成false,然后Target会变成null。
不过一般情况下,Asset本身是不会自然变成不存在于内存的,因为如果不是执行Resources.UnloadAsset或者Resources.UnloadUnusedAssets,资源并不会随着GameObject的删除而回收的。所以正常的情况下,想要弱引用能正确判断资源已经不存活,必须在删除GameObject时候,调用Resources.UnloadUnusedAssets。
所以如果正常的使用方法,应该是,用从AssetBundle里面LoadAsset得到的asset的对象,创建一个弱引用对象,然后如果需要使用该对象作为资源或者实例化对象的时候,先判断弱引用资源的IsAlive和Target,在保证IsAlive为true,并且Target不为null的情况下,可以取得Target去使用。如果IsAlive为false或者Target为null时,证明这个弱引用关联的资源已经没有任何人在使用了,就可以删除当前的弱引用对象。
不过这个弱引用对象,在Unity里面使用是有问题的,在Unity的自带文档的Overview of .NET in Unity页面里面,有这么一句说明:
在这里插入图片描述

Unity does not currently support the use of the C# WeakReference class
with UnityEngine.Objects. For this reason, you should not use a
WeakReference to reference a loaded asset. See Microsoft’s
WeakReference documentation for more information on the WeakReference
class.

  看了Unity的这个说明,对刚刚找到希望的我们来说,简直是晴天霹雳。不过究竟Unity对弱引用不支持到什么程度呢?
  我自己测试过,实际上在大部分情况下,弱引用对于Unity的Asset对象都是能正常判断的。但在有一种情况下,是会出问题的:
  如果我们从弱引用对象的Target实例化一个GameObject,然后删除这个GameObject并执行一次Resources.UnloadUnusedAssets,并且在很短的时间内再次访问弱引用对象的Target(只要访问,包括读取属性、打log、实例化),这时候,弱引用就大概率的会出错,具体出错的信息是这样的:
在这里插入图片描述

A scripted object (script unknown or not yet loaded) has a different
serialization layout when loading. (Read 32 bytes but expected 120
bytes) Did you #ifdef UNITY_EDITOR a section of your serialized
properties in any of your scripts?

  这个状态下,弱引用里面的Target并不为空,IsAlive也是true的,但Target被破坏了,再次取得这个Target就都是错的了,所有里面的资源都会丢失掉。
  上面提到了大概率会出问题,是在于调用Resources.UnloadUnusedAssets之后,多短时间内再次访问这个弱引用对象的Target,时间并不固定。有时候同一帧访问会出错,有时候又不会。
  这个现象,我做出了一些猜想,很有可能是因为Resources.UnloadUnusedAssets本身是异步处理的,会返回AsyncOperation,并不是同一帧就立刻清理完所有。在这个过程中,如果访问弱引用对象,它还没有完全判断到资源的清空情况,又一次去尝试访问这个资源本身,导致Resources.UnloadUnusedAssets清理到一半失败了。

六、加载和卸载的策略

  到了最后,来总结一下。
  关于怎样去加载和卸载AssetBundle的资源和Asset资源本身。策略可以有很多种,但核心的问题基本上就只有一个,就是什么时候才是真正可以释放资源内存。
  通过上面的分析知道,Unity并没有很明确的API接口让我们知道一个资源是否还在被引用着。弱引用虽然可以判断,但存在一定的问题,Unity本身是不建议我们这样做的。
  那么,接下来就可以分为2种可能性,一种是使用弱引用来判断,另外一种是不使用弱引用判断。

1、使用弱引用的情况

  使用弱引用最大的问题,是在Resources.UnloadUnusedAssets的过程中访问Target,会导致资源释放失败,并让Target永久的被破坏。其实这个时候我们只要重新去AssetBundle里面LoadAsset,那么就能解决Target被破坏的问题。但由于这种情况下弱引用对象的IsAlive是true,而Target也不为null,导致我们不知道Target被破坏了,而导致这个资源一直错下去。
  为了解决这个问题,我们只能严格的控制Resources.UnloadUnusedAssets的使用时机。切换场景也是会默认有UnloadUnusedAssets的调用的。
  于是,整体的加载和卸载过程就会变成这样:
加载:
1、建立一个AssetsMgr管理器,里面通过AssetBundleName和AssetName作为key,可以获取对应的资源。
2、逻辑层通过对象池来申请对象使用,假如对象池里面不存在可用对象,则去AssetsMgr里面获取Asset的Object。
3、AssetMgr里面保存的是WeakReference对象,如果该key没有被保存过,或者WeakReference对象的IsAlive不为true或者WeakReference对象的Target的对象为null,则需要重新去AssetBundleMgr管理器加载资源,并保存成为新的WeakReference。当AssetMgr里面已经有对应的WeakReference,那么将会返回作为Target的Object给对象池那边实例化使用。
4、AssetBundleMgr管理器里面保存着加载过的AssetBundle和它们所有的依赖关系。如果没有加载过对应的AssetBundle,将会在这里加载并返回。
卸载:
1、定时去遍历AssetMgr里面的所有弱引用对象,判断它们是否存活。如果已经不存活了,那么推进一个等待删除的列表,并等待一个短的时间再把它们真正清理。不立刻清理的原因是怕当前帧里面判断到不使用,然后立刻又有地方请求使用。
2、关于AssetBundle的卸载,按道理使用的Object已经存到弱引用管理里面,所以AssetBundle在加载完Asset的Object之后,就可以直接Unload(false)了。不过引用的资源的AssetBundle应该存起来,并通过计数器记录现在的使用情况。如果弱引用里面的主Asset被清理了,那么应该通知依赖AssetBundle计数器减一。当依赖AssetBundle的计数器为0时,则应该去卸载对应的AssetBundle。
特殊处理:
  由于弱引用存在一定的问题,所以要避免Resources.UnloadUnusedAssets或者切换场景之后,立刻去判断弱引用的存活。
  如果是可以频繁切换场景的游戏,比如回合制游戏,每次战斗都需要切换场景。那么可以用切换场景作为时机,每次切换场景时候等于是Resources.UnloadUnusedAssets了一次,然后等待Resources.UnloadUnusedAssets完全结束之后,才开始时机加载模型,并判断弱引用的使用情况。
  如果是不会切换地图场景的游戏,比如大地图SLG那种,建议是所有资源都做成异步加载,定时的执行Resources.UnloadUnusedAssets,然后在等待Resources.UnloadUnusedAssets执行完成之前,先把所有异步加载的请求和弱引用判断停掉,等待Resources.UnloadUnusedAssets结束,再继续执行弱引用加载请求和清理。

2、不使用弱引用的情况

  如果不敢使用弱引用计数的话,那么纯资源类型的引用就不好精确判断。

GameObject类资源

我们还是从对象池入手。
1.对于GameObject类的对象,我们做场景对象池,在每个对象需要实例化的时候,去AssetsMgr获取Object。而AssetsMgr里面同样是通过AssetBundleName和AssetName作为key 去保存资源,不过这时候就不是保存一个弱引用对象,而是保存一个计数器类对象,里面会保存Object本身,还有这个Object被实例化的次数。然后Object的GetInstanceID获得唯一Id,通过这个id也同样存一份对应计数器对象的字典。
2.当Object不存在的时候,去加载AssetBundle,并且加载依赖AssetBundle。然后LoadAsset出资源Object,主AssetBundle执行Unload(false),依赖的AssetBundle计数器加1。
3.当对象实例化的时候,在实例化对象身上挂一个脚本,里面记录着实例化这个GameObject的Object的InstanceID,脚本Awake的时候,抛出创建事件,InstanceID作为参数。AssesMgr里面监听这个事件,对InstanceID的引用加一。
4.当对象被删除的时候,身上的脚本的OnDestroy方法会抛出删除事件,InstanceID作为参数。AssetsMgr监听这个事件,并对InstanceID的引用减一。
5.定时去检查AssetsMgr里面的InstanceID计数器,如果有计数器数量为0的情况,就推入待删除队列,下一帧就把InstanceID对应的计数器删除,并且把该AssetBundle的依赖AssetBundle计数器减一,再检查依赖AssetBundle的计数器有等于0 的,执行Unload(false)。

纯资源

  对于纯资源类的Asset,比如图片之类的,如果不用弱引用,就只有有所取舍了。可以考虑一下这两种方案:
1、读取了AssetBundle里面的图片之后,AssetBundle本身立刻Unload(false),图片保存在代码里面复用。然后通过最后一次申请调用的时间去判断,大于一定时间之后,就清理掉。
这样做的好处是,AssetBundle本身的内存不会再占用了,然后如果很久没有人申请的资源,也只是在代码层面去掉引用,如果场景里面还有,也不会受到影响。
坏处是,如果一张图片在场景里面长久都没删除,代码引用被清理掉了。下次又申请了同一张图片使用,就要重新在AssetBundle加载一次,内存里面就会出现重复资源。
2、纯资源的asset对象不保存在代码引用,它们的AssetBundle也完全不卸载,这样每次需要使用该图片的时候,都从AssetBundle里面LoadAsset。然后定时执行Resources.UnloadUnusedAssets,让没有在使用的图片Asset内存得到释放。
  这样做的好处是,由于每次请求的资源都是从同一次加载的AssetBundle里面读取的,所以同一张图片的内存肯定不会有多份重复的。
  这样做的坏处是,引用AssetBundle占用内存由于没有释放的时机,会一直占用内存。不过实际上AssetBundle本身如果是通过LoadFromFile方法加载的话,AssetBundle不会占用太多的内存。
  至于LoadFromMemory类的方法加载,原则上是不建议这么做的,不过有些时候却迫不得已,比如要加密AssetBundle文件,或者混淆AssetBundle文件,就需要在文件的二进制里面做修改,然后使用时通过读取完整的二进制再进行解密。这种情况下,就肯定不能不释放AssetBundle文件了,不然内存的占用就非常大。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1522993.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【网络原理】TCP 协议中比较重要的一些特性(三)

目录 1、拥塞控制 2、延时应答 3、捎带应答 4、面向字节流 5、异常情况处理 5.1、其中一方出现了进程崩溃 5.2、其中一方出现关机(正常流程的关机) 5.3、其中一方出现断电(直接拔电源,也是关机,更突然的关机&am…

校园博客系统 |基于springboot框架+ Mysql+Java的校园博客系统设计与实现(可运行源码+数据库+设计文档)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 目录 前台功能效果图 管理员功能登录前台功能效果图 系统功能设计 数据库E-R图设计 lunwen参考 摘要 研究…

每日一练:LeeCode-125、验证回文串【字符串+双指针】

如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。 字母和数字都属于字母数字字符。 给你一个字符串 s,如果它是 回文串 ,返回 true ;否则&#…

mysql中的非空间数据导入sqlserver中空间化

以下操作都在Navicat Premium 15软件中操作 1、mysql导出数据 以导出csv为例 不修改导出路径的话默认就是在桌面 设置编码UTF-8 这边还是默认,最好不要修改,如果文本识别符号为空,导入的时候可能字段会错乱 开始即可 2、导入sqlserver数据库中

App的测试,和传统软件测试有哪些区别?增加哪些方面的测试用例

从上图可知,测试人员所测项目占比中,App测试占比是最高的。 这就意味着学习期间,我们要花最多的精力去学App的各类测试。也意味着我们找工作前,就得知道,App的测试点是什么,App功能我们得会测试&#xff0…

绩效考核设计:拟定工时标准,实现量化考核

该度假村工程维修部的主要工作是修灯泡、换水管、修门、开锁等,部门员工大多是老员工,随着年龄的增加,这些员工的工作积极性越来越差,“老油条”越来越多,其他部门对工程维修部的抱怨声也越来越大。一起来看看人力资源…

RTC的Google拥塞控制算法 rmcat-gcc-02

摘要 本文档描述了使用时的两种拥塞控制方法万维网(RTCWEB)上的实时通信;一种算法是基于延迟策略,一种算法是基于丢包策略。 1.简介 拥塞控制是所有共享网络的应用程序的要求互联网资源 [RFC2914]。 实时媒体的拥塞控制对于许…

【Java设计模式】二十五、自定义Spring IoC

文章目录 1、IoC类的定义1.1 定义bean相关的pojo类PropertyValue1.2 定义MutablePropertyValues类1.3 定义BeanDefinition类 2、定义注册表相关类2.1 BeanDefinitionRegistry接口2.2 SimpleBeanDefinitionRegistry类 3、定义解析器相关类3.1 BeanDefinitionReader接口3.2 XmlBe…

【JavaScript】JavaScript 运算符 ④ ( 逻辑运算符 | 逻辑与运算符 | 逻辑或运算符 || | 逻辑非运算符 ! )

文章目录 一、JavaScript 逻辑运算符1、逻辑运算符 概念2、逻辑与运算符 &&3、逻辑或运算符 ||4、逻辑非运算符 !5、完整代码示例 一、JavaScript 逻辑运算符 1、逻辑运算符 概念 JavaScript 中的 逻辑运算符 的作用是 对 布尔值 进行运算 , 运算完成 后 的 返回值 也是…

10倍提效,每天100篇,如何使用AI提取arXiv论文知识?

arXiv arXiv是国际上最有影响力的论文预发平台,在arXiv发表论文,已经成为科研圈的“潜规则”。arXiv创建于1991年,论文主要是理工科论文,包括数学、物理、计算机、统计、金融等领域。 目前收录论文数量已达200万篇。研究人员每个月…

springboot 简易文件共享工具

文章目录 一、运行界面1、登录2、展示 二、源码传送1、使用技术2、代码结构3、源码 三、运行部署1、jar方式2、docker方式3、docker-compose方式 四、优化方向 一、运行界面 1、登录 后台查看日志,获取token值 2、展示 批量上传文件或者点击链接下载 二、源码传…

Vulnhub - Symfonos

希望和各位大佬一起学习,如果文章内容有错请多多指正,谢谢! 个人博客链接:CH4SER的个人BLOG – Welcome To Ch4sers Blog Symfonos 靶机下载地址:https://www.vulnhub.com/entry/symfonos-1,322/ 0x01 信息收集 …

在pzp203上运行ad9361 no-os工程

0. 环境 - pzp203 - ubuntu18 vivado2018 pzp203是一款plutosdr的国产兼容版。出厂默认是基于linux系统的,用libiio调用。软硬件兼容adalm-pluto。开发板提供网盘资料,是添加了板卡适配的。 1. hdl 1.1 准备源码 hdl https://github.com/analogdevi…

力扣162-寻找峰值, test ok

题目 代码实现 #include<iostream> #include<vector> using namespace std;class Solution { public:int findPeakElement(vector<int>& nums) {int len nums.size();int left 0, right len - 1, mid;while (left < right) {mid left (right -…

基于Java的大学计算机课程管理平台(Vue.js+SpringBoot)

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 实验课程档案模块2.2 实验资源模块2.3 学生实验模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 实验课程档案表3.2.2 实验资源表3.2.3 学生实验表 四、系统展示五、核心代码5.1 一键生成实验5.2 提交实验5.3 批阅实…

修改NLog配置文件参数的方法

目录 一、背景 二、NLog配置文件 三、C#代码 四、验证结果 ​ 五、总结 一、背景 最近项目中要用到NLog记录日志&#xff0c;有一个要求是可以灵活地修改日志文件的存放位置&#xff0c;琢磨了一小会&#xff0c;发现可以使用XML文件的形式修改文件的参数&#xff0c;现将…

实现兼容性良好的前端页面开发

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

【LLM加速】注意力优化(基于位置/内容的稀疏注意力 | flashattention)

note &#xff08;1&#xff09;近似注意力&#xff1a; Routing Transformer采用K-means 聚类方法&#xff0c;针对Query和Key进行聚类&#xff0c;类中心向量集合为 { μ i } i 1 k \left\{\boldsymbol{\mu}_i\right\}_{i1}^k {μi​}i1k​ &#xff0c;其中k 是类中心的…

源于一区| 改善性能的5种高效而小众的变异策略,一键调用 (Matlab)

基于群体的优化算法在达到迭代后期时种群多样性往往会速降&#xff0c;进化将陷入停滞&#xff0c;而许多算法本身并没有突变机制&#xff0c;一旦受到局部最优值的约束&#xff0c;就很难摆脱这些约束。它还将减少种群多样性&#xff0c;减缓收敛速度。 变异策略可以增加种群…

2025武忠祥考研数学,视频百度网盘+基础全程课程PDF

“得数学者的天下”&#xff0c;25考研首先要开始的就是数学复习&#xff0c;而数学复习首先要开始的必然是高数&#xff01; 很多同学选择了跟着武忠祥老师学习高数&#xff0c;但是具体要怎么学&#xff1f;用什么书&#xff1f;怎么刷题&#xff1f;快来看看以 下的武忠祥…