熬夜三晚之深度解析DuckDB MetaPipeline

news2024/11/25 2:23:54

深度解析DuckDB MetaPipeline

深度解析DuckDB MetaPipeline

    1.导语    2.基础理论    3.HashJoin深度解析        3.1 RESULT_COLLECTOR        3.2 PROJECTION        3.3 HASH_JOIN    4.Ready        4.1 翻转        4.2 MetaPipeline与pipeline    5.汇总

1.导语

DuckDB 是一个高性能的分析型数据库系统。它旨在快速、可靠且易于使用。

最近对DuckDB的代码比较感兴趣,就打算读一下看看,这个是一个C++开源项目,非常适合新手学习,特别是从事数据库行业的伙伴。

本节将会深度解析DuckDB的MetaPipeline,以实际两个表join为例

例如:

SELECT name, score FROM student st INNER JOIN score s ON  st.id = s.stu_id;

这个SQL最后的plan为:

➜  debug git:(master) ./duckdb stu
v0.8.1-dev416 9d5158ccd2
Enter ".help" for usage hints.
D EXPLAIN SELECT name, score FROM student st INNER JOIN score s ON  st.id = s.stu_id;

┌─────────────────────────────┐
│┌───────────────────────────┐│
││       Physical Plan       ││
│└───────────────────────────┘│
└─────────────────────────────┘
┌───────────────────────────┐                             
│         PROJECTION        │                             
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │                             
│            name           │                             
│           score           │                             
└─────────────┬─────────────┘                                                          
┌─────────────┴─────────────┐                             
│         HASH_JOIN         │                             
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │                             
│           INNER           │                             
│        stu_id = id        ├──────────────┐              
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │              │              
│           EC: 4           │              │              
│          Cost: 4          │              │              
└─────────────┬─────────────┘              │                                           
┌─────────────┴─────────────┐┌─────────────┴─────────────┐
│         SEQ_SCAN          ││         SEQ_SCAN          │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   ││   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           score           ││          student          │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   ││   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           stu_id          ││             id            │
│           score           ││            name           │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   ││   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           EC: 4           ││           EC: 3           │
└───────────────────────────┘└───────────────────────────┘

在开始本篇文章之前,先问几个问题:

  • MetaPipeline到底发生了什么?

  • 上面计划有几个Plan?

  • 什么是Pipeline?

  • 什么是MetaPipeline?

好了,进入正文。

2.基础理论

MetaPipeline 表示一组都具有相同Sink的Pipeline。

先讲一些预备知识:对于DuckDB,跟arrow类似,plan基本都长这个样子:

c936dfcb937397ec4dad275f0e34a33f.png

Source为输入,Sink为输出,Other Node就是其他节点,当然树不止长这个样子,还可以是多个source,像这样的一张图串起来多个节点就是一个Pipeline。

那么对于一个复杂的查询计划,Pipeline通常是多个,而且还存在一定的依赖关系,例如hashjoin节点必须依赖build端的pipeline产生的数据才可以,所以就有个MetaPipeline用于构建多个Pipeline依赖关系,在最后节点执行时就只有Pipeline了。

3.HashJoin深度解析

对于像这样的查询计划,发生了什么过程呢?构建了几个Pipeline?build端与probe端怎么构建的?

➜  debug git:(master) ./duckdb stu
v0.8.1-dev416 9d5158ccd2
Enter ".help" for usage hints.
D EXPLAIN SELECT name, score FROM student st INNER JOIN score s ON  st.id = s.stu_id;

┌─────────────────────────────┐
│┌───────────────────────────┐│
││       Physical Plan       ││
│└───────────────────────────┘│
└─────────────────────────────┘
┌───────────────────────────┐                             
│         PROJECTION        │                             
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │                             
│            name           │                             
│           score           │                             
└─────────────┬─────────────┘                                                          
┌─────────────┴─────────────┐                             
│         HASH_JOIN         │                             
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │                             
│           INNER           │                             
│        stu_id = id        ├──────────────┐              
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │              │              
│           EC: 4           │              │              
│          Cost: 4          │              │              
└─────────────┬─────────────┘              │                                           
┌─────────────┴─────────────┐┌─────────────┴─────────────┐
│         SEQ_SCAN          ││         SEQ_SCAN          │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   ││   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           score           ││          student          │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   ││   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           stu_id          ││             id            │
│           score           ││            name           │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   ││   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           EC: 4           ││           EC: 3           │
└───────────────────────────┘└───────────────────────────┘

入口InitializeInternal先构建了一个MetaPipeline,其sink为NULL,随后使用这个meta_pipeline来继续构建pipeline。

auto root_pipeline = make_shared<MetaPipeline>(*this, state, nullptr);
root_pipeline->Build(*physical_plan);

3.1 RESULT_COLLECTOR

Build会到用到对应的operator的BuildPipelines,physical_plan第一个operator为RESULT_COLLECTOR,于是调用PhysicalResultCollector::BuildPipelines,PhysicalResultCollector为PhysicalOperator的子类。

void PhysicalResultCollector::BuildPipelines(Pipeline &current, MetaPipeline &meta_pipeline)

当前pipeline会变为:也就是把当前operator作为current的source。

┌───────────────────────────┐
│            NULL           │   -> sink
└─────────────┬─────────────┘ 
┌─────────────┴─────────────┐
│      RESULT_COLLECTOR     │   -> source
└───────────────────────────┘

同时meta_pipeline还创建了一个child_meta_pipeline,sink节点为当前节点,即 RESULT_COLLECTOR

auto &child_meta_pipeline = meta_pipeline.CreateChildMetaPipeline(current, *this);
┌───────────────────────────┐
│      RESULT_COLLECTOR     │   -> sink
└───────────────────────────┘

随后使用child_meta_pipeline继续构建。

child_meta_pipeline.Build(plan);

3.2 PROJECTION

继续往下是PhysicalProjection,这个并没有重写基类的BuildPipelines,于是调用:

void PhysicalOperator::BuildPipelines(Pipeline &current, MetaPipeline &meta_pipeline)

此时添加到当前pipeline的operators中,得到:

┌───────────────────────────┐
│      RESULT_COLLECTOR     │   -> sink
└─────────────┬─────────────┘                             
┌─────────────┴─────────────┐
│         PROJECTION        │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│            name           │   -> operators[0]
│           score           │
└───────────────────────────┘

继续构建它的子operator。

children[0]->BuildPipelines(current, meta_pipeline);

3.3 HASH_JOIN

此时到了hash join:

void PhysicalJoin::BuildJoinPipelines(Pipeline &current, MetaPipeline &meta_pipeline, PhysicalOperator &op)
  1. 保存一份pipeline,后面用

vector<shared_ptr<Pipeline>> pipelines_so_far;
meta_pipeline.GetPipelines(pipelines_so_far, false);
auto last_pipeline = pipelines_so_far.back().get();
  1. 创建一个子MetaPipeline,用于构建build端的hash join。

auto &child_meta_pipeline = meta_pipeline.CreateChildMetaPipeline(current, op);
child_meta_pipeline.Build(*op.children[1]);

此时得到:

┌───────────────────────────┐
│         HASH_JOIN         │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           INNER           │
│        stu_id = id        │   -> sink
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           EC: 4           │
│          Cost: 4          │
└─────────────┬─────────────┘
┌─────────────┴─────────────┐
│         SEQ_SCAN          │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│          student          │   -> source
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│             id            │
│            name           │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           EC: 3           │
└───────────────────────────┘
  1. probe端继续构建,这个BuildPipelines会继续往下调用构建,最后调用到tablescan的构建,由于tablescan并没有重写基类的BuildPipelines,所以还是使基类的,那边是设置source。

op.children[0]->BuildPipelines(current, meta_pipeline);

于是得到:

┌───────────────────────────┐
│      RESULT_COLLECTOR     │   -> sink
└─────────────┬─────────────┘                             
┌─────────────┴─────────────┐
│         PROJECTION        │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│            name           │   -> operators[0]
│           score           │
└─────────────┬─────────────┘                             
┌─────────────┴─────────────┐
│         HASH_JOIN         │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           INNER           │
│        stu_id = id        │  -> operators[1]
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           EC: 4           │
│          Cost: 4          │
└─────────────┬─────────────┘                             
┌─────────────┴─────────────┐
│         SEQ_SCAN          │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           score           │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           stu_id          │   -> source
│           score           │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           EC: 4           │
└───────────────────────────┘
  1. 最后,使用步骤1保存的pipeline,创建了一个child metapipeline。

meta_pipeline.CreateChildPipeline(current, op, last_pipeline);

得到:

┌───────────────────────────┐
│      RESULT_COLLECTOR     │   -> sink
└─────────────┬─────────────┘                             
┌─────────────┴─────────────┐
│         PROJECTION        │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│            name           │   -> operators[0]
│           score           │
└─────────────┬─────────────┘                             
┌─────────────┴─────────────┐
│         HASH_JOIN         │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           INNER           │
│        stu_id = id        │  -> source
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           EC: 4           │
│          Cost: 4          │
└───────────────────────────┘

CreateChildPipeline会为当前pipeline添加依赖关系,对于当前metapipeline,里面有两个pipeline:

pipelines[0]  -> probe 端
pipelines[1] -> child pipeline

CreateChildPipeline会去调用函数AddDependenciesFrom,表示继续添加依赖关系,从start开始,继续往dependencies添加。

假设当前metapipeline有n个pipeline, 图中表示pipelines[s] 为起始pipeline,pipelines[m]dependant,其实就是end,那么会把这中间的所有pipeline添加到当前dependant依赖数组里面。

pipelines[0] 
....
pipelines[s]   ---> start
.....
pipelines[m]   ---> dependant

pipelines[n-1]

结构为:

unordered_map<Pipeline *, vector<Pipeline *>> dependencies;

完成依赖关系后:

pipelines[m] : [pipelines[s]......pipelines[m-1]]

由于这里的s = 0, m = 1,所以依赖关系就只有:

pipelines[1] : [ pipelines[0] ]

pipelines[1] 就是child_pipeline。

即:

child_pipeline : [probe pipeline]

小结1:meta_pipeline.CreateChildMetaPipeline

  • meta_pipeline.children会添加一个MetaPipeline(会创建一个新的pipeline),这个新的MetaPipeline的sink节点为当前operator。

  • 维护当前pipeline与新pipeline的依赖关系。current.dependencies会添加这个新的pipeline,新的pipeline的parents会添加current。

// current、new_pipeline
current.dependencies.push_back(new_pipeline);
new_pipeline.parents.push_back(current);

4.Ready

4.1 翻转

经过上面的执行,InitializeInternal Build就搞定了,此时便进入Ready阶段。

假设以上面hashjoin probe 的pipeline为例:

[result->project->hashjoin->scan] 那么source 为scan, sink为 result,operators[0] = project ,operators[1] = hashjoin。

pipeline的Ready会把这个operators顺序变为:[hashjoin, project]

  • ready前

┌───────────────────────────┐
│      RESULT_COLLECTOR     │   -> sink
└─────────────┬─────────────┘                             
┌─────────────┴─────────────┐
│         PROJECTION        │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│            name           │   -> operators[0]
│           score           │
└─────────────┬─────────────┘                             
┌─────────────┴─────────────┐
│         HASH_JOIN         │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           INNER           │
│        stu_id = id        │  -> operators[1]
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           EC: 4           │
│          Cost: 4          │
└─────────────┬─────────────┘                             
┌─────────────┴─────────────┐
│         SEQ_SCAN          │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           score           │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           stu_id          │   -> source
│           score           │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           EC: 4           │
└───────────────────────────┘
  • ready后

┌───────────────────────────┐
│      RESULT_COLLECTOR     │  -> sink
└─────────────┬─────────────┘
┌─────────────┴─────────────┐
│         HASH_JOIN         │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           INNER           │
│        stu_id = id        │  -> operators[0]
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           EC: 4           │
│          Cost: 4          │
└─────────────┬─────────────┘
┌─────────────┴─────────────┐
│         PROJECTION        │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│            name           │  -> operators[1]
│           score           │
└─────────────┬─────────────┘
┌─────────────┴─────────────┐
│         SEQ_SCAN          │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           score           │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           stu_id          │  -> source
│           score           │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           EC: 4           │
└───────────────────────────┘

4.2 MetaPipeline与pipeline

另外,在这个阶段,我们可以看到Build之后的MetaPipelie与pipeline的关系,分别如下。

  • 第一个MetaPipeline

{pipelines[1], children[1]}

┌───────────────────────────┐
│      RESULT_COLLECTOR     │
└───────────────────────────┘

children MetaPipeline

  • 上面的child MetaPipeline

{pipelines[2], children[1]}

需要注意Ready()之后除了operators逆序,可以看到operations数组顺序发生了变化。

pipelines[0]

┌───────────────────────────┐
│      RESULT_COLLECTOR     │
└─────────────┬─────────────┘
┌─────────────┴─────────────┐
│         HASH_JOIN         │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           INNER           │
│        stu_id = id        │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           EC: 4           │
│          Cost: 4          │
└─────────────┬─────────────┘
┌─────────────┴─────────────┐
│         PROJECTION        │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│            name           │
│           score           │
└─────────────┬─────────────┘
┌─────────────┴─────────────┐
│         SEQ_SCAN          │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           score           │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           stu_id          │
│           score           │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           EC: 4           │
└───────────────────────────┘

pipeline[1]

┌───────────────────────────┐
│      RESULT_COLLECTOR     │
└─────────────┬─────────────┘
┌─────────────┴─────────────┐
│         PROJECTION        │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│            name           │
│           score           │
└─────────────┬─────────────┘
┌─────────────┴─────────────┐
│         HASH_JOIN         │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           INNER           │
│        stu_id = id        │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           EC: 4           │
│          Cost: 4          │
└───────────────────────────┘

children MetaPipeline

  • 上面child MetaPipeline

{pipelines[1], children[0]}

pipeline[0]

┌───────────────────────────┐
│         HASH_JOIN         │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           INNER           │
│        stu_id = id        │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           EC: 4           │
│          Cost: 4          │
└─────────────┬─────────────┘                             
┌─────────────┴─────────────┐
│         SEQ_SCAN          │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│          student          │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│             id            │
│            name           │
│   ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─   │
│           EC: 3           │
└───────────────────────────┘

5.汇总

最后会从root_pipeline(这个是metapipeline)开始,递归所有metapipeline的pipelines数组,汇总得到打平后的pipelines。

root_pipeline->GetPipelines(pipelines, true);

最终得到:

(lldb) p pipelines 
(duckdb::vector<std::shared_ptr<duckdb::Pipeline>, true>) $150 = {
  std::__1::vector<std::__1::shared_ptr<duckdb::Pipeline>, std::__1::allocator<std::__1::shared_ptr<duckdb::Pipeline> > > = size=4 {
    [0] = std::__1::shared_ptr<duckdb::Pipeline>::element_type @ 0x00006110001bf5d8 strong=3 weak=3 {
      __ptr_ = 0x00006110001bf5d8
    }
    [1] = std::__1::shared_ptr<duckdb::Pipeline>::element_type @ 0x00006110001bf858 strong=2 weak=4 {
      __ptr_ = 0x00006110001bf858
    }
    [2] = std::__1::shared_ptr<duckdb::Pipeline>::element_type @ 0x00006110001bfc18 strong=2 weak=2 {
      __ptr_ = 0x00006110001bfc18
    }
    [3] = std::__1::shared_ptr<duckdb::Pipeline>::element_type @ 0x00006110001bfad8 strong=2 weak=3 {
      __ptr_ = 0x00006110001bfad8
    }
  }
}

好了,相信通过本节的分析,你会对Pipeline与MetaPipeline有一个深刻的理解,本节完~

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

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

相关文章

用 ChatGPT 重构工作流程

如果你第一次听说 ChatGPT&#xff0c;那你要反思一下自己的信息获取渠道是不是出了问题&#xff0c;作为 AI 时代最强代表&#xff0c;你一定要亲自去体验一下&#xff0c;而不是道听途说。 公司需要降本增效&#xff0c;个体也一样&#xff0c;在工作中畅快应用 AI 来帮助自己…

chatgpt赋能python:Python断点调试指南:让调试更高效

Python断点调试指南&#xff1a;让调试更高效 在Python编程中&#xff0c;调试是一个必不可少的环节。当我们面临代码出现错误或程序不按预期运行时&#xff0c;如何快速找到问题&#xff0c;解决它们呢&#xff1f;这时候断点调试就发挥了重要的作用。本篇文章主要介绍Python…

NI VeriStand

概览 本文简要概述了NI VeriStand以及如何创建实时测试应用程序&#xff0c;并介绍如何结合NI LabVIEW等不同软件环境采用不同的方式创建额外的功能&#xff0c;如模型导入、FPGA功能自定义以及将NI LabVIEW Run-Time控件添加为工作区对象等。 内容 什么是NI VeriStand什么是…

Vicuna本地部署的实战方案

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

全志V3S嵌入式驱动开发(屏幕花屏问题的解决)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 之前我们测试过lcd屏幕驱动以及触控驱动时&#xff0c;当时确实是成功的。但是由于这个屏幕是直接从旗舰店买的&#xff0c;价格略贵&#xff0c;有…

vue3+vite+element-plus创建项目,修改主题色

element-plus按需引入&#xff0c;修改项目的主题色 根据官方文档安装依赖 ​npm install -D unplugin-vue-components unplugin-auto-import vite.config.js配置 // vite.config.ts import { defineConfig } from vite import AutoImport from unplugin-auto-import/vite …

Flutter - 命令行工具源码调试环境搭建

文章目录 前言开发环境环境搭建运行测试调试测试最后 前言 开发Flutter项目时难免会遇到各种问题&#xff0c;源码调试对于问题的解决不可或缺。 对于Flutter框架项目的源码调试&#xff0c;如果是Flutter核心库调试&#xff0c;只需要创建一个Flutter项目并在项目中使用需要…

中级前端笔试面试题总结

typeof null 的结果是什么&#xff0c;为什么&#xff1f; typeof null 的结果是Object。 在 JavaScript 第一个版本中&#xff0c;所有值都存储在 32 位的单元中&#xff0c;每个单元包含一个小的 类型标签(1-3 bits) 以及当前要存储值的真实数据。类型标签存储在每个单元的…

AI大模型落地不远了!首个全量化Vision Transformer的方法FQ-ViT(附源代码)

点击蓝字 关注我们 关注并星标 从此不迷路 计算机视觉研究院 公众号ID&#xff5c;计算机视觉研究院 学习群&#xff5c;扫码在主页获取加入方式 论文地址&#xff1a;https://arxiv.org/pdf/2111.13824.pdf 项目代码&#xff1a;https://github.com/megvii-research/FQ-ViT 计…

自研算法提升文本图像篡改检测精度,抖音技术团队获 ICDAR2023 分类赛道冠军...

动手点关注 干货不迷路 近日&#xff0c;抖音 CV 技术团队在 ICDAR 2023 的“Detecting Tampered Text in Images”比赛中&#xff0c;利用自研的“CAS”算法从 1267 个参赛队伍中脱颖而出&#xff0c;获得分类赛道的第一名。 ICDAR&#xff08;International Conference on Do…

开源反分裂与数字大同世界

这是一篇报告的读后感&#xff0c;这篇报告是由Linux基金会研究部门发布的&#xff0c;名为《助力全球协作——开源代码的领导者如何面对分裂的挑战》。 这份报告的主要内容包括&#xff1a; - 开源代码开发中的分裂及其利弊- 开源的国际化&#xff0c;以及项目领导者如何克服参…

[Halcon3D] 3D鞋点胶的点云边界提取

&#x1f4e2;博客主页&#xff1a;https://loewen.blog.csdn.net&#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;本文由 丶布布原创&#xff0c;首发于 CSDN&#xff0c;转载注明出处&#x1f649;&#x1f4e2;现…

Springboot整合Camunda工作流引擎实现审批流程实例

环境&#xff1a;Spingboot2.6.14 camunda-spring-boot-starter7.18.0 环境配置 依赖配置 <camunda.version>7.18.0</camunda.version> <dependency><groupId>org.camunda.bpm.springboot</groupId><artifactId>camunda-bpm-spring-boo…

OpenCV 项目开发实战--用 (C++ / Python)实现伪着色

文末附相关测试代码下载链接 在本教程中,我们将首先展示一种使用 OpenCV 的预定义颜色图对灰度图像进行伪彩色/伪彩色 的简单方法。如果您更愿意使用自己的颜色图,我们还将展示一种定义自定义颜色图的方法。 行星和太空中其他物体的灰度图像通常是伪彩色的,以显示细…

基于在线光度校准的混合稀疏单目视觉里程计

文章&#xff1a;Hybrid sparse monocular visual odometry with online photometric calibration 作者&#xff1a;Dongting Luo, Yan Zhuang and Sen Wang 编辑&#xff1a;点云PCL 代码&#xff1a;https://github.com/luodongting/HSO.git 欢迎各位加入知识星球&#xff0c…

小破站上线了!

作者 | 磊哥 来源 | Java中文社群&#xff08;ID&#xff1a;javacn666&#xff09; 转载请联系授权&#xff08;微信ID&#xff1a;GG_Stone&#xff09; 为了给大家更好的阅读体验&#xff0c;磊哥搞了一个专业的技术网站&#xff0c;整理并总结了 Java 中比较经典的文章&…

管理类联考——英语——知识篇——作文题材说明

小作文题材 一、题材&#xff1a;小作文的内容&#xff08;写什么&#xff09; 1.疫情相关的主题&#xff1a;&#xff08;以信件方式出题可能性60%&#xff0c;对生活影响大&#xff09; (1)停学、复学&#xff1a;线上或线下停学和复学( go back to class/ school )&#x…

绕过激活锁 ,拯救一台旧手机iphone

一台旧的iphone忘了apple id账号和密码了&#xff0c;导致锁住了 某宝上解锁要花50&#xff0c; 不是舍不得花钱&#xff0c;作为一个搞技术的&#xff0c;实在觉得花钱有点丢人 经过一番探索 最终确定了有用的流程 并贴出来 亲测可用 最终实现了趟再床上就可以打卡 1、 刷机 …

拯救者2022款 y9000k 安装ubuntu20.04 休眠后无法唤醒(成功解决)

拯救者2022款 y9000k 安装ubuntu20.04 休眠后无法唤醒 一.建议在安装新的 NVIDIA 显卡驱动之前卸载原有的驱动程序。这样可以确保新驱动程序的安装过程更加干净和稳定。以下是卸载原有驱动的步骤&#xff1a;1.进入命令行界面&#xff1a; 按下 Ctrl Alt F3 进入文本模式的命…

IDEA Build Artifacts 功能使用总结

文章目录 创建Artifact步骤Build Artifact步骤 打开IDEA&#xff0c;在没有创建Artifact时&#xff0c;菜单"Build -> Build Artifacts…“是灰色的&#xff0c;不可用状态。 所以&#xff0c;第一步是进入"File -> Project Structure…”&#xff0c;创建Arti…