Ascend的aclgraph(二)_npu_backend中还有些什么秘密?
1 _npu_backend
文章还是从代码开始
import torch_npu, torchair
config = torchair.CompilerConfig()
# 设置图下沉执行模式
config.mode = "reduce-overhead"
npu_backend = torchair.get_npu_backend(compiler_config=config)
opt_model = torch.compile(model, backend=npu_backend)
get_npu_backend
调用的是_npu_backend
函数。整体流程图如下:
上文Ascend的aclgraph(一)aclgraph是什么?torchair又是怎么成图的?对get_compiler
函数进行了分析。本章接着分析剩余的4个部分。
1.1 _wrap_compiler
_wrap_compiler
是一个装饰器,先看代码:
def _wrap_compiler(compiler: Callable, compiler_config: CompilerConfig):@functools.wraps(compiler)def wrapped_compiler(gm: torch.fx.GraphModule,example_inputs: List[torch.Tensor],is_inference: bool):if is_inference and compiler_config.experimental_config.npu_fx_pass:_npu_joint_graph_passes(gm)return compiler(gm, example_inputs)@functools.wraps(compiler)def joint_compiler(gm: torch.fx.GraphModule,example_inputs: List[torch.Tensor]):if compiler_config.experimental_config.npu_fx_pass:_npu_joint_graph_passes(gm)return compiler(gm, example_inputs)fw_compiler = functools.partial(wrapped_compiler, is_inference=False)inference_compiler = functools.partial(wrapped_compiler, is_inference=True)return fw_compiler, inference_compiler, joint_compiler
依旧使用了python的偏函数的功能:functools.partial
。该函数中主要返回了3个compiler
对象。
首先是wrapped_compiler
,该函数的功能是主要功能就是调用compiler(gm, example_inputs)
,但根据是否是推理场景,加入了_npu_joint_graph_passes
(torch._inductor.fx_passes.joint_graph模块提供)。
joint_graph在torch.compile中一般表示的前向和反向混在一起的一张联合图。
- 对
wrapped_compiler
函数进行了2次封装,区别在于is_inference
参数。从代码上推测,_npu_joint_graph_passes
在推理的时候,需要对图进行_npu_joint_graph_passes
优化。在is_inference = True
的时候,返回inference_compiler
。在is_inference = False
的时候,返回fw_compiler
,可以认为该compile是带有反向图功能的。这里也可以看出,torchair也是可以用在训练里面的。 - 另外根据
compiler_config.experimental_config.npu_fx_pass
参数,专门返回了一个joint_compiler
函数。
通过该装饰器,总共返回3个compile执行函数。
return fw_compiler, inference_compiler, joint_compiler
1.2 _set_gear_to_compiler
接着看_set_gear_to_compiler
函数。
该函数依旧是个装饰器。先理解下guard是什么,这也是torch.compile中的概念。
在 PyTorch 的 torch.compile 功能中,“guard” 是一个关键概念,它用于确保编译后的代码仅在满足特定条件时执行。Guard 本质上是编译器为了保证优化后的模型行为与原始未优化模型一致而设置的断言或检查点。
Guard 的组成
Guards 主要由以下几部分组成:
- 类型检查:确保输入张量的数据类型保持不变。
- 形状检查:验证输入张量的形状是否符合预期。这对于动态形状的模型尤为重要。
- 设备检查:确认所有张量都在正确的设备(如 CPU 或 GPU)上。
- 值检查:在某些情况下,可能还需要对特定值进行检查以确保逻辑正确性。例如,对于依赖于某个具体值的控制流。
Guard 的作用
- 稳定性:Guards 确保了即使在不同的运行条件下,编译后的模型也能稳定、正确地运行。如果任何 guard 条件不满足,则会触发回退机制,即重新追踪或编译该部分计算图。
- 性能优化:通过设置 guards,编译器能够在假设这些条件始终成立的情况下应用更激进的优化策略,因为它们不需要考虑所有可能的变化情况。这可以显著提高执行效率和资源利用率。
- 动态适应性:尽管静态编译能够带来性能提升,但深度学习模型往往需要处理多样化的输入。Guards 提供了一种方式来平衡静态编译带来的性能优势与动态适应不同输入需求之间的矛盾。
举例来说,在使用 torch.compile 对模型进行编译时,如果你有一个接受可变批次大小的模型,那么编译器会在生成的代码中加入关于输入张量形状的 guards。这意味着,只要输入的批次大小不超过预设的最大值并且不低于最小值,优化后的代码就会被执行;否则,将触发重新编译或者采用默认执行路径。
重点关注函数中的guard_gears_shape
。该函数是对图输入的情况进行判断,输入的tensor的shape需要在指定的范围内变化,也就说,torchair当前使用torch.compile支持有限个shape是变化的场景。如果超过了指定的范围,程序就会就会报错推出。
def _set_gear_to_compiler(compiler: Callable, compiler_config: CompilerConfig, input_dim_gears: Dict[int, List[int]]):@functools.wraps(compiler)def gear_compiler(gm: torch.fx.GraphModule,example_inputs: List[torch.Tensor],):for i, dim_gears in input_dim_gears.items():set_dim_gears(example_inputs[i], dim_gears)guard_gears_shape(example_inputs)return compiler(gm, example_inputs)return gear_compiler
注意,_npu_backend
中对上述的fw_compiler
和inference_compiler
都调用了_set_gear_to_compiler
函数。这里也好也好理解,训练和推理都是要确认图的功能是正确的,对图是否改变要有感知。
fw_compiler = _set_gear_to_compiler(fw_compiler, compiler_config, input_dim_gears)
inference_compiler = _set_gear_to_compiler(inference_compiler, compiler_config, input_dim_gears)
1.3 _get_partition_fn
接着看_get_partition_fn
函数。
def _get_partition_fn(compiler_config: CompilerConfig):def partition_fn(graph: torch.fx.GraphModule, joint_inputs, **kwargs):_npu_joint_graph_passes(graph)return default_partition(graph, joint_inputs, **kwargs)if compiler_config.experimental_config.npu_fx_pass:return partition_fnreturn default_partition
partition是torch.compile中计算图拆分的概念,也就是将joint graph的图拆分为前向图和反向图,此处先给出default_partition
的解释。
default_partition:模拟了PyTorch的默认行为,找出从forward的输入到输出的所有算子输出,剩余部分都视为backward部分,从而分割出正反向graph,forward的所有中间结果都保存用于backward。
default_partition
是torch._functorch.partitioners
中的函数。_get_partition_fn
返回的partition函数类型与compiler_config.experimental_config.npu_fx_pass
参数相关。在配置compiler_config.experimental_config.npu_fx_pass
情况下,返回与输入参数joint_inputs
相关的partition函数。
return default_partition(graph, joint_inputs, **kwargs)
1.4 aot_module_simplified
aot_module_simplified
是_npu_backend
中的最后一个调用函数。
return aot_module_simplified(gm, example_inputs, fw_compiler=fw_compiler, bw_compiler=compiler,decompositions=decompositions, partition_fn=partition_fn,keep_inference_input_mutations=keep_inference_input_mutations,inference_compiler=inference_compiler)
aot_module_simplified
是torch._functorch.aot_autograd
中的函数,看如下注释。
aot_module_simplified
是 PyTorch 中与 Ahead-of-Time (AOT) 编译相关的函数,特别是在使用 TorchInductor 后端时。AOT 编译是指在模型运行之前预先编译模型的计算图,以期达到加速执行和优化资源使用的目的。
在 PyTorch 中,TorchInductor 是 torch.compile 的一个后端,它旨在为 GPU 和 CPU 生成高度优化的代码。aot_module_simplified 函数通常用于简化将一个 PyTorch 模型准备好进行 AOT 编译的过程,简单理解就是AOT编译前的预操作。其主要作用包括:
- 准备模型进行AOT编译:该函数帮助用户方便地指定需要进行 AOT 编译的模型部分。通过处理模型的输入输出、设置必要的编译参数等,使得模型可以直接进入编译流程。
- 优化计算图:在编译阶段,aot_module_simplified 可能会应用一系列优化措施,如算子融合、内存优化等,目的是提高最终编译产物的执行效率。
- 简化流程:正如其名“simplified”所暗示的,这个函数简化了 AOT 编译的准备工作,允许用户以较少的代码实现模型的编译和优化,而不需要深入了解编译过程中的复杂细节。
- 跨设备支持:它可能还提供对不同硬件平台的支持,确保编译后的模型可以在指定的目标设备(如GPU或CPU)上高效运行。
总的来说,aot_module_simplified 提供了一种简化的方式,让开发者可以更容易地利用 PyTorch 的 AOT 编译功能来优化他们的模型性能,尤其是在使用 TorchInductor 后端时。这有助于降低高性能模型部署的门槛,使得更多开发者能够从先进的编译技术和硬件优化中受益。需要注意的是,具体的实现细节可能会随着 PyTorch 版本的更新而有所变化。
2 torch.compile
回到最开始的例子中,可以看到传入到torch.compile
中的backend是get_npu_backend
返回的,也就是
npu_backend = torchair.get_npu_backend(compiler_config=config)
opt_model = torch.compile(model, backend=npu_backend)
从Ascend的aclgraph(一)aclgraph是什么?torchair又是怎么成图的?开始,反反复复提到了torch.compile概念,看来想了解aclgraph,必须对torch.compile做一个基本的了解。
但由于其代码内容比较复杂,小编认知有限,接下来的内容,对其涉及到的基本概念做个介绍。
2.1 torch.compile背景
参考:https://zhuanlan.zhihu.com/p/9418379234
为了改善模型训练推理性能,Pytorch推出torch.compile,并从torch 1.xx更为torch 2.xx,改变并增强了Pytorch在编译器级别的运行方式,同时开始将pytorch的部分代码从C++迁移回python,主要有以下特点:
- 将动态的 PyTorch 模型转换为静态的、优化过的执行图,从而减少运行时的开销,提高计算效率。
- 用户无需手动修改模型代码或选择特定的优化策略,torch.compile可以自动识别和应用最佳的优化路径,使得模型加速更加便捷。
- 支持广泛的 PyTorch API 和操作,确保大部分现有模型在使用 torch.compile后无需进行大的调整即可获益于性能提升。
torch.compile
的出现给我的感觉就是,torch.eager模式性能基本优化到头了,从python->c++->cuda(npu)
,流程上已经没有可以优化的空间。而如果将模型整形成一张图,图的优化空间比较大,有可为。可能有的小伙伴会问:tensorflow的图模式不是已经很好了吗?tensorflow为什么会日落西山了
这里允许小编胡诌一下。
1、tensorflow在小模型时代,或者说模型输入shape,dtype或format如果保持不变的话,那么使用tensorflow成图的性能依旧不输torch的eager模式,甚至是现在的torch.compile模式;
2、时代成就英雄。tensorflow以图模式(性能)起家,torch以eager模式(易用性)起家,而时代的浪潮是:模型的输入是动态shape的。
- 图模式的性能来源于:模型结构不变(也即是没有if,else等能改变图结构)、输入shape不变(显存效率)等。这样的情况下,模型能做各种算子融合、ir融合的pass,内存利用率也能达到极致。
- 而在动态shape下,图模式的这些优势荡然无存,反而因为图的反复编译,造成的性能的劣化。而torch以eager模式不存在图编译的概念,每次都是单算子下发,时代的浪潮并没有掀翻它,反而是在给它助力。
mindspore(昇思),起步也是从图模式开始,没有赶上时代的步伐,现在还在时代浪潮中被拉扯,而回头看pytorch已然成为了AI领域的的基础设施。
留个问题:大语言模型(LLM)时代,模型的输入也是变化的,那么做图模式的收益来自于哪里呢?
想想看,哪些地方的输入shape是不变的。回答留在后面的文章中。
2.2 torch.compile的组成
torch.compile主要包含四个基础组件:
- TorchDynamo:从python bytecode中解析构建计算图,是一个动态的、Python级别的编译器,旨在捕捉 PyTorch 模型的动态执行路径,将其转换为优化代码,实现bytecode-to-bytecode编译。
- AOTAutograd:是 PyTorch 引入的一种自动求导机制,旨在在模型执行之前预先生成梯度计算的代码。这种方法通过静态分析模型的前向计算图,提前生成反向传播所需的梯度计算逻辑,从而减少运行时的开销,提升训练效率。
- PrimTorch:将2000+ Pytorch op规范化为约250个原始op封闭集合,是 PyTorch 的一个中间表示(Intermediate Representation, IR)层,负责将高层的 PyTorch 操作转换为更低层次的、适合进一步优化和编译的基础操作。它通过简化和标准化操作,提高编译器和后端优化器处理计算图的效率。
- TorchInductor:深度学习编译器,可为多种加速器和后端生成代码,生成OpenAI Triton(Nvidia/AMD GPU)和OpenMP/C++(CPU)代码,它利用多种优化技术,包括内存优化、并行化和低层次的代码生成,以最大化计算性能。
以上四个组件都是用Python编写的,支持动态shape(即能够发送不同大小的张量而无需重新编译),实现灵活并减低开发人员和供应商开发门槛的效果。
2.3 torch.compile支持的后端
当前支持的后端有:
>>> import torch
>>> import torch._dynamo as dynamo
>>> dynamo.list_backends()
['cudagraphs', 'inductor', 'onnxrt', 'openxla', 'tvm']
一般默认的是inductor
,使用的方式是:
import torch
import torchvision.models as modelsmodel = models.resnet18().cuda(
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)
compiled_model = torch.compile(model, backend="inductor") # 通过backend参数指定后端,默认为inductor
# compiled_model = torch._dynamo.optimize("inductor")(fn) # 也可以通过torch._dynamo.optmize函数进行编译x = torch.randn(16, 3, 224, 224).cuda()
optimizer.zero_grad()
out = compiled_model(x)
out.sum().backward()
optimizer.step()
请注意代码中注释的部分:
compiled_model = torch.compile(model, backend="inductor") # 通过backend参数指定后端,默认为inductor
# compiled_model = torch._dynamo.optimize("inductor")(fn) # 也可以通过torch._dynamo.optmize函数进行编译
也就是说torch._dynamo.optimize("inductor")(fn)
和torch.compile(model, backend="inductor")
是等价调用的。
2.3.1 torch.compile调用
torch.compile函数源码解析:torch.compile只是对torch._dynamo.optmize
函数的简单封装
def compile(model: Optional[Callable] = None, *, # Module/function to optimizefullgraph: builtins.bool = False, #If False (default), torch.compile attempts to discover compileable regions in the function that it will optimize. If True, then we require that the entire function be capturable into a single graph. If this is not possible (that is, if there are graph breaks), then this will raise an error.dynamic: Optional[builtins.bool] = None, # dynamic shapebackend: Union[str, Callable] = "inductor", # backend to be usedmode: Union[str, None] = None, # Can be either "default", "reduce-overhead", "max-autotune" or "max-autotune-no-cudagraphs"options: Optional[Dict[str, Union[str, builtins.int, builtins.bool]]] = None, # A dictionary of options to pass to the backend. Some notable ones to try out aredisable: builtins.bool = False) # Turn torch.compile() into a no-op for testing-> Callable:# 中间代码省略... return torch._dynamo.optimize(backend=backend, nopython=fullgraph, dynamic=dynamic, disable=disable)(model)
torch._dynamo.optimize
函数解析:进一步分析torch._dynamo.optimize
的函数调用栈,会发现torch._dynamo.optimize
只是通过_optimize_catch_errors
函数返回了一个OptimizeContext
对象。
def _optimize(rebuild_ctx: Callable[[], Union[OptimizeContext, _NullDecorator]],backend="inductor",*,nopython=False,guard_export_fn=None,guard_fail_fn=None,disable=False,dynamic=None,
) -> Union[OptimizeContext, _NullDecorator]:# 中间代码省略...return _optimize_catch_errors(convert_frame.convert_frame(backend, hooks=hooks), // backend,回调函数hooks,backend_ctx_ctor,dynamic=dynamic,compiler_config=backend.get_compiler_config()if hasattr(backend, "get_compiler_config")else None,rebuild_ctx=rebuild_ctx,)# ---------------------------------------------------------------------------------------------------------------------------------------
def _optimize_catch_errors(compile_fn,hooks: Hooks,backend_ctx_ctor=null_context,export=False,dynamic=None,compiler_config=None,rebuild_ctx=None,
):return OptimizeContext(convert_frame.catch_errors_wrapper(compile_fn, hooks), // 回调函数backend_ctx_ctor=backend_ctx_ctor,first_ctx=True,export=export,dynamic=dynamic,compiler_config=compiler_config,rebuild_ctx=rebuild_ctx,)
因此,经过torch.compile装饰过的函数/模型成为一个OptimizeContext
,其中convert_frame.catch_errors_wrapper
成为了OptimizeContext中的一个回调函数 (callback),而这个回调函数包含编译函数入口,如inductor或自定义编译函数。由此可见,torch.compile并没有进行实际的编译,只是做一些简单的初始化工作,只有第一次正式执行代码前才会进行编译。
torch.compile优化流程:基于TorchDynamo和AOTAutograd构建Pytorch的前向和反向计算图,通过PrimTorch将op拆解转化为更低层次的、适合进一步优化和编译的基础op,最后Inductor进行算子融合等图优化并针对特定硬件生成triton(GPU)或OpenMP/C++(CPU)优化代码。
2.3.2 实现自定义的backend后端
从如上TorchInductor
的定义:深度学习编译器,可为多种加速器和后端生成代码,生成OpenAI Triton(Nvidia/AMD GPU)和OpenMP/C++(CPU)代码。也就是说,这种后端的作用,是为了生成能够执行的代码。那是否可以自己自定义后端实现?来来来,结合例子试一下。
import torch
from typing import List
import torch._dynamo as dynamodef my_compiler(gm: torch.fx.GraphModule, example_inputs_: List[torch.Tensor]):print("===============my compiler=================")gm.graph.print_tabular() # 格式化输出FX Graphprint("code is:",gm.code) # 对应的python代码return gm# 调用torch._dynamo.optimize函数
def my_func(x, y):if x.sum() > y.sum():loss = torch.cos(torch.cos(x))else:loss = torch.cos(torch.cos(y))return lossdef test():func = dynamo.optimize(my_compiler)(my_func) // my_compiler就是自定义的后端,my_func就是模型x, y = torch.randn(10,requires_grad=True), torch.randn(10,requires_grad=True)func(x,y)test()
如上,my_compiler
就是自定义的而后端。
看到这里也就明白了。_npu_backend
的后端也是自定义的,生成的或者优化后的代码是能在npu上执行的代码。
下一篇对TorchDynamo做个一个简单的了解。
相关文章:

Ascend的aclgraph(二)_npu_backend中还有些什么秘密?
1 _npu_backend 文章还是从代码开始 import torch_npu, torchair config torchair.CompilerConfig() # 设置图下沉执行模式 config.mode "reduce-overhead" npu_backend torchair.get_npu_backend(compiler_configconfig) opt_model torch.compile(model, back…...

ventoy安全启动怎么选_ventoy安全启动支持是开还是关
ventoy安全启动怎么选?Ventoy新一代多系统启动U盘解决方案。国产开源U盘启动制作工具,支持Legacy BIOS和UEFI模式,理论上几乎支持任何ISO镜像文件,支持加载多个不同类型的ISO文件启动,无需反复地格式化U盘,…...
MySQL文章总结,简单整理和详细整理
这篇博客文章《MySQL 有这一篇就够(呕心狂敲37k字,只为博君一点赞!!!)》是一篇非常全面的MySQL基础教程,适合初学者和需要复习MySQL知识的开发者。以下是文章的核心内容整理: 一、SQ…...

CC53.【C++ Cont】二分查找的普通模版
目录 1.知识回顾 2.关键点 特点 三个模版 普通的模版(有局限) 以LeetCode上的一道题为例:704. 二分查找 分析 引入二段性:分两段,舍一段,操作另一段(这个是二分查找的本质!) 代码 提交结果 当然也可以使用随机数来分两段 普通模版总结 1.知识回顾 之前在C语言专栏…...
泛型加持的策略模式:打造高扩展的通用策略工具类
一、传统策略模式的痛点与突破 1.1 传统策略实现回顾 // 传统支付策略接口 public interface PaymentStrategy {void pay(BigDecimal amount); }// 具体策略实现 public class AlipayStrategy implements PaymentStrategy {public void pay(BigDecimal amount) { /* 支付宝支…...

【优选算法 | 链表】链表操作技巧:常见算法
算法相关知识点可以通过点击以下链接进行学习一起加油!双指针滑动窗口二分查找前缀和位运算模拟 链表是一种灵活的数据结构,广泛用于需要频繁插入和删除的场景。掌握链表的常见操作技巧,如插入、删除、翻转和合并等,能帮助开发者更…...
HTTP:十三.HTTP日志
日志记录 日志记录了跟踪使用情况、安全性、计费、错误检验。记录事务的基本信息。通常会记录下来的几个字段示例为: HTTP方法:主要记录事务用了什么方法客户端和服务器的HTTP版本:给出客户端和服务器有关的提示,比如兼容性提示什么的所请求资源的URL:记录Web站点某个资源…...
web 自动化之 selenium 元素四大操作三大切换等待
文章目录 一、元素的四大操作二、三大切换&等待1、切换窗口:当定位的元素不在当前窗口,则需要切换窗口2、切换iframe:当定位的元素在frame/iframe,则需要切换3、切换弹出窗口 一、元素的四大操作 1、输入 2、点击 3、获取文本 4、获取属…...
FEKO许可证的安全与合规性
在电磁仿真领域,FEKO软件因其出类拔萃的性能和广泛的应用场景,赢得了全球用户的广泛赞誉。但在这背后,是什么让FEKO在众多竞争者中脱颖而出?答案是其许可证的安全与合规性。它们不仅为用户提供了坚固的保障,更确保了用…...

w~大模型~合集30
我自己的原文哦~ https://blog.51cto.com/whaosoft/13284996 #VideoMamba 视频理解因大量时空冗余和复杂时空依赖,同时克服两个问题难度巨大,CNN 和 Transformer 及 Uniformer 都难以胜任,Mamba 是个好思路,让我们看看本文是…...

PBR材质-Unity/Blender/UE
目录 前言: 一、Unity: 二、Blender: 三、UE: 四、全家福: 五、后记: 前言: PBR流程作为表达物理效果的经典方式,很值得一学。纹理贴图使用的是上一期的Textures | cgbookcas…...

websocketpp 安装及使用
介绍 WebSocket 是从 HTML5 开始支持的一种网页端和服务端保持长连接的消息推送机制。 传统的 web 程序都是属于 "一问一答" 的形式,即客户端给服务器发送了一个 HTTP 请求,服务器给客户端返回一个 HTTP 响应。这种情况下服务器是属于被动…...
web:InfiniteScroll 无限滚动
InfiniteScroll 无限滚动 分页加载 <div class"data-box" v-infinite-scroll"loadMore"> <li v-fori in dataList></li> </div>form: {current: 1,size: 10,}loadMore(){console.log(this.dataList.length, this.total ,8888)if…...
LeetCode[101]对称二叉树
思路: 对称二叉树是左右子树对称,而不是左右子树相等,所以假设一个树只有3个节点,那么判断这个数是否是对称二叉树,肯定是先判断左右两个树,然后再看根节点,这样递归顺序我们就确认了࿰…...
c/c++爬虫总结
GitHub 开源 C/C 网页爬虫探究:协议、实现与测试 网页爬虫,作为一种自动化获取网络信息的强大工具,在搜索引擎、数据挖掘、市场分析等领域扮演着至关重要的角色。对于希望深入理解网络工作原理和数据提取技术的 C/C 开发者,尤其是…...
js fetch流式请求 AI动态生成文本,实现逐字生成渲染效果
开启流式请求:向后端接口发起普通的 fetch,它会返回一个包含 ReadableStream 的 Response 对象获取流式读取器:调用 response.body.getReader() 获取一个 ReadableStreamDefaultReader 实例循环读取数据块:在 while(true) 循环或 …...

第8章-2 查询执行的基础
上一篇:《第8章-1 查询性能优化-优化数据访问》,接着来了解查询执行的过程,这个对sql执行有个更直观的了解。 查询执行的基础 当希望MySQL能够以更高的性能运行查询时,最好的办法就是弄清楚MySQL是如何优化和执行查询的。一旦理解…...

java面试OOM汇总
在正式 Minor GC 前,JVM 会先检查新生代中对象,是比老年代中剩余空间大还是小。假如 Minor GC之后 Survivor 区放不下剩余对象,这些对象就要进入老年代 老年代剩余空间大于新生代中的对象大小,那就直接 Minor GC, GC 完…...
Java面试全记录:Spring Cloud+Kafka+Redis实战解析
Java面试全记录:Spring CloudKafkaRedis实战解析 人物设定 面试官:来自某互联网大厂资深架构师,着深灰色西装,手持MacBook Pro 候选人:张伟(随机生成),28岁,硕士&…...
com.fasterxml.jackson.dataformat.xml.XmlMapper把对象转换xml格式,属性放到标签<>里边
之前从没用过xml和对象相互转换,最近项目接了政府相关的。需要用xml格式数据进行相互转换。有些小问题,困扰了我一下下。 1.有些属性需要放到标签里边,有的需要放到标签子集。 2.xml需要加<?xml version"1.0" encoding"…...
LiveData:Android响应式编程的核心利器
LiveData是一种可观察的数据持有类,用于在Android应用中实现数据的响应式编程。它具有以下特点和作用: 特点 生命周期感知:LiveData能够感知与其关联的组件(如Activity、Fragment)的生命周期状态。只有当组件处于活跃状态(如Activity处于RESUMED状态)时,LiveData才会将…...
Browserless 快速上手
要将你提供的 HTML 模板和数据结构转换为可以用于 Browserless /pdf 接口的 JSON 请求体(且能正确渲染为 PDF),需要满足以下几点: ✅ 最终目标格式(这是能用的格式): json 复制编辑 { "h…...
安装Hadoop并运行WordCount程序
一、安装 Java Hadoop 依赖 Java,首先需要安装 Java 开发工具包(JDK)。以 Ubuntu 为例: bash sudo apt update sudo apt install openjdk-8-jdk安装后,设置环境变量: bash echo export JAVA_HOME/usr/li…...

react-diff-viewer 如何实现语法高亮
前言 react-diff-viewer 是一个很好的 diff 展示库,但是也有一些坑点和不完善的地方,本文旨在描述如何在这个库中实现自定义语法高亮。 Syntax highlighting is a bit tricky when combined with diff. Here, React Diff Viewer provides a simple rend…...

自定义prometheus exporter实现监控阿里云RDS
# 自定义 Prometheus Exporter 实现多 RDS 数据采集## 背景1. Prometheus 官网提供的 MySQL Exporter 对于 MySQL 实例只能一个进程监控一个实例,数据库实例很多的情况下,不方便管理。 2. 内部有定制化监控需求,RDS 默认无法实现,…...

【计算机网络】--tcp三次握手
文章目录 示意图:抓包结果:第一次握手(Client → Server)第二次握手(Server → Client)第三次握手(Client → Server)为什么是三次握手 不是两次或者四次 示意图: 抓包结…...
List<T>中每次取固定长度的数据
工具类方法 package org.common.util; import java.util.ArrayList; import java.util.Iterator; import java.util.List;/*** 批处理取值组件* param <T>*/ public class BatchIterator<T> implements Iterator<List<T>> {private final List<T&g…...

UI-TARS: 基于视觉语言模型的多模式代理
GitHub:https://github.com/bytedance/UI-TARS 更多AI开源软件:发现分享好用的AI工具、AI开源软件、AI模型、AI变现 - 小众AI 基于视觉语言模型(Vision-Language Model)的 GUI 代理应用,允许用户通过自然语言控制电脑操…...
02_线性模型(回归线性模型)
描述 线性模型是在实践中广泛使用的一类模型,线性模型利用输入特征的线性函数(linear function)进行预测。 用于回归的线性模型 对于回归问题,线性模型预测的一般公式如下: $ \widehat y w[0]*x[0]w[1]*x[1]…w[p…...

Spark SQL 运行架构详解(专业解释+番茄炒蛋例子解读)
1. 整体架构概览 Spark SQL的运行过程可以想象成一个"SQL查询的加工流水线",从原始SQL语句开始,经过多个阶段的处理和优化,最终变成分布式计算任务执行。主要流程如下: SQL Query → 解析 → 逻辑计划 → 优化 → 物理…...