流批一体向量化计算引擎 Flex 在蚂蚁的探索和实践

news2024/12/27 9:48:11

编者按:Flex是蚂蚁数据部自研的一款流批一体的向量化引擎,Flex是Fink和Velox的全称,也是Flexible的前缀,被赋予了灵活可插拔的寓意。本文将重点从向量化技术背景、Flex架构方案和未来规划三个方面展开论述。

作者介绍刘勇,蚂蚁数据部分布式计算引擎技术专家,Calcite Committer、Flink Contributor。毕业8年一直对分布式计算领域保持着十年如一日的热情与专注。

  

一、背景

1. 什么是向量化计算?

1)并行数据处理SIMD指令

以下面循环代码为例,计算在CPU内完成需经三步:

  • 加载(Load),从内存加载2个源操作数(a[i]和b[i])到2个寄存器。
  • 计算(Compute),执行加法指令,作用于2个寄存器里的源操作数副本,结果产生到目标寄存器。
  • 存储(Store),将目标寄存器的数据存入(拷贝)到目标内存位置(c[i])。
void addArrays(const int* a, const int* b, int* c, int num) {
  for (int i = 0; i < num; ++i) {
    c[i] = a[i] + b[i];
  }
}

该流程即对应传统的计算架构:单指令单数据(SISD)顺序架构,任意时间点只有一条指令作用于一条数据流。如果有更宽的寄存器(超机器字长,比如256位16字节),一次性从源内存同时加载更多的数据到寄存器,一条指令作用于寄存器x和y,在x和y的每个分量(比如32位4字节)上并行进行加,并将结果存入寄存器z的各对应分量,最后一次性将寄存器z里的内容存入目标内存,那么就能实现单指令并行处理数据的效果,这就是单指令多数据(SIMD)。

图片

2)向量化执行框架具有的特性

执行引擎常规按行处理的方式,存在以下问题:

  • CPU Cache命中率差。一行的多列(字段)数据的内存紧挨在一起,哪怕只对其中的一个字段做操作,其他字段所占的内存也需要加载进来,这会抢占稀缺的Cache资源。Cache命中会导致被请求的数据从内存加载进Cache,等待内存操作完成会导致CPU执行指令暂停(Memory Stall),这会增加延时,还可能浪费内存带宽。

  • 变长字段影响计算效率。假设一行包括int、string、int三列,其中int类型是固定长度,而string是变长的(一般表示为int len + bytes content),变长列的存在会导致无法通过行号算offset做快速定位。

  • 虚函数开销。对一行的多列进行处理通常会封装在一个循环里,会抽象出一个类似handle的接口(C++虚函数)用于处理某类型数据,各字段类型会override该handle接口。虚函数的调用多一步查表,且无法被内联,循环内高频调用虚函数的性能影响不可忽。

图片

因此,要让向量化计算发挥威力,只使用SIMD指令还不够,还需要对框架层面进行改造,数据按列组织

  • 数据按列组织将提高数据局部性。参与计算的列的多行数据会内存紧凑的保存在一起,CPU可以通过预取指令将接下来要处理的数据加载进Cache,从而减少Memory Stall。不参与计算的列的数据不会与被处理的列竞争Cache,这种内存交互的隔离能提高Cache亲和性。

  • 同一列数据在循环里被施加相同的计算。批量迭代将减少函数调用次数,通过模版能减少虚函数调用,降低运行时开销。针对固定长度类型的列很容易被并行处理(通过行号offset到数据),这样的执行框架也有利于让编译器做自动向量化代码生成,显著减少分支,减轻预测失败的惩罚。结合模板,编译器会为每个实参生成特定实例化代码,避免运行时查找虚函数表,并且由于编译器知道了具体的类型信息,可以对模板函数进行内联展开。

图片

2. 向量化计算在分布式计算领域的现状

随着技术的发展,向量化技术在硬件、指令集、配套工具、类库等得到多方位协同发展。

  • 在指令集层面,当前大多数机器都支持SIMD指令集,比如x86平台的SSE、AVX指令集,ARM平台的NEON指令集。

  • 在编译器层面,现代编译器如GCC、LLVM可以自动将代码中可向量化的部分转成SIMD指令。比如开源有一个基于arrow的表达式计算框架gandiva就是基于LLVM生成SIMD指令。

  • 在类库层面,有支持跨平台的库XSimd,可同时运行在x86和arm架构。

图片

上面讲述了向量化的技术原理和可供使用的方案,我们再看下大数据领域在向量化方向做了哪些探索。首先看下Photon,Photon是Databricks闭源的一个全新的向量化引擎,完全用C++编写。Databricks在过去几年里,一直通过各种技术手段对内部的Spark版本Databricks Runtimes进行优化。

左下图展示了不同DBR版本相对于2.1版本的性能提升情况,可以看到未开启Photon的版本性能提升在1-2倍,自从上了Photon后,性能提升可以达到3-8倍。自从Databricks论文公布其方案和效果后,大数据领域进行了各种尝试和探索。主流分布式计算引擎都在往向量化方向发力且效果显然,Flink作为流批一体实时计算引擎在湖仓一体中起到了关键核心作用,但缺少核心的向量化能力,因此我们蚂蚁在该领域发起了挑战。

图片

由于目前开源社区已经有比较成熟的 Native Engine,如ClickHouse、Velox,具备了优秀的向量化、批处理、列计算等能力,并经过业界广泛验证和实践。因此,我们不打算重头造轮子,而是站在巨人的肩膀上。采用了类似Gluten的实现方式,基于Velox上构建。同时,为了防止闭门造车,在立项之出选择和Gluten社区合作,讨论构建方案以及加强紧密型合作。

自此,该项目在蚂蚁正式落地,内部代号Flex,Flex是Fink和Velox的全称,也是Flexible的前缀,我们希望它能够做到灵活可插拔,像胶水一样扮演其工作。

3. 流批一体向量化引擎在蚂蚁的探索

因此,今年我们在Flink 1.18中首次引入了流批一体向量化计算,通过使用C++开发,利用 SIMD、向量化、列计算等技术,在完全兼容 Flink SQL 语法的基础之上,可以提供比原生的执行引擎更高的吞吐,并可以降低用户 Flink 作业的执行成本。同时,SQL语义对齐,对用户来说,仅仅需要增加配置即可开启,降低存量用户使用成本。该技术具有较强的创新性和技术挑战性,属于自主研发,弥补了业界空白

二、总体方案

Velox算子面向传统批处理场景,算子本身无法处理回撤消息,即天然和流式计算场景相违背,因此无法直接基于gluten的substrain plan方案。该方案实现复杂工作量大,但是也没法支持状态算子。同时,为了复用Velox一些现有的能力,不必重复造轮子,因此我们另辟蹊径采用了新的方案,新方案可以做到真正的流批一体;同时,向量化核心效果在于表达式计算。因此非常适合Calc算子,而且Calc算子不区分流和批,也就是说流任务和批任务都可以享受到向量化带来的技术红利,也可以使用到Velox的表达式计算能力。为此,我们从功能性、正确性、易用性和稳定性四个方面进行全方位建设。

图片

1. 功能性建设

1)Native算子层优化

NativeCalc算子优化
  • 在用户向量化作业验证过程中,发现对于任务中含有很多RexInputRef引用字段,这些字段数据内容长且没有加工逻辑,如果把使用到的Scheme字段全部做攒批和数据转换,转换开销将近11%。我们可以将引用字段单独拆出来,拆成两个Calc算子,在NativeCalc算子层仅仅将表达式中使用的字段进行数据转换,其他字段仅仅forward到原有费Calc codegen计算进行取值,最后再拼接JoinedRowData。为了高效的拼接JoinedRowData,自然需要插入Calc算子对字段重排序,因此需要增加规则支持projection reorder。通过该优化数据转换层开销降低到6.68%(3.96+2.72)

  • 同时对于表达式里面含有udf,复用此方案把udf也拆到引用字段的Calc里面,就不需要整个Calc算子fallback。

图片

支持NativeSource/NativeSink
  • 对于蚂蚁内部有类似kafka的消息队列sls,sls source/sink当前支持Arrow模式,为了避免冗余的数据行转列开销,插件层直接读写Arrow格式数据,从而避免额外的行转列开销。

2)Plan层优化

  • 为了尽可能多的将DAG中各种算子中包含projection和condition的逻辑中包含simd函数的计算逻辑委托给velox进行计算,我们在plan层增加了多种基于规则的Rule对算子进行拆分合并和交互,全方位的发挥性能极致。目前Calc、Join、Correlate算子都已经支持Native执行

  • 由于流式计算场景需要攒批,发挥列计算的优势,但是攒批和转换数据层存在一定开销,因此对于calc算子中整个表达式tree都没有使用到simd函数,将不翻译成native算子

  • 下面的sql RexCall以blink_json_value为例进行阐述优化规则

projection reorder
  • 以下面sql为例,该规则将RexInputRef引用字段排在RexCall前面,通过一个非Native的Cal记录下引用关系。主要用于和算子层优化结合。

SELECT a, blink_json_value(a, c), b FROM MyTable
Calc(select=[a, f0 AS EXPR$1, b])
+- NativeCalc(select=[a, b, blink_json_value(a, c) AS f0])
   +- LegacyTableSourceScan(table=[[default_catalog, default_database, MyTable, source: [TestTableSource(a, b, c, d)]]], fields=[a, b, c, d]
projections包含simd函数
  • 该sql仅仅projections含有simd函数,condition没有使用,因此filter逻辑还是使用非native的算子。

SELECT a, blink_json_value(b, d) FROM MyTable
WHERE concat(a, '1') is not null

经过优化后的执行计划如下所示

NativeCalc(select=[a, blink_json_value(b, d) AS EXPR$1])
+- Calc(select=[a, b, d], where=[CONCAT(a, '1') IS NOT NULL])
   +- LegacyTableSourceScan(table=[[default_catalog, default_database, MyTable, source: [TestTableSource(a, b, c, d)]]], fields=[a, b, c, d])

condition包含simd函数

  • 该sql projections 没有使用simd函数,condition含有,因此condition部分使用native算子执行。

SELECT a, b, concat(a, '1') FROM MyTable
WHERE blink_json_value(a, c) is not null

经过优化后的执行计划如下所示 

Calc(select=[a, b, concat(a, '1') AS EXPR$1], where=[f0])
+- NativeCalc(select=[a, b, blink_json_value(a, c) IS NOT NULL AS f0])
   +- LegacyTableSourceScan(table=[[default_catalog, default_database, MyTable, source: [TestTableSource(a, b, c, d)]]], fields=[a, b, c, d])

projections和condition都包含simd函数

  • 该sql projections和condition都含有simd函数,因此拆成两个Native算子执行。

SELECT blink_json_value(a, b), concat(c, '1') FROM MyTable
WHERE blink_json_value(a, c) is not null

经过优化后的执行计划如下所示 

NativeCalc(select=[blink_json_value(a, b) AS EXPR$0, CONCAT(c, '1') AS EXPR$1])
+- Calc(select=[a, b, c], where=[f0])
   +- NativeCalc(select=[a, b, c, blink_json_value(a, c) IS NOT NULL AS f0])
      +- LegacyTableSourceScan(table=[[default_catalog, default_database, MyTable, source: [TestTableSource(a, b, c, d)]]], fields=[a, b, c, d])

Inner Join的condition中包含simd函数

  • 下面的双流join例子,不仅仅On条件中包含simd函数,后面的Where逻辑也有。

SELECT a, concat(d, '1') FROM(
            SELECT a, d FROM(
            SELECT a, d
                    FROM leftTable JOIN rightTable ON
                    a = d and blink_json_value(a, a) = concat(a, d))
    WHERE blink_json_value(a, d) = concat(a, d))
  • calcite 解析后的AST树如下


LogicalProject(a=[$0], EXPR$1=[CONCAT($1, _UTF-16LE'1')])
+- LogicalProject(a=[$0], d=[$1])
   +- LogicalFilter(condition=[=(blink_json_value($0, $1), CONCAT($0, $1))])
      +- LogicalProject(a=[$0], d=[$3])
         +- LogicalJoin(condition=[AND(=($0, $3), =(blink_json_value($0, $0), CONCAT($0, $3)))], joinType=[inner])
            :- LogicalTableScan(table=[[default_catalog, default_database, leftTable]])
            +- LogicalTableScan(table=[[default_catalog, default_database, rightTable]])
  • 通过logical plan优化,可以看到两个simd函数,已经抽取到单独的Calc算子,跟在Join后面

FlinkLogicalCalc(select=[a, CONCAT(d, '1') AS EXPR$1], where=[f0])
+- FlinkLogicalCalc(select=[a, d, AND(=(blink_json_value(a, a), CONCAT(a, d)), =(blink_json_value(a, d), CONCAT(a, d))) AS f0])
   +- FlinkLogicalJoin(condition=[=($0, $1)], joinType=[inner])
      :- FlinkLogicalCalc(select=[a])
      :  +- FlinkLogicalLegacyTableSourceScan(table=[[default_catalog, default_database, leftTable, source: [TestTableSource(a, b, c)]]], fields=[a, b, c])
      +- FlinkLogicalCalc(select=[d])
         +- FlinkLogicalLegacyTableSourceScan(table=[[default_catalog, default_database, rightTable, source: [TestTableSource(d, e, f)]]], fields=[d, e, f])
  • 如果不开启native能力,翻译后的ExecPlan如下,其中Calc推到了下面的Join算子

Calc(select=[a, CONCAT(d, '1') AS EXPR$1])
+- Join(joinType=[InnerJoin], where=[((a = d) AND (blink_json_value(a, a) = CONCAT(a, d)) AND (blink_json_value(a, d) = CONCAT(a, d)))], select=[a, d], leftInputSpec=[NoUniqueKey], rightInputSpec=[NoUniqueKey])
   :- Exchange(distribution=[hash[a]])
   :  +- Calc(select=[a])
   :     +- TableSourceScan(table=[[default_catalog, default_database, leftTable]], fields=[a, b, c])
   +- Exchange(distribution=[hash[d]])
      +- Calc(select=[d])
         +- TableSourceScan(table=[[default_catalog, default_database, rightTable]], fields=[d, e, f])
  • 开启native能力后,simd表达式是委托给了native计算

Calc(select=[a, CONCAT(d, '1') AS EXPR$1], where=[f0])
+- NativeCalc(select=[a, d, ((blink_json_value(a, a) = CONCAT(a, d)) AND (blink_json_value(a, d) = CONCAT(a, d))) AS f0])
   +- Join(joinType=[InnerJoin], where=[(a = d)], select=[a, d], leftInputSpec=[NoUniqueKey], rightInputSpec=[NoUniqueKey])
      :- Exchange(distribution=[hash[a]])
      :  +- Calc(select=[a])
      :     +- TableSourceScan(table=[[default_catalog, default_database, leftTable, nativeOperator=[false]]], fields=[a, b, c])
      +- Exchange(distribution=[hash[d]])
         +- Calc(select=[d])
            +- TableSourceScan(table=[[default_catalog, default_database, rightTable, nativeOperator=[false]]], fields=[d, e, f])
Correlate节点对应物理算子condition中包含simd函数
  • 同理,对于Flink内置udtf函数物理算子实现,由于包含condition,对于该场景也是可以使用native进行加速

Calc(select=[a, b, c, f0, f1], where=[((CAST(f1 AS BIGINT) = a) AND (c = f0))])
+- Correlate(invocation=[func($cor0.c)], correlate=[table(func($cor0.c))], select=[a,b,c,f0,f1], joinType=[INNER], condition=[AND(=(blink_json_value($0, _UTF-16LE'$.id'), 2), =(+($1, 1), *($1, $1)))])
   +- LegacyTableSourceScan(table=[[default_catalog, default_database, MyTable, source: [TestTableSource(a, b, c)]]], fields=[a, b, c])

经过优化后的执行计划如下所示 

Calc(select=[a, b, c, f0, f1], where=[f00])
+- NativeCalc(select=[a, b, c, f0, f1, ((blink_json_value(f0, '$.id') = 2) AND (CAST(f1 AS BIGINT) = a) AND (c = f0)) AS f00])
   +- Correlate(invocation=[func($cor0.c)], correlate=[table(func($cor0.c))], select=[a,b,c,f0,f1], joinType=[INNER], condition=[=(+($1, 1), *($1, $1))])
      +- LegacyTableSourceScan(table=[[default_catalog, default_database, MyTable, source: [TestTableSource(a, b, c)]]], fields=[a, b, c])

3)Native层

  • 在native层,我们支持了18个simd函数,其中字符串函数15个,数学函数3个,也补齐了大量velox不支持的Flink内置函数。

4)细粒度Fallback机制

  • 支持细粒度的fallback机制,全部做到可配置

    • 仅含有SIMD函数的表达式才翻译成NativeCalc

    • 支持细粒度的函数签名级黑名单机制

    • 对于SQL timestamp/decimal类型fallback机制,对这种容易出现正确性问题的类型先fallback。

5)其他

  • 支持配置化的函数映射机制,函数覆盖优先级机制

    • 支持配置化的函数映射机制对于flink velox spark/presto函数语义一直但是函数名不一样,可以不用改动c++代码,修改配置即可,函数覆盖优先级机制持配置化的函数映射机制。新引入的函数或者修复velox函数bug无需加入velox,导致整个编译时间很久。在Flex内部加入即可,编译时间从2个小时到2分钟。

2. 正确性建设

这个是重中之重。两套引擎函数行为语义无法保证是对齐的,如何通过自动化手段发现二者行为上的差异?因此我们支持了函数级和作业级两套自动化比对框架。

如何复用Flink原有函数单元测试代码,而不需要改动和重写测试逻辑。然后当前Flink内置函数有两套机制oldstack/newstack,测试框架也不同。为此我们改造原有引擎2套测试框架逻辑,通过反射方式将这些单元测试自动注入向量化配置方式进行自动化比对。每天定时跑脚本将语义不一致的函数输出出来。通过这个工具,我们发现了Flink引擎本身函数正确性问题4个,都已经提给社区,Flink和Velox函数语义不对齐问题15个。

也支持了作业级端到端比对框架,本质就是上线前双跑比对。对重要作业mock两个作业消费固定的数据集输出到不同表,每天定时跑并输出比对结果报告到钉钉群。

3. 易用性建设

为了全方位提升易用性,我们在引擎层,尽最大程度将简单交给用户,复杂留给自己

  • 如何提前发现用户作业中使用的函数Velox是否支持,可以翻译成Native算子?我们开发了一套自动化编译工具捞取线上作业自动化执行,将各种函数不支持问题问题提前解决掉,从而减少用户干扰。

  • 如何提前知道开发的SIMD函数效果怎么样,是否有性能回退?因此我们基于JMH框架实现了一套端到端的性能测试框架,因为数据转换层有一定开销,因此需要对比整体性能更合理,目前支持GenericRow和ColumnRow。下图是函数使用SIMD实现端到端的性能效果数据。

图片

  • 我们还搭建了向量化大盘,可以看到作业级别的效果数据

  • 易用的DAG中native calc/source/sink算子展示

4. 稳定性建设

对向量化作业配置监控告警,提前发现问题。同时,由于Native算子需要额外的native内存,我们plan层自动注入额外资源。

效果

我们从线上捞取符合向量化场景的部分作业跑成向量化方式,下面这张图是真实的效果数据,有13%的作业端到端TPS可以提升1倍以上,37%的作业可以提升40%以上,作业平均提升了75%。其中效果最好的作业提升了14倍。

图片

未来规划

  • 全新的数据转换层支持RowData直接转velox RowVector,减少转换层数据拷贝开销

  • 支持更多算子,非状态算子如维表算子,状态算子等

  • 支持更多SIMD,支持SQL全类型,对齐Flink所有内置函数

  • 和Paimon结合,支持Native Parquet/Orc Reader

参考文献

  • https://github.com/apache/incubator-gluten

  • https://github.com/facebookincubator/velox

  • https://tech.meituan.com/2024/06/23/spark-gluten-velox.html

欢迎关注「蚂蚁数据AntData」CSDN 官方博客,跟技术爱好者一起学习Data+AI前沿技术知识~

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

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

相关文章

upload-labs关卡记录11

先上传一个一句话木马试试&#xff0c;居然可以上传成功&#xff0c;复制图片链接&#xff0c;在另一个窗口打开&#xff1a; 会发现&#xff0c;我们明明上传的是shell.php&#xff0c;但是这里就是没有了php,这样我们在执行我们相关的语句的时候就无法执行了&#xff1a; 就…

WebRTC服务质量(08)- 重传机制(05) RTX机制

WebRTC服务质量&#xff08;01&#xff09;- Qos概述 WebRTC服务质量&#xff08;02&#xff09;- RTP协议 WebRTC服务质量&#xff08;03&#xff09;- RTCP协议 WebRTC服务质量&#xff08;04&#xff09;- 重传机制&#xff08;01) RTX NACK概述 WebRTC服务质量&#xff08;…

借助 obdiag,让 OceanBase 参数和变量的对比更简单

本文将介绍 obdiag 工具中参数对比和变量对比功能的适用场景和试用方法。​​​​​​​ obdiag 参数和变量对比功能的适用场景 参数对比功能适用场景 不同observer对于同一参数允许配置不同的值&#xff0c;实际生产环境中&#xff0c;用户可能因多种原因在不同observer上为同…

Net9解决Spire.Pdf替换文字后,文件格式乱掉解决方法

官方文档 https://www.e-iceblue.com/Tutorials/Spire.PDF/Program-Guide/Text/Find-and-replace-text-on-PDF-document-in-C.html C# 在 PDF 中查找替换文本 原文件如下图&#xff0c;替换第一行的新编码&#xff0c;把41230441044替换为41230441000 替换代码如下&#xff…

VBA技术资料MF246:将工作表中形状复制到WORD文档

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。“VBA语言専攻”提供的教程一共九套&#xff0c;分为初级、中级、高级三大部分&#xff0c;教程是对VBA的系统讲解&#…

一次医院RIS系统的升级

2020-03-11 目录 数据库升级... 1 数据结构升级... 1 系统配置... 2 WEB服务器准备... 3 启动ASP.NET State Service服务... 3 检查IIS. 4 发布站点... 4 添加应用程序池... 4 发布网站... 5 处理打印模板... 6 web.config的配置... 6 处理图片文件目录... 6 修改W…

显示 Windows 任务栏

显示 Windows 任务栏 1. 取消勾选自动隐藏任务栏2. 重启 Windows 资源管理器References 1. 取消勾选自动隐藏任务栏 Windows 任务栏具有自动隐藏功能&#xff0c;不使用时自动隐藏&#xff0c;使用时显示。 鼠标右键单击桌面上的空白区域&#xff0c;个性化 -> 任务栏。不…

【Unity3D】Jobs、Burst并行计算裁剪Texture3D物体

版本&#xff1a;Unity2019.4.0f1 PackageManager下载Burst插件(1.2.3版本) 利用如下代码&#xff0c;生成一个Texture3D资源&#xff0c;它只能脚本生成&#xff0c;是一个32*32*32的立方体&#xff0c;导出路径记得改下&#xff0c;不然报错。 using UnityEditor; using Uni…

轻量级安全云存储方案Hoodik

什么是 Hoodik &#xff1f; Hoodik 是一款轻量级、安全且自托管的云存储解决方案。它采用 Rust 和 Vue 设计和构建&#xff0c;专注于端到端加密&#xff0c;保护您的数据免受窥探和黑客的侵害。Hoodik 支持文件上传和下载&#xff0c;让您可以轻松地与其他用户共享文件。简单…

[WASAPI]音频API:从Qt MultipleMedia走到WASAPI,相似与不同

[WASAPI] 从Qt MultipleMedia 来看WASAPI 最近在学习有关Windows上的音频驱动相关的知识&#xff0c;在正式开始说WASAPI之前&#xff0c;我想先说一说Qt的Multiple Media&#xff0c;为什么呢&#xff1f;因为Qt的MultipleMedia实际上是WASAPI的一层封装&#xff0c;它在是线…

Linux 大文件管理与 Hugging Face 模型下载实践:解决磁盘空间与大文件传输的全攻略20241226

Linux 大文件管理与 Hugging Face 模型下载实践&#xff1a;解决磁盘空间与大文件传输的全攻略 引言 在 Linux 系统中管理大文件是一项常见但不容忽视的任务&#xff0c;尤其是在处理复杂场景时&#xff0c;比如磁盘空间不足、断点续传下载模型文件、管理日志文件等。通过实际…

TOGAF之架构标准规范-业务架构

TOGAF标准规范中&#xff0c;业务架构阶段的主要工作是开发支持架构愿景的业务架构。 如上所示&#xff0c;业务架构&#xff08;Business Architecture&#xff09;在TOGAF标准规范中处于B阶段&#xff0c;该阶段的主要内容包括阶段目标、阶段输入、流程步骤、架构方法。 阶段…

aPaaS是什么?有何特点?以及aPaaS核心优势有哪些?

​aPaaS是什么&#xff1f; aPaaS&#xff0c;Application Platform as aService&#xff0c;应用程序平台即服务。国际知名咨询机构 Gartner 对aPaaS所下的定义是&#xff1a;“这是基于PaaS(平台即服务)的一种解决方案&#xff0c;支持应用程序在云端的开发、部署和运行&…

【网络分析工具】WireShark的使用(超详细)

网络分析工具——WireShark的使用 简介WireShark软件安装Wireshark 开始抓包示例WireShark抓包界面WireShark 主要分为这几个界面TCP包的具体内容Wireshark过滤器设置wireshark过滤器表达式的规则Wireshark抓包分析TCP三次握手Wireshark分析常用操作 简介 WireShark是非常流…

前端js验证码插件

相关代码,在最上方的绑定资源

URDF文件中inertial数据的描述坐标系说明

这件事的来源是这样的&#xff1a;结构手动把连杆坐标系下描述的惯性张量数据写入了urdf中&#xff0c;给我到以后发现有问题&#xff0c;给我搞懵了&#xff0c;以为我错了这么多年&#xff0c;于是有了本次的深度调研&#xff0c;先上结论&#xff0c;感兴趣的可以参考后文。…

宠物行业的出路:在爱与陪伴中寻找增长新机遇

在当下的消费市场中&#xff0c;如果说有什么领域能够逆势而上&#xff0c;宠物行业无疑是一个亮点。当人们越来越注重生活品质和精神寄托时&#xff0c;宠物成为了许多人的重要伴侣。它们不仅仅是家庭的一员&#xff0c;更是情感的寄托和生活的调剂。然而&#xff0c;随着行业…

Web前端基础知识(三)

表单的应用非常丰富&#xff0c;可以说&#xff0c;每个网站都会用到表单。下面首先介绍表单中的form标签。 --------------------------------------------------------------------------------------------------------------------------------- <form></form&g…

学习C++:程序的注释

一&#xff0c;注释 作用&#xff1a;在代码种加一些说明和解释&#xff0c;方便自己或其他程序员阅读代码 1&#xff0c;单行注释&#xff1a;// 描述信息 通常放在一行代码的上方&#xff0c;或者一条语句的末尾&#xff0c;对该行代码说明。 2&#xff0c;多行注释&#x…

LAION-SG:一个大规模、高质量的场景图结构注释数据集,为图像-文本模型训练带来了革命性的进步。

2024-12-03&#xff0c;由浙江大学、江南大学、北京大学、阿里巴巴集团和蚂蚁集团联合创建的LAION-SG数据集&#xff0c;通过提供高质量的场景图&#xff08;SG&#xff09;结构注释&#xff0c;显著提升了复杂场景图像生成的性能&#xff0c;为图像-文本模型训练带来了革命性的…