基于Vulkan Specialization Constants的材质变体系统
材质变体
所谓材质变体,指的是一份材质代码文件,最终对应的是多份运行时gpu程序。比如,shader代码里面有开关或者选项,不同的组合对应不同的最终gpu program。那么,所有的这些组合对应的gpu program,可以统一理解为这个材质对应的所有变体。
比如下面shader代码:
float4 color;
float3 normal;void setColorAndNormal() {
#if COLOR_REDcolor = float4(1, 0, 0, 1);
#elsecolor = float4(1, 1, 1, 1);
#endif#if NORMAL_POSITIVEnormal = float3(0, 1, 0);
#elsenormal = float3(0, -1, 0);
#endif
}
有2个开关,COLOR_RED,NORMAL_POSITIVE。每个开关都有2种状态,开或者关。那么,可以组合出2*2=4种状态。
类似C语言,glsl或者hlsl也支持#define宏,因此也有大于2个状态的开关,比如COLOR_RED == 0、COLOR_RED == 1、COLOR_RED == 2。总的状态计算方式是所有开关的状态数相乘,也就是复杂度是指数级的。
传统变体(静态编译变体)
概念
这里的传统变体指的是针对每一种组合状态都编译生成单独的着色器代码。实际上,目前绝大部分引擎实现的变体方案都是这种方式。
实现思路
编译期
算法基本思路很简单,遍历所有的状态组合,针对当前状态,#define相应的宏,然后编译当前代码。
比如,
#define COLOR_RED 1
#define NORMAL_POSITIVE 0float4 color;
float3 normal;void setColorAndNormal() {
#if COLOR_REDcolor = float4(1, 0, 0, 1);
#elsecolor = float4(1, 1, 1, 1);
#endif#if NORMAL_POSITIVEnormal = float3(0, 1, 0);
#elsenormal = float3(0, -1, 0);
#endif
}
对应的代码就是开关COLOR_RED打开、NORMAL_POSITIVE关闭的变体组合状态。
同时,将当前状态的激活关键字(变体开关)组合与编译后的代码做映射,保存在编译结果中。
运行时
根据当前的变体开关选择,映射到具体的代码。这里的映射方式与编译期的算法类似。比如,在材质类里面有一个hashmap, 保持变体状态组合到具体gpu program的映射。如果,hashmap内不存在这个映射,那么从编译期生成的代码内加载具体的编译后shader code,然后创建gpu program。
问题
由于需要在编译期就决定所有的状态组合,那么很可能会出现包体和内存爆炸的情况。比如,有10个开关,每个开关有2种状态,那么就是1024个变体,对应1024份代码。假设,一份代码的尺寸是10kb,那么就是10mb,有10个这样的材质,那么包体占用就是100mb,内存占用会更大。这就是游戏项目中常说的变体爆炸问题。
动态变体
概念
相比于传统变体,动态变体的最大优势是不会出现变体爆炸问题。在编译期间的编译结果,只有一份代码,同时保存变体定义信息。在运行时,二次编译生成真正的中间代码(spir-v)或者gpu上的汇编代码。
实现思路
编译期
不需要复杂的遍历算法,直接编译shader代码即可。但是,需要工具链或者图形API支持。比如,使用vulkan支持的Specialization Constants实现变体,那么可以在编译期保存Specialization Constants定义信息的同时,使用spirv-tools编译生成一份spir-v中间代码。
运行时
加载这份编译后的代码,比如spir-v中间代码。针对,当前的变体设置,对spir-v进一步处理成指定的变体状态或者将变体设置提交给vulkan,让驱动去编译。
问题
这种方式会有一定的局限性,无法优化所有的情况。比如驱动可能有bug,无法优化掉一些复杂变体组合的情况或者一些复杂的代码,导致真正运行的代码有多余的指令,引起性能大幅度下降。
Vulkan Specialization Constants变体
概念
不同的图形API对动态变体的支持情况不一样,比如OpenGL不支持,Vulkan支持Specialization Constants,metal支持Function Constants。
这里专门指代基于Vulkan Specialization Constants实现的变体系统。
实现思路
使用Specialization Constants实现材质代码
layout(constant_id = 0) const bool COLOR_RED = true;
layout(constant_id = 1) const bool NORMAL_POSITIVE = true;float4 color;
float3 normal;void setColorAndNormal() {
if (COLOR_RED) {color = float4(1, 0, 0, 1);
}
else {color = float4(1, 1, 1, 1);
}if (NORMAL_POSITIVE) {normal = float3(0, 1, 0);
}
else {normal = float3(0, -1, 0);
}}
比如上述代码,定义了2个Specialization Constants变量:COLOR_RED和NORMAL_POSITIVE 。同时在代码内使用了这2个变量作为开关进行分支选择,注意:从语法上,Specialization Constants是作为变量处理,而不是宏。
使用spirv-tools编译带有Specialization Constants信息的材质代码
这一步与传统变体的区别是不需要遍历所有的变体状态组合,直接编译代码即可。对于,vulkan来说,使用glslang库调用spirv-tools编译代码就可以获得带有Specialization Constants信息的spir-v中间代码。
运行时决定Specialization Constants
上一步得到的是一份带有Specialization Constants信息的spir-v中间代码,如果获得最终的运行时代码了?
变体组合映射gpu program
这部分类似传统变体方案,需要将变体组合状态映射到具体的Specialization Constants设置。
设置Specialization Constants
有两种实现思路,各有优劣,下面具体说明。
1. 使用vulkan的Specialization Constants接口
使用vulkan的Specialization Constants,在在vulkan的pipeline中传递运行时的Specialization Constants设置信息。因为Specialization Constants是PSO的一部分,因此这种方式需要重新编译gpu program和PSO。由于,不需要完整编译gpu program,因此与切换gpu program的方案(传统变体)这个方案会编译更快。
2. 使用spirv-optimizer剔除分支
第二种方式是使用spir-optimizer里面的pass处理spir-v中间代码,比如设置Specialization Constants的值后,剔除dead code和Specialization Constants信息等,直接获得最终不带Specialization Constants信息的spir-v。这个spir-v就可以直接传递vulkan创建gpu program。
3. 两个方案对比
- vulkan的Specialization Constants依赖驱动的JIT编译结果,如果驱动实现有问题,那么实际上Specialization Constants无法精准剔除代码,导致性能达不到预期。
- spirv-optimizer剔除代码的方式,可以避免驱动的问题,在不同的驱动上表现一致;而且方便调试,比如可以在RenderDoc上抓取最终运行代码,或者mali offline compiler离线查看,确定最终运行的变体状态,但是这个方案依赖这个中间处理工具的能力。
问题
- 依赖高级特性,在传统图形API上不支持。只能针对运行Vulkan的平台做优化。
- 一些复杂的情况无法兼容,比如高通驱动无法优化比较复杂的使用Specialization Constants的代码等,会出现明显性能下降;spirv-optimizer对于将Specialization Constants作为变量传递的代码无法识别等。
- 使用vulkan的Specialization Constants接口在驱动上进行JIT编译的方案,运行结果依赖具体硬件的驱动实现,结果不稳定。
- 使用spirv-optimizer剔除分支的方案需要额外的运行时处理时间,会引起切换变体卡顿,引擎需要妥善处理,比如异步调用spirv-optimizer,同时对优化后的spir-v缓存。
动态变体与传统变体的对比
优势
- 可以解决变体爆炸问题。
- 可能更快的编译PSO。
劣势
- 跨平台差,需要高级图形API特性。
- 可能依赖硬件驱动实现。
- 可能引入运行时卡顿。
其它问题探讨
变体收集
变体收集是另一个经常讨论的问题。一般需要引擎支持才能实现完整的变体收集。下面讲一个之前实现过的方案。
- 引擎runtime
引擎内有一个统一的ShaderProgramManger。该管理器内有2层hash,保存了所有材质和材质变体组合对应的gpu program。引擎内所有切换变体的操作最终都通过该类来查找gpu program。因此,该类完整收集了当前引擎运行状态下所有的变体组合。那么,可以在该类里面实现dump接口,遍历所有缓存的材质变体组合,输出文件作为变体集合文件。
实际项目中,可以用自动化系统运行常见的场景,在合适的时机调用dump接口进行收集。 - 变体集合编译
假如使用spirv-optimizer的方案实现动态变体,那么可以针对变体集合文件内的收集到的变体状态组合,提前编译出最终的spir-v。在ShaderProgramManger查找变体时候,判断有提前预编译的情况,可以直接加载,而不是去调用spirv-optimizer处理。
变体预热
-
变体切换
对于传统变体来说,就是根据变体设置查找相应的着色器代码;对于动态变体来说,可能需要对spir-v代码进行预处理。 -
编译PSO
对于使用vulkan的Specialization Constants接口的方案来说,gpu program已经确定,需要设置Specialization Constants,再重新编译PSO。这个过程实际上是对gpu program重新编译获得最终的版本,由于有之前的编译信息,会比编译完整的gpu program更快。
对于其它方案,实际上是编译完整的gpu program,与使用vulkan的Specialization Constants接口的方案对比,速度更慢。 -
实现思路
比如引擎可以加载变体集合文件,根据变体集合文件的描述,提前编译对应变体的代码以及PSO。
动态变体无法解决PSO编译的问题
网上也有讨论Specialization Constants的文章,比如:【笔记】Shader变体大杀器:specialization constants。该文章的评论里面提到Specialization Constants无法解决PSO的预热问题,从而对Specialization Constants进行了否定。实际上,这个是概念上的混淆。无论如何,PSO是需要重新编译的,因为最终的渲染状态数目是没有改变的;动态变体只是将确定最终gpu program的过程延迟到运行时决定,从而避免变体爆炸,并没有减少材质变体的状态总数。期望通过
Specialization Constants减少PSO数目或者加快PSO预热是方向上的错误。正确的思路是从PSO的收集缓存等方面来考虑,避免第一次切换到该PSO的卡顿。
相关文章:
基于Vulkan Specialization Constants的材质变体系统
材质变体 所谓材质变体,指的是一份材质代码文件,最终对应的是多份运行时gpu程序。比如,shader代码里面有开关或者选项,不同的组合对应不同的最终gpu program。那么,所有的这些组合对应的gpu program,可以统…...
Langchain+RAG+向量数据库
加载数据 import osimport lancedb from langchain_community.document_loaders import TextLoader from langchain_community.embeddings import BaichuanTextEmbeddings from langchain_community.vectorstores import LanceDB from langchain_core.output_parsers import St…...
Stack和Queue和deque的讲解(底层实现 手撕版)
一.底层的基本思路 我们cpp中实现的栈和队列不同于我们数据结构c语言实现的栈和队列,c语言中实现的栈和队列都是通过一个数组指针的形式来完成,每个函数都需要写大量的代码,但是我们的cpp,就是通过函数模板 适配器来完成的。 我们…...
《Pinia 从入门到精通》Vue 3 官方状态管理 -- 插件扩展篇
使用插件扩展功能 可以同时使用多个插件(插件“中间件式”机制)一、使用多个插件的方式二、插件机制简图三、插件互不冲突的关键点四、实战示例:多插件组合使用五、组合使用注意事项推荐插件组合搭配方案(实战模板) 根…...
JavaScript 中的 Reflect 对象:深入理解与应用
JavaScript 中的 Reflect 对象:深入理解与应用 一、引言 在 JavaScript 不断发展的过程中,ES6 引入了许多新的特性和对象,其中 Reflect 对象是一个强大且实用的工具。Reflect 对象提供了一系列静态方法,这些方法与 Proxy 对象的…...
dirsearch 使用教程:详细指南与配置解析
dirsearch 是一款强大的开源命令行工具,用于对 Web 服务器进行目录和文件暴力破解。它通过扫描目标网站,尝试发现隐藏的目录、文件或潜在的敏感资源,广泛应用于渗透测试和安全审计。dirsearch 提供丰富的选项和灵活的配置文件支持,…...
【C++基础知识】C++类型特征组合:`disjunction_v` 和 `conjunction_v` 深度解析
这两个模板是C17引入的类型特征组合工具,用于构建更复杂的类型判断逻辑。下面我将从技术实现到实际应用进行全面剖析: 一、基本概念与C引入版本 1. std::disjunction_v (逻辑OR) 引入版本:C17功能:对多个类型特征进行逻辑或运算…...
ctfhow——web入门214~218(时间盲注开始)
web入门214 #another:uwvwko import requestsurlhttp://b0c11589-31c9-4bf9-8b66-6b5a1fc08726.challenge.ctf.show/api/index.php flag str{-_1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM}for i in range(1,50):for j in str:# 查数据库# payload "…...
shell练习(2)
1.给脚本service.sh进行修改,当执行的时候要求输入(1、2、3、4、5)时安装对应的httpd、vim、wget、更换aliyum等功能,当输入错误 时提示应该输入正确的值但是不会退出。 [rootbogon yy]# cat service.sh #!/bin/bash while : do cat <<-EOF --------------…...
【GO语言小案例手记】基于GIN的简易代理网关
基于GIN的简易代理网关 背景目标开工依赖主体代码配置文件 后记 背景 正好最近对GO也有点兴趣,搞个小项目练练手。 目标 网关需要能够根据路由自动映射到服务支持轮询、加权轮询、随机轮询三种算法简单好理解好使用,最好一个配置文件就能跑起来网关本…...
Qt 入门 6 之布局管理
Qt 入门 6 之布局管理 对于一个完整的软件,布局管理时必不可少的。其会让界面中嗯嗯部件呈现一个整齐的排列,也可令其大小随着窗口界面的大小变换而变化Qt 主要提供了QLayout 类及其子类作为布局管理器,他们可以实现常用的布局管理功能&…...
Java技术体系的主要产品线详解
Java技术体系的主要产品线详解 Java Card:支持Java小程序(Applets)运行在小内存设备(如智能卡)上的平台。 Java ME(Micro Edition):支持Java程序运行在移动终端(手机、P…...
第四章: 服务集成抽象
Chapter 4: 服务集成抽象 🌟 从上一章到本章 在第三章:传输机制中,我们学习了如何通过STDIO和SSE协议让LLM与不同服务器通信。现在想象这样的场景:你的AI助手需要同时操作本地文件和云端数据库。这时问题来了——如何让LLM像操作…...
高精度并行2D圆弧拟合(C++)
依赖库 Eigen3 GLM Ceres-2.1.0 glog-0.6.0 gflag-2.2.2 基本思路 Step 1: RANSAC找到圆弧,保留inliers点; Step 2:使用ceres非线性优化的方法,拟合inliers点,得到圆心和半径; -------…...
【防火墙 pfsense】1简介
(1) pfSense 有以下可能的用途: 边界防火墙 路由器 交换机 无线路由器 / 无线接入点 从OSI7层模型了解设备在典型网络结构中所处的位置。 (2)边界防火墙 ->要充当边界防火墙,pfSense 系统至少需要两个接…...
GPT-4o最新图像生成完全指南:10大应用场景与提示词模板
引言 OpenAI于近期推出的全新GPT-4o图像生成功能,代表了AI图像创作领域的重大突破。作为一个原生多模态系统,GPT-4o将文本理解和图像生成无缝整合,为创作者、教育工作者和专业人士提供了前所未有的视觉创作灵活性。本文将分享10个GPT-4o图像…...
32单片机——外部中断
STM32F103ZET6的系统中断有10个,外部中断有60个 1、中断的概念 中断是为使单片机具有对外部或内部随机发生的事件实时处理而设置的,中断功能的存在,很大程度上提高了单片机处理外部或内部事件的能力 eg::你打开火&…...
《Pinia 从入门到精通》Vue 3 官方状态管理 -- 进阶使用篇
《Pinia 从入门到精通》Vue 3 官方状态管理 – 基础入门篇 《Pinia 从入门到精通》Vue 3 官方状态管理 – 进阶使用篇 《Pinia 从入门到精通》Vue 3 官方状态管理 – 插件扩展篇 目录 Store 的模块化设计4.1 多模块结构设计✅ 推荐目录结构(中大型项目) …...
HarmonyOs @hadss/hmrouter路由接入
参考文档:官方文档 在根目录oh-package.json5配置 {"dependencies": {"hadss/hmrouter": "^1.0.0-rc.11"} }加入路由编译插件 hvigor/hvigor-config.json文件 {"dependencies": {"hadss/hmrouter-plugin": &…...
第九节:性能优化高频题-首屏加载优化策略
路由懒加载:component: () > import(‘…’) CDN加速第三方库、Tree-Shaking移除未使用代码 前端首屏加载优化核心策略解析 一、路由懒加载:按需拆分代码块 实现原理 通过动态导入语法 import() 将路由组件拆分为独立代码块,仅在用户访问…...
ESP32_IDF_VScode安装多版本共存
ESP32_IDF_VScode安装多版本共存 一、安装离线版本idf 详情见文章:ESP32_IDF_基于win11的开发环境搭建 二、windows的VScode安装乐鑫插件 三、导入已经安装好的idf(将VScode插件和本地安装的IDF绑定的一个关系) 1、选择“配置ESP-IDF扩展”…...
JavaScript 的“积木”:函数入门与实践
引言:告别重复,拥抱模块化 想象一下,你在写代码时发现,有几段逻辑几乎一模一样,需要在不同的地方反复使用。你是选择每次都复制粘贴,还是希望能像搭积木一样,把这段逻辑封装起来,需…...
代码注释标记的含义
在代码中,TODO 是一种常用的注释标记,用于标识需要后续处理或完善的任务。它是开发者之间的常见约定,帮助团队协作和任务管理。以下是详细解释: 1. TODO 的核心含义 待办事项:标记代码中需要完成但尚未实现的功能、需…...
深度学习:迁移学习
迁移学习 标题1.什么是迁移学习 迁移学习(Transfer Learning)是一种机器学习方法,就是把为任务 A 开发 的模型作为初始点,重新使用在为任务 B 开发模型的过程中。迁移学习是通过 从已学习的相关任务中转移知识来改进学习的新任务,虽然大多数…...
Nest集成健康检查
文章目录 前言✅ NestJS 健康检查集成思路(标准实践)📦 推荐使用官方包: 🧱 结构设计✅ 1. 创建健康模块✅ 2. 集成 nestjs/terminushealth.module.tshealth.controller.ts ✅ 3. 在 AppModule 注册模块 🔍…...
第十五届蓝桥杯 2024 C/C++组 拼正方形
目录 题目: 题目描述: 题目链接: 思路: 思路详解: 易错点: 代码: 代码详解: 题目: 题目描述: 题目链接: P10898 [蓝桥杯 2024 省 C] 拼正…...
前端出现的一些新技术或者升级的技术汇总
以下是截至2024年第三季度前端领域的最新技术动态与论坛热议焦点,涵盖框架、工具链、性能优化等方向,结合社区讨论和实际案例展开分析: 一、框架演进与争议热点 1. React 19「Actions」引发范式转变 核心变化: 服务端Actions&…...
SQL数据类型
数字类型 1. 整型(Integer) 整型数据类型用于存储整数值,不包含小数部分。通常用于表示没有小数部分的数字,如年龄、数量、ID 等。 常见的整型数据类型: INT:用于存储常规整数值,通常占用 4 字…...
手机访问电脑端Nginx服务器配置方式
修改当前站点Nginx的配置如下。其中端口号必须是一个比较独特的端口号,比如8001。这样可以跟别的项目区分开来。域名使用0.0.0.0。 server {listen 80;listen 8001;server_name zfmap.cc 0.0.0.0;假设你电脑端的ip地址是192.168.1.101,那么你的手机与你的电脑连在同…...
PyQt6基础_QTabWidget
目录 代码 运行 官方文档 PySide6.QtWidgets.QTabWidget - Qt for Python 代码 class TempWidget(QWidget):def __init__(self):super().__init__()self.tabs QTabWidget()self.tabs.tabBarClicked.connect(self.tabs_tabBarClicked)widget_tab1 QWidget()widget_tab2…...
海思ISP调试记录
1、proc_param 功能:在海思中,proc_param参数用来控制每个多少帧更新一次ISP,默认是30帧。 过短的更新间隔会导致图像参数不稳定,产生闪烁或色彩跳跃4过长的间隔会使3A调整滞后,影响动态场景适应性1海思建议在1080p3…...
以运营为核心的智能劳动力管理系统,破解连锁零售、制造业排班难题
在连锁零售、制造业、物流等劳动力密集型行业中,排班与考勤管理不仅是人力资源管理的核心环节,更是直接影响企业运营效率、成本控制与合规风险的关键场景。尤其在当前经济环境下,企业面临用工成本攀升、政策合规趋严、业务波动频繁等多重挑战…...
缓存穿透、雪崩、击穿深度解析与解决方案
缓存穿透、雪崩、击穿深度解析与解决方案 一、缓存三大核心问题全景解析 1. 问题定位与影响分析 问题类型触发条件典型现象核心风险缓存穿透大量请求访问不存在的键Redis 命中率骤降(<10%)数据库压力激增,可能宕机缓存雪崩大量缓存键同…...
【AI】基于OllamaSharp与.NET Core API的高效LLM查询实现
本文旨在演示如何通过OllamaSharp NuGet包在.NET Core API中高效查询Ollama大语言模型,重点展示如何通过JSON配置文件动态设置模型参数和服务器地址,实现灵活维护的AI服务架构。 创建.NET Core API项目dotnet new webapi -n OllamaLLMAPI cd OllamaLLMAPI添加OllamaSharp NuG…...
kotlin和MVVM的结合使用总结(二)
MVVM 架构详解 核心组件:ViewModel 和 LiveData 在 Android 中,MVVM 架构主要借助 ViewModel 和 LiveData 来实现。ViewModel 负责处理业务逻辑,而 LiveData 则用于实现数据的响应式更新。 ViewModel 的源码分析 ViewModel 的核心逻辑在 …...
U盘能识别但无法写入数据的原因
1. U 盘物理损坏 原因:U 盘内部存储芯片、电路板或接口接触不良,可能因摔落、高温、频繁插拔等导致。表现:插入电脑能识别盘符,但读写时提示 “磁盘错误”“无法访问” 或操作无反应。解决方法: 尝试用其他设备&#…...
多模态大模型 Qwen2.5-VL 的学习之旅
Qwen-VL 是阿里云研发的大规模视觉语言模型(Large Vision Language Model, LVLM)。Qwen-VL 可以以图像、文本、检测框作为输入,并以文本和检测框作为输出。Qwen-VL 系列模型性能强大,具备多语言对话、多图交错对话等能力ÿ…...
linux sudo 命令介绍
sudo(superuser do)是一个用于 Linux 系统的命令,它允许授权用户以其他用户(通常是 root 超级用户)的安全权限执行命令。 有了 sudo,用户在执行特定的、需要更高权限的操作时,就不需要切换到 r…...
STM32F103系列单片机寄存器操作和标准库操作
关于stm32,标准库很早就学完了,但如果想要更加深入学习计算机硬件,那么学会寄存器操作是非常有必要的。今天从最简单的点灯开始,我们来对比一下二者的不同。 一、寄存器操作和标准库操作中点亮LED的区别 寄存器操作:…...
如何解决PyQt从主窗口打开新窗口时出现闪退的问题
在PyQt5中,当从主窗口打开新窗口时,经常会出现闪退现象,这通常是由于对象生命周期管理不当或事件循环错误等所导致。 1. 确保新窗口实例被正确引用 新窗口的实例若未被主窗口引用,可能会被Python的垃圾回收机制销毁。 错误示例&…...
2025五一杯数学建模竞赛思路助攻预定
2025五一杯数学建模竞赛思路助攻预定(思路内容见文末名片) 一、概况 数学建模竞赛是一项模拟面对实际问题寻求解决方案的活动,是一次近似 于“真刀真枪”的创新探索性实践训练。在丰富并活跃学生课外生活活动的同 时,数学建模竞…...
Java集合框架解析
一、集合框架概述 1. 集合框架体系结构 Java集合框架(Java Collections Framework, JCF)位于java.util包中,包含三大核心接口: Collection:单列数据集合的根接口 List:有序可重复集合Set:无序…...
《100天精通Python——基础篇 2025 第1天:从编程语言到计算机基础,开启你的学习之旅》
目录 一、计算机组成原理之概述篇二、编程语言是什么三、编译型语言和解释型语言的区别3.1 编译型语言3.2 解释型语言 四、Python是什么五、Python有哪些优点和缺点?5.1 Python的优点5.2 Python 的缺点 六、学Python能干什么,Python的应用领域有哪些&…...
JavaFX 第三篇 HostServices和Platform
1、HostServices类 介绍这个类主要是使用里面的一个方法 返回类型方法说明voidshowDocument(java.lang.String uri)使用默认浏览器打开一个url地址 /*** description: 程序打开3秒后,打开百度* author: HK* since: 2025/4/24 16:40*/ public class Demo1 extends…...
【Java 8新特性】Stream API 和 Lambda 表达式
一、前言 Java 8 的 Stream API 和 Lambda 表达式 为集合处理带来了函数式编程风格,显著简化了代码并提高了可读性。 二、Lambda 表达式 1.作用 简化匿名内部类的语法,允许将函数作为参数传递。实现函数式接口(只有一个抽象方法的接口&…...
Vue 3 相比 Vue 2 的优势
1. 性能优化 更快的渲染: 基于 Proxy 的响应式系统,比 Vue 2 的 Object.defineProperty 更高效,初始化速度和内存占用优化显著。编译时优化(如静态树提升、补丁标志等),减少运行时开销。 更小的体积&#…...
深度解析 TransmittableThreadLocal(TTL):原理、实战与优化指南
深度解析 TransmittableThreadLocal(TTL):原理、实战与优化指南 在现代 Java 应用中,ThreadLocal 被广泛用于线程隔离上下文,比如用户会话、链路追踪等。但随着线程池的普及,ThreadLocal 也暴露出严重局限性,尤其是在异步场景中上下文无法正确传递的问题。 本文从 Thr…...
入门 Go 语言
本专栏的 Go 语言学习参考了B站UP 软件工艺师的视频 本节需要: Go 语言环境VSCode 安装环境 下载 Go 环境,并安装下载 VSCode,安装。在 VSCode 中安装 Go 扩展: 接下来就可以编写 Go 语言了 第一条 Go Go 语言是一种编译型…...
膳食营养诊断活动:科技赋能,共筑全民健康新基石
膳食营养诊断活动:科技赋能,共筑全民健康新基石 一、活动背景:响应营养周号召,开启健康新征程 (一)2025营养周主题解读 2025年全民营养周的核心主题“吃动平衡,健康体重,全民行动…...
考拉悠然:科技与匠心,以烟草虫情AI监测系统共筑品质未来
李工,一位在卷烟厂辛勤耕耘了二十余载的老工艺师,他的青春和汗水,都挥洒在了这片弥漫着烟草香气的土地上。他像一位老农,精心呵护着每一片烟叶,因为他深知,烟草品质的把控,就是守护着卷烟厂的生…...