unity内存优化之AB包篇(微信小游戏)

news2024/10/7 14:22:32

1.搭建资源服务器使用(HFS软件(https://www.pianshen.com/article/54621708008/))

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using System;

public class Singleton<T> where T : class, new()
{
    private static readonly Lazy<T> lazy = new Lazy<T>(() => new T());
    public static T Instance { get { return lazy.Value; } }
    protected Singleton() { }
}

public class MonoSingleton<T> : MonoBehaviour where T : MonoBehaviour
{

    private static T _instance;

    public static T Instance
    {
        get
        {
            return _instance;
        }
    }

    protected virtual void Awake()
    {
        _instance = this as T;
    }
}


2.核心代码

using Cysharp.Threading.Tasks;
using System.Collections;
using System.Collections.Generic;
using System.Xml.Linq;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.U2D;



/*
内存优化之AB包篇(微信小游戏)
问题:如何优化AB包占用的内存,游戏的AB包资源卸载策略是什么
答:卸载时机
1.该AB包被引用次数为0时候,此时缓存一定的时间,当缓存时间为0时,就可以调用bundle.Unload(true);
缓存时间内被调用重置缓存时间,引用次数增加。
这部分主要用来处理偶尔打开的界面
2.首先维护一个已经加载的AB包资源大小总值,然后设置一个内存基准值,当总值大于内存基准值的时候,
此时去卸载那些引用次数为0的ab资源(优先卸载加载早的ab包资源)。
这部分用来处理短时间内,玩家打开过多大型界面场景,如果不这么处理,手机内存会占用高且发热会严重。

引用次数的维护时机(引用次数始终不小于0)
1.例如一张图片 更换属于不同ab包的资源图片时,需要先将旧的ab包  引用次数减一,界面销毁时,最后动态加载的ab图片资源也需要减一,其他资源同理
2.同时加载一个AB资源时,在AB资源未加载完毕前,需要维护一个加载中的AB包资源实际被加载次数,
由于部分界面 正在动态加载的ab包资源未加载完毕时,此界面就可能已经被销毁,如果被销毁就需要将加载中的ab包的实际被加载次数减一。
3.当ab包资源加载完毕时,如果发现加载中的此ab包维护的实际被加载次数大于0,此时ab包的引用次数加一,同时实际被加载次数减一。
4.当界面销毁时,此界面的ab包和相关依赖的引用次数需要减一,动态加载的ab包资源也需要将引用次数减一


5.!!!需要注意的是 当A依赖于B时,  A的最后一个实例被销毁时  A的引用变为0  但是B的引用此刻不变,除非A被卸载 才能将B的引用减一
                //  A依赖于B
                //  A被加载 会先加载B
                //那么A引用为1 B引用为1
                //A被加载第二次 A引用为2 B引用为2
                //A被加载第3次 A引用为3 B引用为3
                // A被删除1次  引用为2  B引用为2
                //A被删除第二次 A引用为1 B引用为1
                //A被删除第3次 A引用为0 B引用为1
                //A被卸载时  B引用为0

                //  A依赖于B
                //  A被加载 会先加载B
                //那么A引用为1 B引用为1
                //A被加载第二次 A引用为2 B引用为2
                // A被删除1次  引用为1  B引用为1
                //A被删除第二次 A引用为0 B引用为1
                //A被卸载时  B引用为0


                //  A依赖于B
                //  A被加载 会先加载B
                //那么A引用为1 B引用为1
                // A被删除1次  A引用为0  B引用为1
                //A被卸载时  B引用为0

 */
[SelectionBase]
public class LoadingAssetBundle
{
    private string abName;
    public string GetABName()
    {
        return abName;
    }

    private int realLoadedTimesInLoading = 0;//在加载中 被加载的真实次数(也就是剔除那些只加载不使用的部分,例如界面动态加载图片还没加载完毕 这个界面就被销毁了)
    public int GetRealLoadedTimesInLoading()
    {
        return realLoadedTimesInLoading;
    }

    public void AddRealLoadedTimesInLoading()
    {
        realLoadedTimesInLoading++;
    }

    public void ReduceRealLoadedTimesInLoading()
    {
        realLoadedTimesInLoading--;
    }

    public LoadingAssetBundle(string _abName)
    {
        abName = _abName;
        AddRealLoadedTimesInLoading();
    }
}

[SelectionBase]
public class LoadedAssetBundle
{
    private string abName;
    private AssetBundle bundle;
    private float cacheTimeBySenconds = 10;//缓存秒数不同ab可配置
    public float curLastCacheTime = 10;//当前剩余缓存时间
    public int referenceTimes = 0;//引用次数
    public long memoryValue = 0;//ab包大小
    public int loadIndexOrder = 0;//引用顺序 越小代表越早被引用
    private bool isUnload = false;//是否被卸载

    public LoadedAssetBundle(string _abName, AssetBundle _bundle, long _memoryValue, int _loadIndexOrder)
    {
        isUnload = false;
        abName = _abName;
        bundle = _bundle;
        memoryValue = _memoryValue;//long size = long.Parse(unityWebRequest.GetResponseHeader("Content-Length"));
        ABManager.Instance.AddMemoryValue(_memoryValue);
        loadIndexOrder = _loadIndexOrder;
    }

    public AssetBundle GetAssetBundle()
    {
        return bundle;
    }

    public void AddRefer()//添加引用1
    {
        referenceTimes = referenceTimes + 1;
        curLastCacheTime = cacheTimeBySenconds;//重置剩余缓存1时间时间
    }

    public int ReduceRefer()//减少引用
    {
        if (referenceTimes > 0) {
            referenceTimes--;
        };
        return referenceTimes;
    }

    public void RefreshCacheLastTime(float time)
    {
        if (referenceTimes == 0)
        { 
            curLastCacheTime -= time;
            CheckCacheTimeUnload();
        }
    }



    private void CheckCacheTimeUnload()
    {
        if (isUnload) return;
        if (curLastCacheTime <= 0&& referenceTimes == 0) { 
            bundle.Unload(true); //卸载时机1
            isUnload = true; 
            ABManager.Instance.ReduceMemoryValue(memoryValue);
            ABManager.Instance.RemoveABRequest(abName);
            ABManager.Instance.ReduceDependciedRefer(abName);
            Debug.Log($"curLastCacheTime Unload{abName},Count={ABManager.Instance.cachedLoadedDic.Count}");
        }
    }
    public void CheckOverMemoryUnload(int curMinReferIndexOrder)
    {
        if (isUnload) return;
        if (referenceTimes == 0 && ABManager.Instance.CheckOverMemoryMemoryReferenceValue())//&& curMinReferIndexOrder == loadIndexOrder
        {
            bundle.Unload(true);//卸载时机2
            isUnload = true;
            ABManager.Instance.ReduceMemoryValue(memoryValue);
            ABManager.Instance.RemoveABRequest(abName);
            ABManager.Instance.ReduceDependciedRefer(abName);
            Debug.Log($"Unload{abName}");
        }
    }

    public string GetABName()
    {
        return abName;
    }

    public bool IsUnLoad()
    {
        return isUnload;
    }
}


public class ABManager : MonoSingleton<ABManager>
{

    public Dictionary<string, LoadedAssetBundle> cachedLoadedDic = new Dictionary<string, LoadedAssetBundle>();
    private Dictionary<string, LoadingAssetBundle> cachedLoadingDic = new Dictionary<string, LoadingAssetBundle>();
    private long memoryReferenceValue= 995406;//内存基准值
    private long curMemoryValue = 0;//内存当前值
    private int curReferIndexOrder = 0;//当前索引
    private int curMinReferIndexOrder = 0;//当前被加载最早的索引
    public void AddMemoryValue(long _memoryValue)
    {
        curMemoryValue = curMemoryValue + _memoryValue;
        //print("curMemoryValue" + curMemoryValue);
    }
    public void ReduceMemoryValue(long _memoryValue)
    {
        //Debug.Log("memoryValue" + _memoryValue);
        curMemoryValue = curMemoryValue - _memoryValue;
        curMinReferIndexOrder++;
        if (curMinReferIndexOrder  > curReferIndexOrder)
        {
            curMinReferIndexOrder = curReferIndexOrder;
        }
    }

    public bool CheckOverMemoryMemoryReferenceValue()
    {
        return curMemoryValue > memoryReferenceValue;
    }


    private float checkSpan =  0.3f;
    public float time;
    List<string> removeList = new List<string>();
    public int CachedLoadedCount;
    private void CheckUnLoadCachedLoaded()
    {
        time += Time.fixedDeltaTime;
        if (time > checkSpan)
        {
            time = 0;
            removeList.Clear();
            foreach (var item in cachedLoadedDic)
            {
                if (!cachedLoadingDic.ContainsKey(item.Key))
                {
                    item.Value.RefreshCacheLastTime(checkSpan);
                    item.Value.CheckOverMemoryUnload(curMinReferIndexOrder);
                    if (item.Value.IsUnLoad()) removeList.Add(item.Key);
                }
            }

            for (int i = 0; i < removeList.Count; i++)
            {
                print($"removeList={removeList[i]}");
                cachedLoadedDic.Remove(removeList[i]);
            }
        }
        CachedLoadedCount = cachedLoadedDic.Count;
    }


    // Update is called once per frame
    void FixedUpdate()
    {
        CheckUnLoadCachedLoaded();
    }

    private AssetBundle mainAB = null; //主包
    private AssetBundleManifest mainManifest = null; //主包中配置文件---用以获取依赖包
    private string basePath = "http://192.168.31.208/AssetBundles/";
    private string mainABName = "AssetBundles";


    public Dictionary<string, string> AssetNameToABName = new Dictionary<string, string>();
    public async UniTask<GameObject> LoadAsset(string assetName)
    {
        string abName = assetName.ToLower() + ".ab";
        AssetBundle ab = await LoadABPackage(abName);
        //await UniTask.SwitchToMainThread();
        return ab.LoadAsset<GameObject>(assetName);
    }


    /// <summary>
    /// 加载图集里面的图片
    /// 案例
    ///   Image a = nul;;
    ///   if (a != null)
    ///        ABManager.Instance.UnloadAsset(a);
    ///    a = ABManager.Instance.LoadAtlasSprite(a);
    /// </summary>
    /// <param name="assetName"></param>
    /// <param name="textureName"></param>
    /// <returns></returns>
    public async UniTask<Sprite> LoadAtlasSprite(string assetName, string textureName)
    {
        string abName = assetName.ToLower() + ".ab";
        AssetBundle ab = await LoadABPackage(abName);
        SpriteAtlas spriteAtlas = ab.LoadAsset<SpriteAtlas>(assetName);
        return spriteAtlas.GetSprite(textureName);
    }

    //单个包卸载
    public void ReduceRefer(string assetName)
    {
        string abName = assetName.ToLower() + ".ab";

        if (cachedLoadingDic.ContainsKey(abName))
        {
            cachedLoadingDic[abName].ReduceRealLoadedTimesInLoading();

        }
        else
        {
            //--引用
            if (cachedLoadedDic.ContainsKey(abName))
            {
              int referValue =  cachedLoadedDic[abName].ReduceRefer();

                //  A依赖于B
                //  A被加载 会先加载B
                //那么A引用为1 B引用为1
                //A被加载第二次 A引用为2 B引用为2
                //A被加载第3次 A引用为3 B引用为3
                // A被删除1次  引用为2  B引用为2
                //A被删除第二次 A引用为1 B引用为1
                //A被删除第3次 A引用为0 B引用为1
                //A被卸载时  B引用为0

                //  A依赖于B
                //  A被加载 会先加载B
                //那么A引用为1 B引用为1
                //A被加载第二次 A引用为2 B引用为2
                // A被删除1次  引用为1  B引用为1
                //A被删除第二次 A引用为0 B引用为1
                //A被卸载时  B引用为0


                //  A依赖于B
                //  A被加载 会先加载B
                //那么A引用为1 B引用为1
                // A被删除1次  A引用为0  B引用为1
                //A被卸载时  B引用为0

                if (referValue > 0)
                {
                    ReduceDependciedRefer(abName);
                }
            }
        }
 
    }


    public void ReduceDependciedRefer(string abName)
    {
        string[] dependencies = mainManifest.GetAllDependencies(abName);
        for (int i = 0; i < dependencies.Length; i++)
        {
            if (cachedLoadedDic.ContainsKey(dependencies[i]))
            {
                cachedLoadedDic[dependencies[i]].ReduceRefer();
            }
        }
    }







    //加载AB包
    private async UniTask<AssetBundle> LoadABPackage(string abName)
    {

        //加载ab包,需一并加载其依赖包。
        if (mainAB == null)
        {
            //获取ab包内容
            mainAB = await DownloadABPackage(mainABName);
            //获取主包下的AssetBundleManifest资源文件(存有依赖信息)
            mainManifest = mainAB.LoadAsset<AssetBundleManifest>("AssetBundleManifest");
        }

        //根据manifest获取所有依赖包的名称 固定API 保证不丢失依赖
        string[] dependencies = mainManifest.GetAllDependencies(abName);
        if (dependencies.Length > 0)
        {
            var tasks = new List<UniTask>(); // 创建一个任务列表来存储异步操作
                                             //循环加载所有依赖包
            for (int i = 0; i < dependencies.Length; i++)
            {
                //如果不在缓存则加入
                if (!cachedLoadedDic.ContainsKey(dependencies[i])) 
                    tasks.Add(LoadABPackage(dependencies[i]));
                else
                {
                    cachedLoadedDic[dependencies[i]].AddRefer(); //++引用
                }
            }
            // 使用UniTask.WhenAll等待所有任务完成
            await UniTask.WhenAll(tasks);
        }

        //加载目标包 -- 同理注意缓存问题
        if (cachedLoadedDic.ContainsKey(abName))
        {
            cachedLoadedDic[abName].AddRefer(); //++引用
            Debug.Log($"ContainsKey{abName}");
            return (cachedLoadedDic[abName].GetAssetBundle());
        }
        else
        {

            await DownloadABPackage(abName);
            Debug.Log($"DownloadABPackage{abName}");
            return (cachedLoadedDic[abName].GetAssetBundle());
        }
    }

    //存儲下載操作
    Dictionary<string, UnityWebRequestAsyncOperation> ABRequestOpera = new Dictionary<string, UnityWebRequestAsyncOperation>();
    public void RemoveABRequest(string abname)
    {
        string url = basePath + abname;
        ABRequestOpera[url].webRequest.Dispose();//试试多个异步创建
        ABRequestOpera.Remove(url);
    }
    async UniTask<AssetBundle> DownloadABPackage(string abname)
    {
        if (cachedLoadedDic.ContainsKey(abname))
        {
            cachedLoadedDic[abname].AddRefer();
            return cachedLoadedDic[abname].GetAssetBundle();
        }
            
        string url = basePath + abname;
        Debug.Log(url);
        if (!cachedLoadingDic.ContainsKey(abname))
        {
            cachedLoadingDic.Add(abname, new LoadingAssetBundle(abname));
        }
        else
        {
            cachedLoadingDic[abname].AddRealLoadedTimesInLoading();
        }
        if (!ABRequestOpera.ContainsKey(url))
        {
            UnityWebRequest req = UnityWebRequestAssetBundle.GetAssetBundle(url);
            UnityWebRequestAsyncOperation operation = req.SendWebRequest();
            ABRequestOpera.Add(url, operation);
        }
        await ABRequestOpera[url];
        if (!cachedLoadedDic.ContainsKey(abname))
        {
            curReferIndexOrder++;
            AssetBundle ab = DownloadHandlerAssetBundle.GetContent(ABRequestOpera[url].webRequest);
            long size = long.Parse(ABRequestOpera[url].webRequest.GetResponseHeader("Content-Length"));
            cachedLoadedDic.Add(abname, new LoadedAssetBundle(abname,ab, size, curReferIndexOrder));

        }

        if (cachedLoadingDic.ContainsKey(abname)&&cachedLoadingDic[abname].GetRealLoadedTimesInLoading() > 0)
        {
            cachedLoadedDic[abname].AddRefer();
            cachedLoadingDic[abname].ReduceRealLoadedTimesInLoading();
            if (cachedLoadingDic[abname].GetRealLoadedTimesInLoading() == 0)
            {
                cachedLoadingDic.Remove(abname);
            }
        }    
        return cachedLoadedDic[abname].GetAssetBundle();
    }



    //所有包卸载
    public void UnLoadAll()
    {
        AssetBundle.UnloadAllAssetBundles(false);
        //注意清空缓存
        cachedLoadedDic.Clear();
        cachedLoadingDic.Clear();
        mainAB = null;
        mainManifest = null;
    }


}

3..打包AB包代码

using UnityEngine;
using UnityEditor;
using System.IO;
using System;
using System.Collections.Generic;

/// <summary>
/// AB包创建
/// </summary>
public class CreateAssetBundles : MonoBehaviour
{

    public static string BuildAssetBundlePath = Application.dataPath + "/AssetsPach/AssetBundles";
    [MenuItem("Build/BuildAssetBundles")]
    public static void BuildAssetBundle()
    {
        SetAssetBundle();
        string dir = BuildAssetBundlePath; //相对路径
        if (!Directory.Exists(dir))   //判断路径是否存在
        {
            Directory.CreateDirectory(dir);
        }
        BuildPipeline.BuildAssetBundles(dir, BuildAssetBundleOptions.None, EditorUserBuildSettings.activeBuildTarget); //这里是第一点注意事项,BuildTarget类型选择WebGL
        AssetDatabase.Refresh();
        Debug.Log("打包完成");
    }

    //需要打包的资源目录
    public static string SetAssetBundlePath = Application.dataPath + "/AssetsPach/WortAsset";
    public static void SetAssetBundle()
    {
        string dir = SetAssetBundlePath; //相对路径

        AssetDatabase.RemoveUnusedAssetBundleNames();//移除无用的AssetBundleName
        //Debug.LogError(Application.dataPath);//上级路径 F:/TUANJIEProject/My project/Assets
        list_Files = new List<stru_FileInfo>();
        ContinueCheck(dir);
        for (int a = 0; a < list_Files.Count; a++)//
        {
            SetBundleName(list_Files[a].assetPath);
        }
        Debug.Log("生成ab包完成");
        //SetBundleName("Assets/Ship/AC_Enterprise_T01/prefab/AC_Enterprise_T01_M01_ShipMesh.prefab");

    }


    //******资源参数
    static List<stru_FileInfo> list_Files;//文件列表

    static string assetBundleName = "ab";
    static string assetBundleVariant = "";
    //int indentation;//缩进等级
    struct stru_FileInfo
    {
        public string fileName;
        public string filePath;//绝对路径
        public string assetPath;//U3D内部路径
        public Type assetType;
    }





    static void ContinueCheck(string path)
    {
        DirectoryInfo directory = new DirectoryInfo(path);
        FileSystemInfo[] fileSystemInfos = directory.GetFileSystemInfos();//获取文件夹下的文件信息

        foreach (var item in fileSystemInfos)
        {
            int idx = item.ToString().LastIndexOf(@"\");
            string name = item.ToString().Substring(idx + 1);
            if (!name.Contains(".meta"))//剔除meta文件
            {
                CheckFileOrDirectory(item, path + "/" + name);
            }
        }
    }

    static void CheckFileOrDirectory(FileSystemInfo fileSystemInfo, string path)
    {
        FileInfo fileInfo = fileSystemInfo as FileInfo;
        if (fileInfo != null)
        {
            stru_FileInfo t_file = new stru_FileInfo();
            t_file.fileName = fileInfo.Name;
            t_file.filePath = fileInfo.FullName;

            t_file.assetPath = "Assets" + fileInfo.FullName.Replace(Application.dataPath.Replace("/", "\\"), "");//用于下一步获得文件类型
            t_file.assetType = AssetDatabase.GetMainAssetTypeAtPath(t_file.assetPath);
            list_Files.Add(t_file);
        }
        else
        {
            ContinueCheck(path);
        }
    }

    static void SetBundleName(string path)
    {
        print(path);
        var importer = AssetImporter.GetAtPath(path);
        string[] strs = path.Split(".");
        string[] dictors = strs[0].Split('/');
        if (importer)
        {
            if (assetBundleVariant != "")
            {
                importer.assetBundleVariant = assetBundleVariant;
            }
            if (assetBundleName != "")
            {
                importer.assetBundleName = path.ToLower() + "." + assetBundleName;
            }
        }
        else
        {
            Debug.Log("importer是空的" + path);//jpg  png tga
        }
    }

}

4.资源如下 几张美女壁纸,每个预设都是一个壁纸和关闭按钮界面挂载了代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Panel : MonoBehaviour
{
    public string asssetName;
    // Start is called before the first frame update
    void Start()
    {
        transform.GetComponentInChildren<Button>().onClick.AddListener(() => {
            //StartCoroutine(TestLoadSize();

            UIManager.Instance.DeletePanel(this);
        });
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

4.启动场景和代码

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class UIManager :  MonoSingleton<UIManager>
{
    public Transform parent;
    // Start is called before the first frame update
    void Start()
    {
        for (int i = 0; i < transform.childCount; i++)
        {
            int index = i;
            transform.GetChild(i).GetComponent<Button>().onClick.AddListener(() => {
                //StartCoroutine(TestLoadSize();
                print(111);
                DownPanel($"Assets/Assetspach/wortasset/prefabs/Panel{index + 1}.prefab");
                //DownPanel($"Assets/Assetspach/wortasset/prefabs/Panel{index + 1}.prefab");
            });
        }
    }

     async void DownPanel(string asssetName)
    {
        GameObject go = await ABManager.Instance.LoadAsset(asssetName);
        GameObject.Instantiate(go, parent).GetComponent<Panel>().asssetName =asssetName;
    }

    // Update is called once per frame
    public  void DeletePanel(Panel panel)
    {
        ABManager.Instance.ReduceRefer(panel.asssetName);
        DestroyImmediate(panel.gameObject);
    }
}

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

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

相关文章

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

大家好&#xff0c;我是阿赵。   这篇文章我想写了很久&#xff0c;是关于Unity项目使用AssetBundle加载资源时的内存管理的。这篇文章不会分享代码&#xff0c;只是分享思路&#xff0c;思路不一定正确&#xff0c;欢迎讨论。   对于Unity引擎的资源内存管理&#xff0c;我…

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

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

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

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

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

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

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

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

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

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

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

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

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

摘要 本文档描述了使用时的两种拥塞控制方法万维网&#xff08;RTCWEB&#xff09;上的实时通信&#xff1b;一种算法是基于延迟策略&#xff0c;一种算法是基于丢包策略。 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是国际上最有影响力的论文预发平台&#xff0c;在arXiv发表论文&#xff0c;已经成为科研圈的“潜规则”。arXiv创建于1991年&#xff0c;论文主要是理工科论文&#xff0c;包括数学、物理、计算机、统计、金融等领域。 目前收录论文数量已达200万篇。研究人员每个月…

springboot 简易文件共享工具

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

Vulnhub - Symfonos

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

在pzp203上运行ad9361 no-os工程

0. 环境 - pzp203 - ubuntu18 vivado2018 pzp203是一款plutosdr的国产兼容版。出厂默认是基于linux系统的&#xff0c;用libiio调用。软硬件兼容adalm-pluto。开发板提供网盘资料&#xff0c;是添加了板卡适配的。 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;减缓收敛速度。 变异策略可以增加种群…