Unity 批处理详讲(含URP)

news2024/11/14 18:03:34

          咱们在项目中,优化性能最重要的一个环节就是合批处理,,在早期Unity中,对于合批的处理手段主要有三种:   

  • Static Batching 
  • Dynamic Batching  
  • GPU Instancing  

如今Unity 为了提升合批范围与效率,提供了新的合批方式(SRP Batcher)。 

   

      一:初识   Draw Call、Batcher、 Sat pass Call  

                                               衡量CPU处理渲染速率的参考值

    Draw Call

            衡量CPU在渲染时的资源消耗大多都是是通过Draw Call的数量 因为CPU在渲染流水线中的处理阶段是应用程序阶段,主要是做一些数据的准备与提交工作,而Draw Call的数量代表了CPU向GPU提交的数据的次数,Draw Call本身只是一些数据流的字节,主要的性能消耗在于CPU的数据准备阶段。

   Batcher

          由于合批的出现,并不会每一个渲染对象都会产生一个Draw Call,所以这个时候就提出了一个新的衡量标准:Batcher

   Sat pass Call  

         CPU在渲染阶段,性能消耗的峰值一般不在于Draw Call,而往往存在于对其数据准备的阶段,因此单纯以数据的提交数量为衡量标准并不准确,同时在数据准备的过程中,假如前后两个材质发生了变化,会更大幅度的消耗性能,这也是整个CPU在渲染阶段最消耗性能的步骤,因此Unity通过Set Pass Call来作为性能消耗的标准。   (注意:实际上并没有减少Draw Call

   二:主流合批技术详讲

    1、Static Batching

            原理:

                     

  • 将静态游戏对象转换到世界空间并为它们构建一个共享的顶点和索引缓冲区。
  • 如果已启用 Optimized Mesh Data,则 Unity 会在构建顶点缓冲区时删除任何着色器变体未使用的任何顶点元素。为了执行此操作,系统会进行一些特殊的关键字检查;例如,如果Unity 未检测到 LIGHTMAP_ON关键字,则会从批处理中删除光照贴图 UV
  • 针对同一批次中的可见游戏对象,Unity 会执行一系列简单的绘制调用,每次调用之间几乎没有状态变化。在技术上,Unity不会减少 API绘制调用,而是减少它们之间的状态变化(这正是消耗大量资源的部分)。在大多数平台上,批处理限制为 64k 个顶点和 64k 个索引(OpenGLES 上为 48k 个索引,在 macOS 上为 32k 个索引)

      简单的来说,Static Batching通过对一些小的网格进行合并备份到内存中,当执行渲染操作时,CPU一次性将合并的内存的发送给GPU来减少Draw Call的数量,不过这样做有一定的限制:

  • 对象必须是静态的,不可移动
  • 合并的对象使用相同的材质

同时在使用Static Batching时需要额外的内存来存储组合的几何体,导致内存在一定程度上的浪费。简单来说,作为通过内存的上的置换可以获得时间上的高效运行,需要根据实际情况来谨慎添加渲染对象,避免获取CPU性能优势时产生不必要的内存问题

而关于Static Batching的使用,首先需要在Project Setting中的Player选项中勾选Static Batching

      接下来就可以在Inspector面板中对需要Static Batching的对象勾选上Batching Static,具体位置如下图所示: 

      

  • 无法参与批处理情况
  1. 改变Renderer.material将会造成一份材质的拷贝,因此会打断批处理,你应该使用Renderer.sharedMaterial来保证材质的共享状态。
  2. 相同材质批处理断开情况
    位置不相邻且中间夹杂着不同材质的其他物体,不会进行同批处理,这种情况比较特殊,涉及到批处理的顺序。
    拥有lightmap的物体含有额外(隐藏)的材质属性,比如:lightmap的偏移和缩放系数等。所以,拥有lightmap的物体将不会进行同批处理(除非他们指向lightmap的同一部分)。

总结: 虽然静态合批可以有效地减少批次,但是可能无法减少DC。

 2、Dynamic Batching   

        Dynamic Batching同样是可以对于有共同材质的对象进行相关的合并,但是其对象可以为动态的,而且这一过程是动态进行的,只需要在Project Setting中的Player中勾选上Dynamic Batching即可,但是注意,在URP模板中,这一选项移到了URP的配置文件中,具体位置如图: 

  

虽然Dynamic Batching的设置简单,但是其使用条件却很苛刻,Unity官方在文档中详细罗列限制条件:

  • 批处理动态游戏对象在每个顶点都有一定开销,因此批处理仅会应用于总共包含不超过 900 个顶点属性且不超过 300 个顶点的网格。如果着色器使用顶点位置、法线和单个 UV,最多可以批处理 300 个顶点,而如果着色器使用顶点位置、法线、UV0、UV1 和切线,则只能批处理 180 个顶点。
  • 如果游戏对象在变换中包含镜像,则不会对这些对象进行批处理(例如,具有 +1 缩放的游戏对象 A 和具有 –1 缩放的游戏对象 B 无法一起接受批处理)。即使游戏对象基本相同,使用不同的材质实例也会导致游戏对象不能一起接受批处理。例外情况是阴影投射物渲染。
  • 带有光照贴图的游戏对象具有其他渲染器参数:光照贴图索引和光照贴图偏移/缩放。通常,动态光照贴图的游戏对象应指向要批处理的完全相同的光照贴图位置。 多 pass 着色器会中断批处理。
  • 几乎所有的 Unity 着色器都支持前向渲染中的多个光照,有效地为它们执行额外 pass。“其他每像素光照”的绘制调用不进行批处理。 旧版延迟(光照 pre-pass)渲染路径会禁用动态批处理,因为它必须绘制两次游戏对象

    

   批处理中断情况:
       位置不相邻且中间夹杂着不同材质的其他物体,不会进行同批处理,这种情况比较特殊,涉及到批处理的顺序,我的另一篇文章有详解。
物体如果都符合条件会优先参与静态批处理,再是GPU Instancing,然后才到动态批处理,假如物体符合前两者,此次批处理都会被打断。
GameObject之间如果有镜像变换不能进行合批,例如,"GameObject A with +1 scale and GameObject B with –1 scale cannot be batched together"。
拥有lightmap的物体含有额外(隐藏)的材质属性,比如:lightmap的偏移和缩放系数等。所以,拥有lightmap的物体将不会进行批处理(除非他们指向lightmap的同一部分)。
使用Multi-pass Shader的物体会禁用Dynamic batching,因为Multi-pass Shader通常会导致一个物体要连续绘制多次,并切换渲染状态。这会打破其跟其他物体进行Dynamic batching的机会。
我们知道能够进行合批的前提是多个GameObject共享同一材质,但是对于Shadow casters的渲染是个例外。仅管Shadow casters使用不同的材质,但是只要它们的材质中给Shadow Caster Pass使用的参数是相同的,他们也能够进行Dynamic batching。
Unity的Forward Rendering Path中如果一个GameObject接受多个光照会为每一个per-pixel light产生多余的模型提交和绘制,从而附加了多个Pass导致无法合批,如下图:

可以接收多个光源的shader,在受到多个光源是无法合批

       总结:同时使用的Shader一定要是单Pass的。同时因为单Pass的限定,对于延迟渲染来说,由于将光照分离到单独的Pass去处理而导致受光的对象完全没有办法进行动态合批的操作,所以会直接屏蔽掉Dynamic Batching。

3、GPU Instanceing

     

适用前提:
兼容的平台及API

相同的Mesh与Material

支持不同的材质球属性块(MaterialPropertyBlock),用于解决动态修改材质的某些属性后无法合批的问题(因为动态改了相当于不同材质了)

不支持SkinnedMeshRenderer

Shader支持GPU Instancing

缩放为负值的情况下,会不参与加速。
受限于常量缓冲区在不同设备上的大小的上限,移动端支持的个数可能较低。
只支持一盏实时光,要在多个光源的情况下使用实例化,我们别无选择,只能切换到延迟渲染路径。为了能够让这套机制运作起来,请将所需的编译器指令添加到我们着色器的延迟渲染通道中。

效果:
  • 批渲染Mesh相同的那些物体,以降低DrawCall数
  • 这些物体可以有不同的参数,比如颜色与缩放,不同颜色要用(MaterialPropertyBlock)实现。

       使用 GPU Instanceing可使用少量绘制调用一次绘制(或渲染)同一网格的多个副本。它对于绘制诸如建筑物、树木和草地之类的在场景中重复出现的对象非常有用

  • GPU Instanceing在每次绘制调用时仅渲染相同的网格,但每个实例可以具有不同的参数(例如,颜色或比例)以增加变化并减少外观上的重复。
  • GPU Instanceing可以降低每个场景使用的绘制调用数量。可以显著提高项目的渲染性能。

         GPU Instanceing同样有一些使用限制条件:

  • Unity 自动选取要实例化的网格渲染器组件和 Graphics.DrawMesh调用。请注意,不支持 SkinnedMeshRenderer
  • Unity 仅在单个GPU实例化绘制调用中批量处理那些共享相同网格和相同材质的游戏对象。使用少量网格和材质可以提高实例化效率。要创建变体,请修改着色器脚本为每个实例添加数据

       参考文献:GPU实例化GPU 实例化 - Unity 手册GPU实例化

上面是官方文档对于GPU Instanceing的一些描述,可以看出与其他两种合批手段不同的是,除了材质相同之外,其主要是对于使用同一网格的物体有效,所以正如名字的Instanceing那样,是通过GPU直接对于某一物体进行实例化来降低CPU对场景物体的数据命令准备所产生的性能消耗的技术手段。 

原理:

简单地说就是一次对具有相同网格物体的多个对象发出一次绘图调用。CPU收集所有每个对象的变换和材质属性,并将它们放入数组中,然后发送给GPU。然后,GPU遍历所有条目,并按提供顺序对其进行渲染。

如何使用GUPInstancing:

   

//熊猫悟道
Shader "XiongMaoWuDao/Gpu_Instancing"
{
 
	Properties{
		_BaseColor("Color", Color) = (1.0, 1.0, 1.0, 1.0)
	}
	SubShader {
		Pass {
			HLSLPROGRAM
 
			// GUIInstancing  调用
			// 一次对具有相同网格物体的多个对象发出一次绘图调用。
			// CPU收集所有每个对象的变换和材质属性,并将它们放入数组中,然后发送给GPU(SetPassCall)。
			// 最后,GPU遍历所有条目,并按提供顺序对其进行渲染。
			#pragma multi_compile_instancing
 
			#pragma vertex vert
			#pragma fragment frag
			#include "UnlitPass.hlsl"					// 里面定义了顶点着色器以及片元着色器
			ENDHLSL
		}
	}
}
 
 
// --------------以下是UnlitPass.hlsl里的代码-----------------
 
// 为了支持GUIInstancing,这里CBUFFER_START改成用UNITY_INSTANCING_BUFFER_START宏
    UNITY_INSTANCING_BUFFER_START(UnityPerMaterial)
	UNITY_DEFINE_INSTANCED_PROP(float4, _BaseColor)								// 把所有实例的_BaseColor放入内存缓冲区
    UNITY_INSTANCING_BUFFER_END(UnityPerMaterial)
 
// 顶点着色器输入
struct Attributes{
	float3 positionOS : POSITION;
	UNITY_VERTEX_i_INSTANCE_ID												// 启用GUIInstancing的时候,用此宏,可以让顶点传入实例化id
};
 
// 顶点着色器输出
struct Varyings {
	float4 positionCS : SV_POSITION;
	UNITY_VERTEX_i_INSTANCE_ID												// 启用GUIInstancing的时候,用此宏,让顶点着色器输出实例化id
};
 
Varyings vert(Attributes i){
	Varyings o;
	UNITY_SETUP_INSTANCE_ID(i);												// 从i中提取对象索引,并将其存储在其他GUIInstancing相关宏所依赖的全局静态变量中
	UNITY_TRANSFER_INSTANCE_ID(i, o);									// 把i中的实例化id转换到片元着色器中用的实例化id
	float3 positionWS = TransformObjectToWorld(i.positionOS);
	o.positionCS = TransformWorldToHClip(positionWS);
	return o;
}
 
float4 frag(Varyings i) : SV_TARGET{
	UNITY_SETUP_INSTANCE_ID(i);												// 从i中提取对象索引,并将其存储在其他GUIInstancing相关宏所依赖的全局静态变量中
	return UNITY_ACCESS_INSTANCED_PROP(UnityPerMaterial, _BaseColor);			// 根据实例id从_BaseColor数组中取出对应的_BaseColor
}

当需要创建海量mesh的时候,一般不要用实例化游戏物体的方式,这样会比较消耗性能,推荐使用Graphics.DrawMeshInstanced来创建。

   例如  :

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

 // 动态生成大量球体mesh,用来测试GPUInstancing
public class MeshBall : MonoBehaviour
{
    static int baseColorId = Shader.PropertyToID("_BaseColor");
 
    [SerializeField]
    Mesh mesh = default;         // 手动拖入mesh
 
    [SerializeField]
    Material material = default;  // 手动拖入支持GPUInstancing的材质球
 
    Matrix4x4[] matrices = new Matrix4x4[500];
    Vector4[] baseColors = new Vector4[500];
 
    MaterialPropertyBlock block;
 
    private void Awake()
    {
        for (int i = 0; i < matrices.Length; i++)
        {
            matrices[i] = Matrix4x4.TRS(Random.insideUnitSphere * 10f, Quaternion.identity, Vector3.one);
            baseColors[i] = new Vector4(Random.value, Random.value, Random.value, 1f);
        }
    }
 
    private void Update()
    {
        if(block == null)
        {
            block = new MaterialPropertyBlock();
            block.SetVectorArray(baseColorId, baseColors);
        }
        Graphics.DrawMeshInstanced(mesh, 0, material, matrices, 500, block);
    }
}

注意: Graphics.DrawMeshInstanced()这方法还有两问题

1.一次最多画1023个元素,如果超出就会报错,所以需要将草进行分类管理。

2.它不提供裁切的功能,也就是说摄像机看不到的地方,这些草是不会被剔除掉的,依然会被渲染。

解决这个问题,为了避免运行时暴力的for循环来判断是否在视野内,我采取的方法是预先将场景分成20X20若干个格子(可根据游戏的可视范围而定)根据玩家的位置,始终只渲染周围9个格子内的草元素,这样将大幅度减少运行时for循环的次数。

如果每个草的顶点色是不一样的怎么办呢?可以用MaterialPropertyBlock来让同一个材质求有不同的属性。

4、SRP Batcher

适用前提:
        需要是同一个shader变体,可以是不同的材质球,项目需要使用自定义渲染管线,Shader代码必须兼容SRP Batcher。

        但是不支持用材质球属性块(MaterialPropertyBlock)

        渲染的物体必须是一个mesh或者skinned mesh。不能是粒子。

效果:

        可以有效降低SetPassCall(设置渲染状态)的数目,用于CPU性能优化。

开启SRP Batch: 要使用 SRP Batcher,项目必须使用可编程渲染管线。可编程渲染管线可以是:

  • 通用渲染管线 (URP)
  • 高清渲染管线 (HDRP)
  • 自定义 SRP

由于后两种方式不常用,所以本文章会基于URP模板来介绍,而关于URP的具体细节,可以查看该文章:Unity 升级项目到Urp(通用渲染管线)以及画面后处理

当我们在项目中使用URP模板后,就可以在资源目录中找到当前项目的URP配置文件,在其中可以看到SRP Batcher的控制选项:

同时当项目在URP模板下时Dynamic Batching的开关控制选项也被迁移到了配置文件,但是相比于默认渲染管线该技术默认是被关闭的,因为其相对于SRP Batcher来说并没有优势

   参考文献:可编程渲染管线 SRP Batcher - Unity 手册

SRP Batcher原理:

Unity中,可以在一帧内的任何时间修改任何材质的属性。但是,这种做法有一些缺点。例如,DrawCall 使用新材质时,要执行许多作业。因此,场景中的材质越多,Unity 必须用于设置GPU 数据的 CPU也越多。解决此问题的传统方法是减少 DrawCall的数量以优化CPU 渲染成本,因为 Unity 在发出 DrawCall之前必须进行很多设置。实际的 CPU 成本便来自该设置,而不是来自 GPU DrawCall本身(DrawCall 只是 Unity需要推送到 GPU 命令缓冲区的少量字节)

正如Set Pass Call的描述那样,游戏在渲染阶段CPU的性能消耗主要在与材质切换阶段的一些作业,而SPR Batcher通过在GPU的数据缓冲区的持久化存储来换取CPU的新材质的准备时间,从而降低CPU的数据准备压力

SRP Batcher 通过批处理一系列 BindDraw GPU 命令来减少 DrawCall之间的 GPU 设置,具体过程如图所示:

     

            为了获得最大渲染性能,这些批次必须尽可能大。为了实现这一点,可以使用尽可能多具有相同着色器的不同材质,但是必须使用尽可能少的着色器变体

在内渲染循环中,当 Unity 检测到新材质时,CPU 会收集所有属性并在 GPU 内存中设置不同的常量缓冲区。GPU缓冲区的数量取决于着色器如何声明其 CBUFFER 。

为了在场景使用很多不同材质但很少使用着色器变体的一般情况下加快速度,SRP 在原生集成了范例(例如GPU 数据持久性)。

  SRP Batcher是一个低级渲染循环,使材质数据持久保留在 GPU 内存中。如果材质内容不变,SRP Batcher 不需要设置缓冲区并将缓冲区上传到 GPU。实际上,SRP Batcher 会使用专用的代码路径来快速更新大型 GPU 缓冲区中的 Unity 引擎属性,如下所示:

    

这是 SRP Batcher 渲染工作流程。SRP Batcher 使用专用的代码路径来快速更新大型 GPU 缓冲区中的 Unity 引擎属性。在此处,CPU仅处理上图中标记为 Per Object large buffer的 Unity 引擎属性。所有材质在 GPU 内存中都有持久的 CBUFFER,可供随时使用。这样会加快渲染速度,原因是: 现在,所有材质内容都持久保留在 GPU 内存中。 专用代码针对所有每对象属性,管理着一个大型的每对象GPU CBUFFER 。

优化原理:

        简单的说,就是把同一种shader对应的材质球的材质、颜色通通放到一个缓冲区中,不用每帧设置给GPU,每帧仅仅设置坐标、缩放、转换矩阵等变量给GPU。

SRP Batcher 限制条件:

为了使 SRP Batcher代码路径能够渲染对象:

  • 渲染的对象必须是网格或蒙皮网格。该对象不能是粒子。
  • 着色器必须与 SRP Batcher 兼容。HDRP 和 URP 中的所有光照和无光照着色器均符合此要求(这些着色器的“粒子”版本除外)。 为了使着色器与 SRP Batcher 兼容:
  • 必须在一个名为UnityPerDraw的 CBUFFER 中声明所有内置引擎属性。例如unity_ObjectToWorld 或 unity_SHAr
  • 必须在一个名为 UnityPerMaterial的 CBUFFER 中声明所有材质属性
如何让Shader支持SRPBatcher:
   1、必须声明所有内建引擎properties 在一个名为"UnityPerDraw"的CBUFFER里。

     

// 如果需要支持SRP合批,内置引擎属性必须在“UnityPerDraw”的 CBUFFER 中声明
CBUFFER_START(UnityPerDraw)
float4x4 unity_ObjectToWorld;			// 模型空间->世界空间,转换矩阵(uniform 值。它由GPU每次绘制时设置,对于该绘制期间所有顶点和片段函数的调用都将保持不变)
float4x4 unity_WorldToObject;			// 世界空间->模型空间
float4 unity_LODFade;
real4 unity_WorldTransformParams;		// 包含一些我们不再需要的转换信息,real4向量,它本身不是有效的类型,而是取决于目标平台的float4或half4的别名。(需要引入unityURP库里的"Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"才能使用real4)
CBUFFER_END

        注意:URP内置的UnityInput.hlsl里自带更全面的代码。也就是说,如果你的代码有引用或间接引用UnityInput.hlsl,那就不用做这一步了。

 2、必须声明所有材质properties在一个名为"UnityPerMaterial"的CBUFFER里。
// 使用核心RP库中的CBUFFER_START宏定义,因为有些平台是不支持常量缓冲区的。这里不能直接用cbuffer UnityPerMaterial{ float4 _BaseColor };
// Properties大括号里声明的所有变量如果需要支持合批,都需要在UnityPerMaterial的CBUFFER中声明所有材质属性
// 在GPU给变量设置了缓冲区,则不需要每一帧从CPU传递数据到GPU,仅仅在变动时候才需要传递,能够有效降低set pass call
CBUFFER_START(UnityPerMaterial)
float4 _BaseColor;															// 将_BaseColor放入特定的常量内存缓冲区
CBUFFER_END

若需要配合GPUInstancing则需要改写为

UNITY_INSTANCING_BUFFER_START(UnityPerMaterial)
	UNITY_DEFINE_INSTANCED_PROP(float4, _BaseColor)								// 把所有实例的_BaseColor以数组的形式声明并放入内存缓冲区
UNITY_INSTANCING_BUFFER_END(UnityPerMaterial)

 

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

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

相关文章

昇思 25 天学习打卡营第 15 天 | mindspore 实现 VisionTransformer 图像分类

1. 背景&#xff1a; 使用 mindspore 学习神经网络&#xff0c;打卡第 15 天&#xff1b;主要内容也依据 mindspore 的学习记录。 2. Vision Transformer 介绍&#xff1a; mindspore 实现 VisionTransformer 图像分类&#xff1b;VisionTransformer 论文地址 VisionTransfo…

掌握Python:三本不可错过的经典书籍

强烈推荐Python初学者用这三本书入门! Python3剑客 一、《Python编程从入门到实践》 这本书适合零基础的Python读者&#xff0c;旨在帮助他们快速入门Python编程&#xff0c;并达到初级开发者的水平。书中深入浅出地介绍了Python的基础概念&#xff0c;如变量、循环、函数等…

华清数据结构day4 24-7-19

链表的相关操作 linklist.h #ifndef LINKLIST_H #define LINKLIST_H #include <myhead.h> typedef int datatype; typedef struct Node {union{int len;datatype data;};struct Node *next; } Node, *NodePtr;NodePtr list_create(); NodePtr apply_node(datatype e); …

开源XDR-SIEM一体化平台 Wazuh (1)基础架构

简介 Wazuh平台提供了XDR和SIEM功能&#xff0c;保护云、容器和服务器工作负载。这些功能包括日志数据分析、入侵和恶意软件检测、文件完整性监控、配置评估、漏洞检测以及对法规遵从性的支持。详细信息可以参考Wazuh - Open Source XDR. Open Source SIEM.官方网站 Wazuh解决…

秒懂C++之string类(上)

目录 一.string类的常用接口说明 二.不太常用接口&#xff08;了解接口&#xff09; 三.string类的遍历访问 3.1 迭代器iterator 3.2 反向迭代器 四.string的其他功能 4.1 reserve(扩容&#xff09; 4.2 resize 4.3 at 4.4 append 4.5 4.6 insert 一.string类的常用…

VS2015加断点(红色),修改过后,断点变为白色不能命中

实际这个问题是因为&#xff1a;源文件和原始版本不同。解决方法有二&#xff1a; 一&#xff0c;在断点上右键&#xff0c;选择“位置”》勾选”允许源代码与原始版本不同&#xff1b; 二&#xff0c;点击菜单栏“调试”》“选项和设置”》“常规”》去掉“要求源文件与原始…

外卖霸王餐运营规划,系统该怎么选择?

在当今的外卖市场中&#xff0c;竞争日益激烈&#xff0c;如何吸引并留住消费者成为了每个餐饮商家关注的焦点。霸王餐作为一种创新的营销策略&#xff0c;以其独特的魅力&#xff0c;吸引了大量消费者的目光。然而&#xff0c;如何有效地运营霸王餐活动&#xff0c;选择合适的…

浅谈断言之XML Schema断言

浅谈断言之XML Schema断言 “XML Schema断言”是一种专门用于验证基于XML的响应是否遵循特定XML Schema定义的标准和结构的断言类型。下面我们将详细探讨XML Schema断言的各个方面。 XML Schema断言简介 XML Schema断言&#xff08;XML Schema Assertion&#xff09;允许用户…

EXO项目解析:pynvml怎么实现监控的,包括什么参数

目录 pynvml怎么实现监控的,包括什么参数 pynvml实现监控的方式 pynvml包括的主要参数 GPU功耗的组成 举例说明 注意事项 EXO项目解析:https://github.com/exo-explore/exo?tab=readme-ov-file 这段代码是一个使用setuptools库编写的Python包安装脚本,主要用于定义和…

std的时间函数——chrono

参考&#xff1a; C 标准库 分数运算&#xff08;ratio库&#xff09; 再也不被时间束缚&#xff1a;C stdchrono时间库全面解析 C11时间类 c11 chrono全面解析(最高可达纳秒级别的精度) C std::chrono库使用指南 (实现C 获取日期,时间戳,计时等功能) 一、std的分数ratio…

Android 防止重复点击

1.第一种方式&#xff1a; private static final int MIN_DELAY_TIME 2000; // 两次点击间隔不能少于2000ms private static long lastClickTime System.currentTimeMillis(); public static boolean isFastClick() { boolean flag true; long currentClickTime …

JMeter接口测试-3.断言及参数化测试

1. 断言 JMeter官方断言&#xff08;Assertion&#xff09;的定义 用于检查测试中得到的响应数据是否符合预期&#xff0c;用于保证测试过程中的数据交互与预期一致 断言的目的&#xff1a; 一个取样器可以添加多个不同形式的断言&#xff0c;根据你的检查需求来添加相应的…

dou dian滑块captchaBody

声明(lianxi a15018601872) 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 前言(lianxi a…

基于生物地理算法的MLP多层感知机优化matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1 生物地理算法&#xff08;BBO&#xff09;原理 4.2 多层感知机&#xff08;MLP&#xff09; 4.3 BBO优化MLP参数 5.完整程序 1.程序功能描述 基于生物地理算法的MLP多层感知机优化mat…

Git之repo sync -l与repo forall -c git checkout用法区别(四十九)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

Java 集合框架:TreeMap 的介绍、使用、原理与源码解析

大家好&#xff0c;我是栗筝i&#xff0c;这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 021 篇文章&#xff0c;在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验&#xff0c;并希望进…

SpringBoot原理解析(二)- Spring Bean的生命周期以及后处理器和回调接口

SpringBoot原理解析&#xff08;二&#xff09;- Spring Bean的生命周期以及后处理器和回调接口 文章目录 SpringBoot原理解析&#xff08;二&#xff09;- Spring Bean的生命周期以及后处理器和回调接口1.Bean的实例化阶段1.1.Bean 实例化的基本流程1.2.Bean 实例化图例1.3.实…

leetcode算法题之接雨水

这是一道很经典的题目&#xff0c;问题如下&#xff1a; 题目地址 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 解法1&#xff1a;动态规划 动态规划的核心就是将问题拆分成若干个子问题求解&#…

相信开源的力量,MoonBit 构建系统正式开源

MoonBit 构建系统正式开源 作为由 AI 驱动的云服务和边缘计算开发者平台&#xff0c;MoonBit 自设计之初便注重工具链与语言的协同效果。MoonBit 为开发者提供了一套开箱即用的工具链&#xff0c;包括集成开发环境&#xff08;IDE&#xff09;、编译器、构建系统和包管理器&…

内网隧道——HTTP隧道

文章目录 一、ReGeorg二、Neo-reGeorg三、Pivotnacci 实验网络拓扑如下&#xff1a; 攻击机kali IP&#xff1a;192.168.111.0 跳板机win7 IP&#xff1a;192.168.111.128&#xff0c;192.168.52.143 靶机win server 2008 IP&#xff1a;192.168.52.138 攻击机与Web服务器彼此之…