DeepSeek总结的AI 如何改变 JIT 编译器的成本效益
原文https://malisper.me/how-ai-changes-the-economics-of-jit-compilers/AI 如何改变 JIT 编译器的成本效益从历史上看JIT 编译一直是一门黑魔法。要编写一个快速的 JIT 编译器你需要知道如何编写汇编代码。一个典型的例子是当今没有一个生产就绪的数据库拥有自己的 JIT 编译器。它们要么使用 LLVM要么生成 C/C 代码。这两种方案都存在编译时间长的问题这限制了它们的适用性。现在借助 AI 的使用通过直接生成汇编代码来编写一个编译速度快的 JIT 编译器比以往任何时候都更容易。这也是新数据库能够在旧数据库基础上进行改进的领域之一。在构建 pgrust 时我最初认为实现一个 JIT 编译器会非常困难。最后我发现由于 AI 的帮助它比我预期的要容易得多这也是 pgrust 速度如此之快的原因之一。在这篇文章中我将带你了解如何构建你自己的 JIT 编译器。我们将构建一个使用 JIT 编译的简单正则表达式引擎作为例子。为什么使用 JIT 编译JIT 编译是在运行时“即时”生成编译代码的做法。如果做得好它可以带来巨大的性能提升通常是 2-5 倍有时甚至更多。JIT 编译的主要用例是当你在运行时获得的信息会极大地改变程序行为时。这在编程语言解释器中尤其常见它们在运行时接收要执行的代码。JIT 编译器在编程语言之外的领域也很有用例如解析数据。有时你在运行时才知道要解析的数据的模式schemaJIT 可以帮助解决这个问题。首先让我们实现一个玩具正则表达式引擎。为了保持简单我们只支持两个特性字面量字符串和重复即正则表达式*。我们还将跳过解析器将正则表达式表示为已经解析好的 Rust 结构。这意味着我们将能够支持如下字符串applesb(an)*但不支持选择、后向查找或类似功能。在代码中这相当简单。我们将有 3 种类型的节点一个字面量字符串节点、一个重复节点和一个连接节点它是两个节点的组合。最终看起来像这样enumNode{Literal(staticstr),Concatenation(BoxNode,BoxNode),Repetition(BoxNode),}fnliteral(text:staticstr)-Node{Node::Literal(text)}fnconcatenation(left:Node,right:Node)-Node{Node::Concatenation(Box::new(left),Box::new(right))}fnrepetition(body:Node)-Node{Node::Repetition(Box::new(body))}为我们的正则表达式引擎编写一个解释器也很直接fnmatch_node(node:Node,input:[u8],pos:usize,next:dynFn(usize)-bool)-bool{matchnode{Node::Literal(text){letliteraltext.as_bytes();input[pos..].starts_with(literal)next(posliteral.len())}Node::Concatenation(left,right){match_node(left,input,pos,|left_end|{match_node(right,input,left_end,next)})}Node::Repetition(body){match_node(body,input,pos,|body_end|{match_node(node,input,body_end,next)})||next(pos)}}}fninterp_match(regex:Node,input:str)-bool{letbytesinput.as_bytes();match_node(regex,bytes,0,|pos|posbytes.len())}这个正则表达式引擎非常简单。它不到 20 行代码但让我们看看它在性能方面的表现。作为比较我们将此代码与专门为正则表达式实现的手写代码进行对比。我们的示例将使用正则表达式b(an)*。手写代码最终看起来像fnhandwritten_b_an_star(input:str)-bool{letbytesinput.as_bytes();letmutpos0;ifposbytes.len()||bytes[pos]!bb{returnfalse;}pos1;whileposbytes.len(){ifbytes[pos]!ba{returnfalse;}pos1;ifposbytes.len()||bytes[pos]!bn{returnfalse;}pos1;}true}还有一些方法可以优化这段代码并使其更快但出于我们的目的它作为一个很好的比较基准当我对几个例子进行基准测试时我得到手写版本比解释器快 10-20 倍。显然有很大的改进空间。现在让我们看看如何使用 JIT 编译来获得一个性能与手写版本相当的正则表达式引擎。如何进行 JIT 编译JIT 编译代码有两个步骤。首先为你想要运行的代码生成汇编代码。一旦你有了代码然后将汇编代码打包成一个函数你可以像调用程序中的任何其他代码一样调用它。为了生成汇编代码我们将使用一种称为“复制和修补”copy-and-patch的方法的变体。其思想是我们有针对不同操作的汇编模板系列。这些模板被称为“模版”stencils。当我们想要 JIT 编译一个操作时我们取关联的模版并根据操作的具体细节进行小的调整。非常类似于填充真正的模版。通过将几个这样的填充模版串在一起我们可以在运行时构建一个程序其性能与手写版本相似。以下是我们将采取的路径首先我们将查看我们想要为b(an)*生成的 ARM64 代码。然后我们将重复的指令序列转化为可重用的模版编写一个发射器emitter从正则表达式 AST 填充并组合这些模版最后将生成的指令复制到可执行内存中以便 Rust 可以像调用普通函数一样调用它们。为了让你了解这是如何工作的最简单的方法是从生成的代码开始然后反向推导到 JIT 编译器本身。再次强调我们正在处理正则表达式b(an)*。为了说明一些设计决策我们将使用一个栈进行回溯backtracking。栈将跟踪如果我们在正则表达式中遇到死路时应该转到的状态。我们匹配的字符串将以空字节NUL byte结尾。这意味着如果我们到达字符串的末尾我们的任何字符比较都会自动失败。这意味着我们在任何时候都不需要进行长度比较。对于程序状态我们将使用以下寄存器x0– 字符串中的当前位置和返回值x1– 用于回溯的栈顶x2– 用于回溯的栈底这是确定栈是否为空所必需的x9– 用作临时变量对于程序的输入我们将传入x0– 指向字符串开头的指针x1– 指向我们将用于栈的内存的指针生成的 ARM64 代码现在我们已经处理好了这些让我们逐部分地浏览生成的汇编代码。这专门针对 macOS 上的 ARM64。首先我们有 prologue它初始化程序。它所做的只是通过将栈顶和栈底设置为传入的值来初始化栈0: aa0103e2 mov x2, x1接下来我们有检查字符b的代码。如果它看到一个不是b的字符我们跳转到一个处理回退逻辑的代码块。否则我们推进字符串中的位置; CHAR b 4: 39400009 ldrb w9, [x0] ; 加载当前输入字节 8: 7101893f cmp w9, #0x62 ; 是 b 吗 c: 54000281 b.ne 0x5c ; 不是 - 回退块 10: 91000400 add x0, x0, #1 ; 是 - 推进输入接下来我们有重复(an)*。对于重复我们需要进行回溯。如果我们在这里回溯这意味着我们立即跳转到循环结束。这意味着我们需要将循环后的指令地址和我们在字符串中的位置都存储在栈上。14: d2800989 movz x9, #0x004c ; 构建恢复地址 18: f2a00009 movk x9, #0x0000, lsl #16 ; 0x1_0000_004c 1c: f2c00029 movk x9, #0x0001, lsl #32 ; (循环出口) 20: f2e00009 movk x9, #0x0000, lsl #48 ; 24: a8810029 stp x9, x0, [x1], #16 ; 将 (exit, pos) 压入栈有了这些我们现在可以执行循环体了。这将检查字符a和n如果看到它们就回到循环顶部但是是在一个新的字符串位置。; CHAR a 28: 39400009 ldrb w9, [x0] 2c: 7101853f cmp w9, #0x61 ; a? 30: 54000161 b.ne 0x5c ; 不是 - 回退块 34: 91000400 add x0, x0, #1 ; CHAR n 38: 39400009 ldrb w9, [x0] 3c: 7101b93f cmp w9, #0x6e ; n? 40: 540000e1 b.ne 0x5c ; 不是 - 回退块 44: 91000400 add x0, x0, #1 ; JMP 48: 17fffff3 b 0x14 ; 回到循环顶部现在我们过了循环。这是我们回溯时将跳转到的位置。一旦我们完成了重复就到了正则表达式的末尾。我们现在要做的只是检查我们是否在字符串的末尾。如果我们在末尾我们返回 1 表示成功。如果我们不在这意味着正则表达式匹配失败我们需要运行失败逻辑进行回退。4c: 39400009 ldrb w9, [x0] 50: 35000069 cbnz w9, 0x5c ; 不是 NUL - 回退块 54: d2800020 mov x0, #1 ; 成功 58: d65f03c0 ret最后我们有回退逻辑。这检查栈是否为空。如果是我们返回 0。如果不是我们从栈中弹出回退地址和回退字符串位置然后跳转到回退地址。5c: eb02003f cmp x1, x2 ; 还有帧吗 60: 54000060 b.eq 0x6c ; 没有 - 放弃 64: a9ff0029 ldp x9, x0, [x1, #-16]! ; 弹出 (resume, pos) 68: d61f0120 br x9 ; 跳转到那里 6c: d2800000 mov x0, #0 ; 不匹配 70: d65f03c0 ret构建模版现在你已经有机会看到编译后的代码你应该开始理解复制和修补编译器是如何工作的。我们有共同的指令集它们之间只有微小的差异。对于这些函数块中的每一个我们可以编写一个函数来生成相应的代码。每个函数将接收用于修改代码的值。例如stencil_char的参数之一将是正则表达式中要比较的字符。我们将该字符直接插入到机器码中。Prologue 很简单因为它只是一个代码块constPROLOGUE_WORDS:usize1;fnstencil_prologue()-[u32;PROLOGUE_WORDS]{[0xAA0103E2]// mov x2, x1}对于字符比较我们需要插入我们正在比较的字符以及跳转到回退逻辑的位置constCHAR_WORDS:usize4;fnstencil_char(byte:u8,stencil_pos:usize,fail_pos:usize)-[u32;CHAR_WORDS]{[0x39400009,// ldrb w9, [x0]0x7100013F|((byteasu32)10),// cmp w9, #byte0x54000001|cond_branch_offset(stencil_pos2,fail_pos),// b.ne fail0x91000400,// add x0, x0, #1]}对于重复我们有循环开始处压入栈的操作和跳转到循环结束的操作constSPLIT_WORDS:usize5;fnstencil_split(resume_addr:u64)-[u32;SPLIT_WORDS]{[0xD2800009|addr_bits(resume_addr,0),// movz x9, #addr[0..16]0xF2A00009|addr_bits(resume_addr,1),// movk x9, #addr[16..32], lsl 160xF2C00009|addr_bits(resume_addr,2),// movk x9, #addr[32..48], lsl 320xF2E00009|addr_bits(resume_addr,3),// movk x9, #addr[48..64], lsl 480xA8810029,// stp x9, x0, [x1], #16]}constJMP_WORDS:usize1;fnstencil_jmp(stencil_pos:usize,target_pos:usize)-[u32;JMP_WORDS]{[0x14000000|branch_offset(stencil_pos,target_pos)]// b target}然后我们有匹配和失败块它们相当简洁constMATCH_WORDS:usize4;fnstencil_match(stencil_pos:usize,fail_pos:usize)-[u32;MATCH_WORDS]{[0x39400009,// ldrb w9, [x0]0x35000009|cond_branch_offset(stencil_pos1,fail_pos),// cbnz w9, fail0xD2800020,// mov x0, #10xD65F03C0,// ret]}constFAIL_WORDS:usize6;fnstencil_fail()-[u32;FAIL_WORDS]{[0xEB02003F,// cmp x1, x20x54000060,// b.eq 3 (to the mov below)0xA9FF0029,// ldp x9, x0, [x1, #-16]!0xD61F0120,// br x90xD2800000,// mov x0, #00xD65F03C0,// ret]}为了完整性这里是我们使用的辅助函数它们只是帮助我们将特定数据插入到指令中// 计算条件分支b.ne / cbnz的分支偏移字段// 从分支到目标的指令数存储在位 5..24 中。fncond_branch_offset(branch_pos:usize,target_pos:usize)-u32{letinstr_counttarget_posasi64-branch_posasi64;// 可能为负(((instr_countasu64)0x7FFFF)5)asu32}// 计算无条件分支b的分支偏移字段// 思路相同但存储在位 0..26 中。fnbranch_offset(branch_pos:usize,target_pos:usize)-u32{letinstr_counttarget_posasi64-branch_posasi64;// 可能为负((instr_countasu64)0x3FF_FFFF)asu32}// 提取绝对地址的 16 位为 movz/movk 立即数定位。fnaddr_bits(addr:u64,part:usize)-u32{(((addr(16*part))0xFFFF)asu32)5}发射代码现在是驱动它的代码// 计算一个节点编译成多少条指令。fnnode_words(node:Node)-usize{matchnode{Node::Literal(text)text.len()*CHAR_WORDS,Node::Concatenation(left,right)node_words(left)node_words(right),Node::Repetition(body)SPLIT_WORDSnode_words(body)JMP_WORDS,}}structEmitter{code:Vecu32,fail:usize,// 共享失败块的偏移以字为单位base:u64,// code[0] 的运行时地址用于绝对地址空洞}implEmitter{// 返回下一条指令将被放置的偏移量。fnpos(self)-usize{self.code.len()}// 将填充的模版追加到代码缓冲区。fnemit(mutself,stencil:[u32]){self.code.extend_from_slice(stencil);}// 为一个节点发射代码递归处理子节点。fnemit_node(mutself,node:Node){matchnode{Node::Literal(text){forbyteintext.as_bytes(){self.emit(stencil_char(byte,self.pos(),self.fail));}}Node::Concatenation(left,right){self.emit_node(left);self.emit_node(right);}Node::Repetition(body){letsplit_atself.pos();letexitsplit_atSPLIT_WORDSnode_words(body)JMP_WORDS;self.emit(stencil_split(self.baseexitasu64*4));self.emit_node(body);self.emit(stencil_jmp(self.pos(),split_at));}}}}// 生成完整程序prologue、编译后的 AST、MATCH、失败块。fngenerate_code(regex:Node,base:u64)-Vecu32{letnwordsPROLOGUE_WORDSnode_words(regex)MATCH_WORDSFAIL_WORDS;letmutemitterEmitter{code:Vec::with_capacity(nwords),fail:nwords-FAIL_WORDS,base,};emitter.emit(stencil_prologue());emitter.emit_node(regex);letmatch_atemitter.pos();emitter.emit(stencil_match(match_at,emitter.fail));emitter.emit(stencil_fail());assert_eq!(emitter.pos(),nwords);emitter.code}这就是困难的部分就我个人而言编写汇编是我觉得 AI 最有帮助的地方。我对汇编的主要经验是完成 microcorruption CTF。我自己从未真正编写过汇编。我真的很难弄清楚需要的具体指令以及如何修改它们以获得我想要的输出。有了 AI我可以给我的编码代理coding agent一个 JIT 编译器工作方式的总体框架它就可以为我处理许多这些细节。加载机器码为了完成我们的编译器我们需要实际加载代码。为此我们将使用mmap分配一块可读、可写和可执行的内存。然后我们将代码复制到该内存中并将该内存块转换为一个函数然后调用它constBSTACK_MAX:usize4096;// 这些函数包含在 mac 系统库中unsafeexternC{fnpthread_jit_write_protect_np(enabled:libc::c_int);fnsys_icache_invalidate(start:*mutlibc::c_void,len:libc::size_t);}typeMatchFnunsafeexternCfn(input:*constu8,bstack:*mutu64)-u64;structJit{buf:*mutu32,nbytes:usize,bstack:Vecu64,}implJit{fncompile(regex:Node)-Jit{letnwordsPROLOGUE_WORDSnode_words(regex)MATCH_WORDSFAIL_WORDS;letnbytesnwords*4;unsafe{letbuflibc::mmap(std::ptr::null_mut(),nbytes,libc::PROT_READ|libc::PROT_WRITE|libc::PROT_EXEC,libc::MAP_PRIVATE|libc::MAP_ANON|libc::MAP_JIT,-1,0,)as*mutu32;assert!(bufas*mutlibc::c_void!libc::MAP_FAILED,mmap failed);letcodegenerate_code(regex,bufasu64);pthread_jit_write_protect_np(0);// 使区域可写Apple W^Xstd::slice::from_raw_parts_mut(buf,code.len()).copy_from_slice(code);pthread_jit_write_protect_np(1);// 恢复为可执行sys_icache_invalidate(bufas*mutlibc::c_void,nbytes);Jit{buf,nbytes,bstack:vec![0;BSTACK_MAX*2]}}}// 运行生成的代码。输入必须以 NUL 字节结尾。fnis_match(mutself,nul_terminated:[u8])-bool{debug_assert_eq!(nul_terminated.last(),Some(0));unsafe{letmatcher:MatchFnstd::mem::transmute(self.buf);matcher(nul_terminated.as_ptr(),self.bstack.as_mut_ptr())!0}}}implDropforJit{fndrop(mutself){unsafe{libc::munmap(self.bufas*mutlibc::c_void,self.nbytes);}}}结果完成所有这些后让我们比较我们构建的不同实现的性能输入长度解释器JIT手写JIT 加速比手写加速比945 纳秒3.8 纳秒3.8 纳秒11.7 倍11.9 倍33103 纳秒7.9 纳秒10.5 纳秒13.0 倍9.8 倍129597 纳秒30 纳秒32 纳秒19.7 倍18.6 倍5131,955 纳秒126 纳秒120 纳秒15.5 倍16.2 倍2,0498,301 纳秒470 纳秒393 纳秒17.7 倍21.1 倍所以 JIT 和手写实现基本上并驾齐驱。有时 JIT 版本更快有时手写版本更快。网上流传着一个梗说 AI 没什么用因为“代码从来都不是困难的部分”。我认为这在某些领域是对的但在其他领域编写代码绝对是困难的部分。JIT 编译器就是一个很好的例子。对于许多软件来说JIT 编译器会大大有助于加速代码。JIT 编译器的稀有性让我相信从历史上看实现 JIT 编译器太难了以至于不值得。LLM 降低了准入门槛使得编写 JIT 编译器变得容易得多。这就是 pgrust 背后的理念。数据库历来是最难构建的软件并因此受到限制。现在有了 AI我们可以对我们构建的软件类型更加雄心勃勃。感谢阅读如果你想支持这个项目支持 pgrust 的最好方式是在 GitHub 上给我们一个 star。如果你想持续关注GitHubDiscord邮件列表pgrust.com每周更新 pgrust包括关于 JIT 编译的后续内容。

相关新闻

2011-2024年各省绿色创新绩效综合测算数据集

2011-2024年各省绿色创新绩效综合测算数据集

本数据集围绕中国各省级区域的绿色创新绩效展开综合测算,时间跨度覆盖2011至2024年。该数据集以《科研管理》期刊刊载的研究方法为参照框架,可服务于区域绿色创新绩效时空演化规律的精确度量、环境规制与工业智能化政策效应的实证评估,以及绿…

2026/8/19 8:50:06 阅读更多 →
Java虚拟线程惰性加载机制剖析

Java虚拟线程惰性加载机制剖析

Java虚拟线程惰性加载机制剖析前言Java虚拟线程惰性加载机制1. 物理栈结构与 HotSpot 数据模型切片HotSpot 核心数据结构差异表2. Continuation 挂起机制 (continuationFreezeThaw.cpp) 源码剖析JNI Pinning 机制的 C 源码实现Panama Trivial Downcall 为何完全避免 Pinning3. …

2026/8/18 6:05:42 阅读更多 →
Linux下RAR文件处理全攻略:从安装到实战排错

Linux下RAR文件处理全攻略:从安装到实战排错

1. 从一次文件传输的“事故”说起前几天,一个做设计的朋友火急火燎地找我,说客户发来一个巨大的素材包,后缀是.rar,他在自己的 Mac 上折腾了半天,用系统自带的归档工具打不开,下了几个免费软件也报错&#…

2026/8/18 6:57:00 阅读更多 →

最新新闻

Pomona:基于Agentic工作流的自动化代码质量改进系统

Pomona:基于Agentic工作流的自动化代码质量改进系统

1. 项目概述:当代码质量成为“日常习惯”在大型金融科技公司,比如彭博社(Bloomberg),代码库的规模动辄数百万行,由数千名工程师共同维护。一个普遍存在的挑战是:如何在不中断核心功能开发、不增…

2026/8/19 8:49:47 阅读更多 →
DM HUGE 表:达梦数据库中高效处理大数据的利器

DM HUGE 表:达梦数据库中高效处理大数据的利器

一、DM HUGE 表概述 1.1 DM HUGE 表的定义与特点 DM HUGE 表是达梦数据库中专门设计用于存储和管理大规模数据的一种特殊表结构。与普通表相比,DM HUGE 表在存储容量、查询性能和数据管理方面具有显著优势。主要特点包括: 支持超大规模数据存储&#xff…

2026/8/19 8:49:47 阅读更多 →
右键菜单管理:我把两屏长的菜单清到一屏,全程只用开关没碰注册表

右键菜单管理:我把两屏长的菜单清到一屏,全程只用开关没碰注册表

右键菜单管理:我把两屏长的菜单清到一屏,全程只用开关没碰注册表 【免费下载链接】ContextMenuManager 🖱️ 纯粹的Windows右键菜单管理程序 项目地址: https://gitcode.com/gh_mirrors/co/ContextMenuManager 装到第30个软件之后&…

2026/8/19 8:49:47 阅读更多 →
spring之事务底层源码解析【TL spring15】

spring之事务底层源码解析【TL spring15】

abstractPlatformTransactionManagerTransactionInterceptor强制回滚:

2026/8/19 8:49:47 阅读更多 →
学工系统专业厂家解决方案,打造高效便捷的学生工作管理平台

学工系统专业厂家解决方案,打造高效便捷的学生工作管理平台

✅作者简介:合肥自友科技 📌核心产品:智慧校园平台(包括教工管理、学工管理、教务管理、考务管理、后勤管理、德育管理、资产管理、公寓管理、实习管理、就业管理、离校管理、科研平台、档案管理、学生平台等26个子平台) 。公司所有人员均有多…

2026/8/19 8:49:47 阅读更多 →
Karma上海车展战略解析:中国化与平台化双轮驱动,三款新车重塑豪华电动车市场

Karma上海车展战略解析:中国化与平台化双轮驱动,三款新车重塑豪华电动车市场

1. 从“复活”到“新生”:Karma在上海车展的战略信号 如果你关注高端电动车市场,或者对汽车行业的“复活”故事感兴趣,那么今年上海车展上Karma Automotive的动向,绝对值得你花时间琢磨。这家公司身上有太多标签:源自破…

2026/8/19 8:48:47 阅读更多 →

日新闻

【单片机课程设计/毕业设计】基于 STM32 与 WiFi 模块的室内通风智能管控系统设计 基于 STM32 的人体存在感知自适应风扇控制系统设计(018503)

【单片机课程设计/毕业设计】基于 STM32 与 WiFi 模块的室内通风智能管控系统设计 基于 STM32 的人体存在感知自适应风扇控制系统设计(018503)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机,Java、小程序技术领域和毕业项目实战 ✌️…

2026/8/19 0:00:30 阅读更多 →
AI如何驱动数学猜想生成:从大语言模型到自动化数学发现

AI如何驱动数学猜想生成:从大语言模型到自动化数学发现

1. 项目概述:当AI开始“猜”数学定理 最近在AI研究圈里,一个名为“Moonshine”的项目引起了不小的讨论。这名字本身就挺有意思,直译是“月光”,但在数学史上,它特指一个神秘而美丽的联系——魔群月光猜想,连…

2026/8/19 0:00:30 阅读更多 →
WarcraftHelper 魔兽争霸3优化实战指南

WarcraftHelper 魔兽争霸3优化实战指南

WarcraftHelper 魔兽争霸3优化实战指南 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 一台刚配的新电脑,跑《魔兽争霸3》却卡成 PPT——这…

2026/8/19 0:02:31 阅读更多 →

周新闻

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

基于阿里云与通义千问(Qwen)构建AI应用:从模型调用到生产部署的完整实践指南

如果你是一名开发者,最近可能已经感受到了AI大模型正在从“玩具”变成“生产力工具”的强烈信号。从代码补全到智能Agent,从本地部署到云端API,我们正处在一个技术栈快速重构的节点。然而,面对层出不穷的模型、框架和工具&#xf…

2026/8/18 9:15:35 阅读更多 →
工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

工业通信系统底层逻辑:04 反射——高频能量撞墙之后会发生什么?

第四篇:反射——高频能量撞墙之后会发生什么? —— 你以为信号已经过去了,其实它正在回来打你 老Q的现场笔记 第五季,我们正式进入工业神经系统层。这里不再是单个设备的战斗,而是整个工厂“经脉”层面的秩序之战。从这一篇开始,你将第一次看清:看似简单的信号传播,背…

2026/8/18 9:06:28 阅读更多 →
【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

【文章复现】非线性值迭代自适应动态规划(ADP):离散时间非线性系统的策略迭代自适应动态规划算法研究附Matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,擅长毕业设计辅导、数学建模、数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室👇 关注我领取海量matlab电子书和…

2026/8/18 9:04:56 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/19 5:04:55 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/19 7:42:22 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/17 18:55:55 阅读更多 →