Postgresql源码(128)深入分析JIT中的函数内联llvm_inline

相关
《Postgresql源码(127)投影ExecProject的表达式执行分析》
《LLVM的ThinLTO编译优化技术在Postgresql中的应用》
《LLVM(5)ORC实例分析》

1 JIT优化效果

create table t1(i int primary key, j int, k int);
insert into t1 select i, i % 10, i % 100 from generate_series(1,10000000) t(i);

set jit_optimize_above_cost to -1;
set jit_above_cost to 0;
set jit_inline_above_cost to 0;
set jit_tuple_deforming to off;
-- jit_inline_above_cost depend on jit_expressions
set jit_expressions to on;

explain analyze select abs(k),abs(k),abs(k),abs(k),abs(k),exp(k),exp(k),exp(k),exp(k),exp(k) from t1;

关JIT:9.3秒

                                                    QUERY PLAN                                                     
-------------------------------------------------------------------------------------------------------------------
 Seq Scan on t1  (cost=0.00..529063.31 rows=10000175 width=60) (actual time=0.039..8863.584 rows=10000000 loops=1)
 Planning Time: 0.081 ms
 Execution Time: 9343.666 ms

开JIT:5.9秒

                                                     QUERY PLAN                                                     
--------------------------------------------------------------------------------------------------------------------
 Seq Scan on t1  (cost=0.00..529063.31 rows=10000175 width=60) (actual time=12.100..5460.438 rows=10000000 loops=1)
 Planning Time: 0.081 ms
 JIT:
   Functions: 1
   Options: Inlining true, Optimization false, Expressions true, Deforming false
   Timing: Generation 0.389 ms, Inlining 5.567 ms, Optimization 1.188 ms, Emission 5.301 ms, Total 12.445 ms
 Execution Time: 5921.711 ms
(7 rows)

2 PERF STAT观测区别

JIT执行5次:29秒

              4.32 msec task-clock                       #    0.000 CPUs utilized             
                13      context-switches                 #    3.008 K/sec                     
                 2      cpu-migrations                   #  462.821 /sec                      
               282      page-faults                      #   65.258 K/sec                     
        10,763,253      cycles                           #    2.491 GHz                         (31.94%)
         9,573,998      instructions                     #    0.89  insn per cycle              (55.08%)
         1,833,970      branches                         #  424.400 M/sec                       (75.93%)
            54,410      branch-misses                    #    2.97% of all branches             (66.02%)
         3,067,449      L1-dcache-loads                  #  709.840 M/sec                       (24.07%)
            69,671      L1-dcache-load-misses            #    2.27% of all L1-dcache accesses   (33.98%)
            54,139      LLC-loads                        #   12.528 M/sec                       (17.49%)
            22,209      LLC-load-misses                  #   41.02% of all L1-icache accesses   (17.49%)

      29.740983598 seconds time elapsed

       0.001851000 seconds user
       0.002641000 seconds sys

NO JIT执行5次:46秒

 Performance counter stats for '/data/pg16/pghome/bin/psql -p 1101 -h127.0.0.1 -U pg16 postgres -f nj.sql':

              3.92 msec task-clock                       #    0.000 CPUs utilized             
                13      context-switches                 #    3.319 K/sec                     
                 2      cpu-migrations                   #  510.542 /sec                      
               282      page-faults                      #   71.986 K/sec                     
         9,896,281      cycles                           #    2.526 GHz                         (35.98%)
         8,902,749      instructions                     #    0.90  insn per cycle              (61.50%)
         1,719,940      branches                         #  439.050 M/sec                       (79.78%)
            51,247      branch-misses                    #    2.98% of all branches             (70.28%)
         1,976,835      L1-dcache-loads                  #  504.628 M/sec                       (20.22%)
            75,055      L1-dcache-load-misses            #    3.80% of all L1-dcache accesses   (29.72%)
            52,070      LLC-loads                        #   13.292 M/sec                       (17.30%)
            29,130      LLC-load-misses                  #   55.94% of all L1-icache accesses   (17.30%)

      46.686925078 seconds time elapsed

       0.002121000 seconds user
       0.001970000 seconds sys

对比
在这里插入图片描述
指标含义

事件no jitjit分析
task-clock3.924.32执行程序的CPU时间
context-switches1313上下文切换次数,相同
cpu-migrations22CPU迁移次数,相同
page-faults282282内存缺页,相同
cycles9,896,28110,763,253CPU花费的周期
instructions8,902,749(0.90 insn per cycle)9,573,998(0.89 insn per cycle这段时间内完成的CPU指令,每个指令都包含多个步骤,每个步骤由CPU的一个叫做功能单元组件处理,每个步骤至少花费一个cycle周期。insn per cycle表示一个CPU周期内完成几个指令,越高越好,这里没有太大差距。
branches1,719,9401,833,970分支预测的次数
branch-misses51,24754,410分支预测失败的次数
L1-dcache-loads1,976,8353,067,449一级数据缓存读取次数,差距非常大
L1-dcache-load-misses75,05569,671一级数据缓存读取失败次数
LLC-loads52,07054,139last level cache读取次数
LLC-load-misses29,13022,209last level cache读取失败次数,最后一级缓存未命中
总执行时间46.68692507829.740983598

差距的原因比较多,内联和优化会有较大的影响:

  • 内存访问模式的改变:LLVM优化可能改变了数据的访问模式,使得数据访问更加局部化。这可以提高缓存的命中率,因为数据更有可能在缓存中被找到。
  • 循环优化:LLVM可能进行了循环展开(loop unrolling)或循环融合(loop fusion)等优化,这些优化可以减少循环开销和提高迭代中数据的重用。
  • 数据预取:LLVM可能插入了数据预取指令,这些指令可以在数据被访问之前就将其加载到缓存中,从而减少缓存未命中。
  • 数据对齐和填充:LLVM可能改变了数据结构的对齐方式,或者添加了填充,以减少缓存行冲突和提高缓存利用率。
  • 死码消除和代码简化:LLVM的优化可能移除了不必要的代码和变量,这样可以减少对内存的需求,从而减少L1缓存的加载操作。
  • 内联和函数优化:通过函数内联,LLVM可以减少函数调用的开销,并可能进一步优化局部变量的使用,这样也可能减少对L1缓存的访问。
  • 变量生命周期的管理:LLVM优化可能改变了变量的生命周期,使得变量在使用时更集中,这样可以提高缓存的命中率。

3 llvm_inline执行流程分析

上面给的用例函数的编译执行是在投影列中的(无JIT投影列执行可以参考这篇《Postgresql源码(127)投影ExecProject的表达式执行分析》)。

LLVM的执行计划,投影列的计算会进入LLVM堆栈:
在这里插入图片描述

static Datum
ExecRunCompiledExpr(ExprState *state, ExprContext *econtext, bool *isNull)
{
	CompiledExprState *cstate = state->evalfunc_private;
	ExprStateEvalFunc func;

	CheckExprStillValid(state, econtext);

	llvm_enter_fatal_on_oom();
	func = (ExprStateEvalFunc) llvm_get_function(cstate->context,
												 cstate->funcname);
	llvm_leave_fatal_on_oom();
	Assert(func);

	/* remove indirection via this function for future calls */
	state->evalfunc = func;

	return func(state, econtext, isNull);
}

llvm的总入口是llvm_get_function,llvm_get_function→llvm_compile_module→llvm_inline。

下面重点分析inline的过程:

static void
llvm_compile_module(LLVMJitContext *context)
{
	...
	if (context->base.flags & PGJIT_INLINE)
	{
		...
		llvm_inline(context->module);
		...
	}
	...

3.1 llvm_inline入参

llvm_inline(context->module);

(gdb) p	*context
$2 = {
  base = {
    flags = 13, 
    resowner = 0x144d320, 
    instr = {
      created_functions = 1, 
      generation_counter = {ticks = 315193}, 
      deform_counter = {ticks = 0}, 
      inlining_counter = {ticks = 0},
      optimization_counter = {ticks = 0}, 
      emission_counter = {ticks = 0}}}, 
  module_generation = 5, 
  llvm_context = 0x0, 
  module = 0x157cf80, 
  compiled = false, 
  counter = 1, 
  handles = 0x0}

module的含义:《LLVM(5)ORC实例分析》
在这里插入图片描述

3.2 module在哪里初始化?

llvm_mutable_module创建一个名称为pg的空module:
在这里插入图片描述

LLVMModuleRef
llvm_mutable_module(LLVMJitContext *context)
{
	llvm_assert_in_fatal_section();

	/*
	 * If there's no in-progress module, create a new one.
	 */
	if (!context->module)
	{
		context->compiled = false;
		context->module_generation = llvm_generation++;
		context->module = LLVMModuleCreateWithNameInContext("pg", llvm_context);
		LLVMSetTarget(context->module, llvm_triple);
		LLVMSetDataLayout(context->module, llvm_layout);
	}

	return context->module;
}

然后再llvm_compile_expr中构造表达式计算函数,加到module里面:
在这里插入图片描述

注意这里通过LLVMAddFunction会把表达式计算用到的函数都加进去(只加用到的)。

本例中:

explain analyze select abs(k),abs(k),abs(k),abs(k),abs(k),exp(k),exp(k),exp(k),exp(k),exp(k) from t1;

llvm_compile_expr函数中LLVMAddFunctionLLVMAddFunction位置
evalexpr_0_0llvm_compile_expr
slot_getsomeattrs_intllvm_compile_expr case EEOP_SCAN_FETCHSOME
int4absllvm_compile_expr case EEOP_FUNCEXPR_STRICT
llvm.lifetime.end.p0i8llvm_compile_expr case EEOP_FUNCEXPR_STRICT
i4todllvm_compile_expr case EEOP_FUNCEXPR_STRICT
dexpllvm_compile_expr case EEOP_FUNCEXPR_STRICT

只有上面5个函数被加到module了,所以后面inline只需要考虑这后四个个函数就好了,第一个是表达式计算入口。

3.3 llvm_inline

void
llvm_inline(LLVMModuleRef M)
{
	LLVMContextRef lc = LLVMGetModuleContext(M);
	// ? 下面有解释
	llvm::Module *mod = llvm::unwrap(M);
  • llvm::unwrap 是一个辅助函数,用于将 C 语言风格的接口转换为 C++ 风格的接口。M是一个来自 LLVM C API 的类型(LLVMModuleRef),这是一个指向 LLVM 模块的指针,但它被封装为一个不透明的指针类型以便在 C 环境中使用。llvm::unwrap 函数将这个不透明的指针转换为一个指向 llvm::Module 的指针,这样就可以在 C++ 代码中使用 LLVM 的 C++ API 来操作这个模块了。
  • (llvm::module 是 LLVM 中的一个类,它代表了一个完整的 LLVM IR模块,这个模块可以包含函数、全局变量、符号等。在 LLVM 的 C++ API 中可以直接使用 llvm::Module 类型的对象)
	
	std::unique_ptr<ImportMapTy> globalsToInline = llvm_build_inline_plan(lc, mod);
	if (!globalsToInline)
		return;
	llvm_execute_inline_plan(mod, globalsToInline.get());
}

llvm_inline→llvm_build_inline_plan

llvm_build_inline_plan函数返回一个ImportMapTy类型,

static std::unique_ptr<ImportMapTy>
llvm_build_inline_plan(LLVMContextRef lc, llvm::Module *mod)
{
	std::unique_ptr<ImportMapTy> globalsToInline(new ImportMapTy());
	FunctionInlineStates functionStates;
	InlineWorkList worklist;

	InlineSearchPath defaultSearchPath;
  • 注意这里的searchpath是什么,怎么来的请看这篇: 《LLVM的ThinLTO编译优化技术在Postgresql中的应用》
	add_module_to_inline_search_path(defaultSearchPath, "$libdir/postgres");
	...
	for (const llvm::Function &funcDecl : mod->functions())
	{
		InlineWorkListItem item = {};
		FunctionInlineState inlineState = {};

  • 只ADD了还没BUILD所以有的函数只有定义,例如evalexpr_0_0llvm.lifetime.end.p0i8
  • 其他函数会正常加入worklist,例如int4absdexp
		if (!funcDecl.isDeclaration())
			continue;
		...

		item.symbolName = funcDecl.getName();
		item.searchpath = defaultSearchPath;
		worklist.push_back(item);
		inlineState.costLimit = inline_initial_cost;
		inlineState.processed = false;
		inlineState.inlined = false;
		inlineState.allowReconsidering = false;
		functionStates[funcDecl.getName()] = inlineState;
	}
  • 本案例中会有四个函数加入worklist:slot_getsomeattrs_int、int4abs、i4tod、dexp
	while (!worklist.empty())
	{
		InlineWorkListItem item = worklist.pop_back_val();
		llvm::StringRef symbolName = item.symbolName;
		char *cmodname;
		char *cfuncname;
		FunctionInlineState &inlineState = functionStates[symbolName];
		llvm::GlobalValue::GUID funcGUID;

		llvm_split_symbol_name(symbolName.data(), &cmodname, &cfuncname);

		funcGUID = llvm::GlobalValue::getGUID(cfuncname);

		for (const auto &gvs : summaries_for_guid(item.searchpath, funcGUID))
		{
  • 从searchpath也就是postgresql.index.bc里面搜索funcGUID,找到modPath。
  • 例如modPath=postgres/utils/adt/float.bc
			const llvm::FunctionSummary *fs;
			llvm::StringRef modPath = gvs->modulePath();
			llvm::Module *defMod;
			llvm::Function *funcDef;

			fs = llvm::cast<llvm::FunctionSummary>(gvs);

			if ((int) fs->instCount() > inlineState.costLimit)
			{
				ilog(DEBUG1, "ineligibile to import %s due to early threshold: %u vs %u",
					 symbolName.data(), fs->instCount(), inlineState.costLimit);
				inlineState.allowReconsidering = true;
				continue;
			}
  • modPath是函数所在的bc文件路径。
  • 在load_module_cached中调用load_module,调用LLVMCreateMemoryBufferWithContentsOfFile、LLVMGetBitcodeModuleInContext2拿到module。
			defMod = load_module_cached(lc, modPath);
			if (defMod->materializeMetadata())
				elog(FATAL, "failed to materialize metadata");

			funcDef = defMod->getFunction(cfuncname);
  • 到这里,通过thinlto生成的index文件的指引,找到函数所在的bc文件,最后拿到了func的bitcode。
			...

			llvm::StringSet<> importVars;
			llvm::SmallPtrSet<const llvm::Function *, 8> visitedFunctions;
			int running_instcount = 0;

  • 拿到了函数的bitcode后确认是否能inline?
  • 能否inline是一系列规则、成本决定的,具体分析在这篇:《Postgresql中JIT函数能否inline的依据function_inlinable》
    在这里插入图片描述

在这里插入图片描述

			if (function_inlinable(*funcDef,
								   inlineState.costLimit,
								   functionStates,
								   worklist,
								   item.searchpath,
								   visitedFunctions,
								   running_instcount,
								   importVars))
			{
  • 整体指令数不能超过150个,超过就不在inline了,Inline太多会造成代码膨胀。
				if (running_instcount > inlineState.costLimit)
				{
					ilog(DEBUG1, "skipping inlining of %s due to late threshold %d vs %d",
						 symbolName.data(), running_instcount, inlineState.costLimit);
					inlineState.allowReconsidering = true;
					continue;
				}

				ilog(DEBUG1, "inline top function %s total_instcount: %d, partial: %d",
					 symbolName.data(), running_instcount, fs->instCount());

  • 这里把当前函数,例如dexp放到数组中。
  • function_inlinable中没放吗?因为function_inlinable只把dexp调用到的函数放进去了。
  • 把全部需要inline的函数名、全局变量名记录到modGlobalsToInline中。
				importVars.insert(symbolName);

				{
					llvm::StringSet<> &modGlobalsToInline = (*globalsToInline)[modPath];
					for (auto& importVar : importVars)
						modGlobalsToInline.insert(importVar.first());
				}
  • 标记已经Inline
				inlineState.inlined = true;

				break;
			}
			else
			{
				ilog(DEBUG1, "had to skip inlining %s",
					 symbolName.data());
			}
		}

  • 更新FunctionInlineState。
  • 注意:
    • functionStates数组存放了所有inline的函数的信息。
    • globalsToInline是map结构{modPath, StringSet},根据"postgres/utils/adt/float.bc"找到StringSet,在StringSet中保存了dexp、dexp函数调用到的其他函数、dexp和调用到函数所有使用到的全局变量。
		inlineState.processed = true;
	}

	return globalsToInline;
}

3.4 llvm_inline→llvm_execute_inline_plan

遍历上述结果集中的函数,配置AvailableExternallyLinkage标记。

static void
llvm_execute_inline_plan(llvm::Module *mod, ImportMapTy *globalsToInline)
{
	...
	for (const auto& toInline : *globalsToInline)
	{
		const llvm::StringRef& modPath = toInline.first();
		const llvm::StringSet<>& modGlobalsToInline = toInline.second;

		std::unique_ptr<llvm::Module> importMod(std::move((*module_cache)[modPath]));
		module_cache->erase(modPath);

		for (auto &glob: modGlobalsToInline)
		{
			llvm::StringRef SymbolName = glob.first();

			llvm::GlobalValue *valueToImport = importMod->getNamedValue(funcname);

			if (llvm::isa<llvm::Function>(valueToImport))
			{
				llvm::Function *F = llvm::dyn_cast<llvm::Function>(valueToImport);
				typedef llvm::GlobalValue::LinkageTypes LinkageTypes;

				...

				if (valueToImport->hasExternalLinkage())
				{
					valueToImport->setLinkage(LinkageTypes::AvailableExternallyLinkage);
				}
			}
		}
	}
}
  • 在LLVM中,有几种编译连接类型,默认会使用ExternalLinkage,表示函数在不同的编译单元之间是可见的,可以被其他单元引用。
  • 如果主动配置为AvailableExternallyLinkage,直观的理解是 把函数转换成一个全局可用的函数,定义只编译一次, 其他模块不会重复编译当前函数。

GlobalValue.h

class GlobalValue : public Constant {
public:
  /// An enumeration for the kinds of linkage for global values.
  enum LinkageTypes {
    ExternalLinkage = 0,///< Externally visible function
    AvailableExternallyLinkage, ///< Available for inspection, not emission.
    LinkOnceAnyLinkage, ///< Keep one copy of function when linking (inline)
    LinkOnceODRLinkage, ///< Same, but only replaced by something equivalent.
    WeakAnyLinkage,     ///< Keep one copy of named function when linking (weak)
    WeakODRLinkage,     ///< Same, but only replaced by something equivalent.
    AppendingLinkage,   ///< Special purpose, only applies to global arrays
    InternalLinkage,    ///< Rename collisions when linking (static functions).
    PrivateLinkage,     ///< Like Internal, but omit from symbol table.
    ExternalWeakLinkage,///< ExternalWeak linkage description.
    CommonLinkage       ///< Tentative definitions.
  };

在这里插入图片描述

当函数被标记为AvailableExternallyLinkage时,LLVM优化器有可能会内联这些函数。但不是一定会发生,内联决策是llvm内联启发式算法做出的,会考虑很多因素:函数的大小、复杂性、调用频率、调用上下文等等。如果llvm决定内联一个函数,它会将函数的代码直接插入到每个调用点。

从module里面读取一下inline后的IR代码,发现函数已经有了available_externally标记。
在这里插入图片描述

4 内联后的效果

4.1 llvm决定不做内联

例如dexp函数:
在这里插入图片描述
优化后,还是正常调用:
在这里插入图片描述

4.2 llvm决定内联

在这里插入图片描述

原来调用的位置变成什么了?
在这里插入图片描述

(备忘)clang编译PG源码的方式

clang编译生成.bc文件

cd src/backend/utils/adt

clang \
-Wno-ignored-attributes -fno-strict-aliasing \
-fwrapv -fexcess-precision=standard \
-Xclang -no-opaque-pointers -Wno-unused-command-line-argument \
-Wno-compound-token-split-by-macro -O2 \
-I. -I. -I../../../../src/include  \
-D_GNU_SOURCE -I/usr/include/libxml2  -flto=thin -emit-llvm \
-c -o int8.bc int8.c

生成可读的ll文件:

llvm-dis int8.bc

其他编译相关的在这篇: 《LLVM的ThinLTO编译优化技术在Postgresql中的应用》

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式开发三:STM32初体验

本节主要向大家介绍如何开发过程中的基本操作&#xff0c;如编译、串口下载、仿真器下载、仿真调试程序&#xff0c;体验一下 STM32 的开发流程&#xff0c;并介绍 MDK5 的一些使用技巧&#xff0c;通过本节的学习&#xff0c;将对 STM32 的开发流程和 MDK5 使用有个大概了解&a…

Windows安全加固-账号与口令管理

在当今日益增长的网络安全威胁中&#xff0c;Windows系统的安全加固显得尤为重要。其中&#xff0c;账号与口令管理作为系统安全的第一道防线&#xff0c;其重要性不言而喻。本文将深入探讨Windows安全加固中的账号与口令管理策略&#xff0c;以确保系统的安全性和稳定性。 账…

认识大模型提示词

一、写作助理 &#x1f4a5;最常使用的 prompt&#xff0c;用于优化文本的语法、清晰度和简洁度&#xff0c;提高可读性。 输入&#xff1a;作为一名写作改进助理&#xff0c;你的任务是改进所提供文本的拼写、语法、清晰、简洁和整体可读性&#xff0c;同时分解长句&#xff…

运行SpringBoot项目失败?异常显示Can‘t load IA 32-bit .dll on a AMD 64-bit platform,让我来看看~

原因是&#xff0c;我放入jdk的bin文件夹下的tcnative-1.dll文件是32位的&#xff0c;那么肯定是无法在AMD 64位平台上加载IA 32位.dll。但是网站上给出的都是32位呀&#xff0c;没有64位怎么办&#xff1a; 其实当我们把“tomcat-native-1.2.34-openssl-1.1.1o-win32-bin.zip”…

项目管理-项目资源管理2/2

项目管理&#xff1a;每天进步一点点~ 活到老&#xff0c;学到老 ヾ(◍∇◍)&#xff89;&#xff9e; 何时学习都不晚&#xff0c;加油 资源管理&#xff1a;6个过程“硅谷火箭管控” ①规划资源管理&#xff1a; 写计划 ②估算活动资源&#xff1a;估算团队资源&…

数据结构学习——二叉树

1. 树概念及结构 1.1 树的概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下的。 有一个特殊的结点&…

C语言:指针(1)

1. 内存和地址 内存划分为⼀个个的内存单元&#xff0c;每个内存单元的⼤⼩取1个字节。 计算机中常⻅的单位&#xff08;补充&#xff09;&#xff1a; ⼀个⽐特位可以存储⼀个2进制的位1或者0 C语⾔中给地址起了新的名字叫&#xff1a;指针。 内存单元的编号地址指针。 1.…

实战BACnet/IP标准通信网关在楼宇自动化中的应用

智慧楼宇建设实现不同设备间的互联互通是一项巨大挑战&#xff0c;尤其是在那些历史悠久的建筑中&#xff0c;新旧系统并存的情况尤为普遍。某大型商业综合体就面临着这样的困境&#xff1a;老旧的暖通空调系统采用Modbus RTU协议&#xff0c;而新部署的能源管理系统却要求BACn…

企业车辆管理系统参考论文(论文 + 源码)

【免费】关于企业车辆管理系统.zip资源-CSDN文库https://download.csdn.net/download/JW_559/89282550 企业车辆管理系统 摘 要 随着经济的日益增长,车辆作为最重要的交通工具,在企事业单位中得以普及,单位的车辆数目已经远远不止简单的几辆,与此同时就产生了车辆资源的合理…

react状态管理之state

第三章 - 状态管理 随着你的应用不断变大&#xff0c;更有意识的去关注应用状态如何组织&#xff0c;以及数据如何在组件之间流动会对你很有帮助。冗余或重复的状态往往是缺陷的根源。在本节中&#xff0c;你将学习如何组织好状态&#xff0c;如何保持状态更新逻辑的可维护性&…

Java特性之设计模式【代理模式】

一、代理模式 概述 在代理模式&#xff08;Proxy Pattern&#xff09;中&#xff0c;一个类代表另一个类的功能。这种类型的设计模式属于结构型模式 在代理模式中&#xff0c;我们创建具有现有对象的对象&#xff0c;以便向外界提供功能接口 主要解决&#xff1a; 在直接访问…

深入了解 NumPy:深度学习中的数学运算利器

文章目录 1. 导入NumPy2. 创建NumPy数组3. 数组的算术运算4. N维数组4.1 创建和操作多维数组4.2 高维数组 5. NumPy的广播功能5.1 基本广播示例5.2 更复杂的广播示例 6. 访问数组元素6.1 基于索引的访问6.2 遍历数组6.3 基于条件的访问6.4 高级索引6.5 性能考虑 在深度学习和数…

论文阅读_使用有向无环图实现流程工程_AgentKit

英文名称: AgentKit: Flow Engineering with Graphs, not Coding 中文名称: AgentKit&#xff1a;使用图而非编码进行流程工程 链接: https://arxiv.org/pdf/2404.11483.pdf 代码: https://github.com/holmeswww/AgentKit 作者: Yue Wu, Yewen Fan, So Yeon Min, Shrimai Prabh…

射频无源器件之耦合器

一. 耦合器的作用 在射频电路中,射频耦合器将一路微波功率按比例分成几路,用于检测或监测信号,如功率测量和波检测,还可改变信号的幅度、相位等特性,以满足不同的通信需求。根据输入与耦合端的功率差,常被分为5dB、6dB、10dB等耦合器。射频耦合器的类型主要包括定向耦合…

区块链 | NFT 相关论文:Preventing Content Cloning in NFT Collections(三)

&#x1f436;原文&#xff1a; Preventing Content Cloning in NFT Collections &#x1f436;写在前面&#xff1a; 这是一篇 2023 年的 CCF-C 类&#xff0c;本博客只记录其中提出的方法。 F C o l l N F T \mathbf{F_{CollNFT}} FCollNFT​ and Blockchains with Native S…

命名空间、C++的输入输出、缺省参数(默认参数)、函数重载

&#x1f436;博主主页&#xff1a;ᰔᩚ. 一怀明月ꦿ ❤️‍&#x1f525;专栏系列&#xff1a;线性代数&#xff0c;C初学者入门训练&#xff0c;题解C&#xff0c;C的使用文章&#xff0c;「初学」C&#xff0c;linux &#x1f525;座右铭&#xff1a;“不要等到什么都没有了…

基于Springboot的校园健康驿站管理系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的校园健康驿站管理系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系…

TMS320F2812DSP最小系统原理图及PCB文件

目录 1、原理图 2、PCB 资料下载地址&#xff1a;TMS320F2812DSP最小系统原理图及PCB文件 1、原理图 2、PCB

视频转GIF动图:一键批量操作技巧,轻松实现动态图像转换

在数字媒体时代&#xff0c;GIF动图因其小巧、循环播放的特性&#xff0c;在网络传播中占据了重要地位。而将视频转换为GIF动图&#xff0c;不仅可以快速捕捉视频中的精彩瞬间&#xff0c;还能为社交媒体、网站和博客等内容创作增添生动与活力。本文将介绍如何通过一键批量操作…

三、VGA接口驱动与图像显示动态移动

文章目录 一、参数介绍二、彩条显示2.1 模块系统架构框图2.2 行、场同步波形:2.3 代码三、VGA 图像显示动态移动3.1波形设计3.2代码 一、参数介绍 对于普通的 VGA 显示器&#xff0c;共有 5 个信号&#xff1a;R、G、B 三基色&#xff1b;HS&#xff08;行同步信号&#xff09…
最新文章