标签 c++ 下的文章

【USparkle专栏】如果你深怀绝技,爱“搞点研究”,乐于分享也博采众长,我们期待你的加入,让智慧的火花碰撞交织,让知识的传递生生不息!


Lyra项目,被誉为UE5的官方教学示例,实际上却远超初学者的难度。官方定义为初学者游戏,但真正掌握它,却需要较深的技术背景和至少200小时的UE开发经验。

视频地址:
【Inside Lyra】之我要学UE

引言

Lyra项目,是伴随着UE5一起发布的官方教学的示例项目,全称是Lyra Starter Game。官方定义为初学者游戏。

但学过的人都知道,这个示例项目远远超出了初学者的难度。简单来说,如果自学Lyra的话,你至少需要掌握以下条件:

1.熟练掌握UE特化C++编程。这就表示,你需要在熟练掌握普通C++的基础上,额外掌握Unreal为C++做的特性化封装,比如:以UHT(Unreal Header Tool)为代表的反射系统,以UObject为代表的UE对象系统,以TSharedPtr等为代表的智能指针和垃圾回收系统。

2.熟练掌握UE编辑器与核心框架。这表示,你需要能够熟练使用UE的编辑器,至少能知道绝大多数功能的入口,以及常用功能的位置和使用方法。在展示某个窗口的时候,你应该知道它是如何打开的,以及它包含哪些操作和信息。掌握核心框架则代表,你了解UE的模块化构建和依赖规则,了解事件委托机制和用法,了解UE的资产序列化与引用,了解常用的UE容器,以及核心的Gameplay框架等等。

除此之外,还希望你拥有至少2年的编程经验,或者至少1年左右的游戏开发经验。这表示,你应该能秒懂某些约定俗成的知识点,比如单例和工厂,广播和分发,会话和鉴权,缓存与热修等等。

低于上面这个门槛的同学,自学Lyra就如同在啃天书一样。并且从经验上来说,200小时的UE开发经验是达到高效学习Lyra的临界值,低于此时长的开发者自学Lyra的放弃率高达 90%。

但可以这么说,如果你能基本看懂并理解Lyra的所有代码,那么月薪2W是简简单单的。如果你能熟练掌握和使用所有的Lyra代码,做个主程也是随随便便的。

本文就是帮大家剖析一下Lyra这个项目,减少自学的挫败感,希望能够尽快掌握这个“工业级”入门游戏,提高自己的游戏开发水平。所以,我将这个系列定义为Inside Lyra。

在开始之前,再次强调一下,本文不是0基础,不会讲解诸如编辑器有几个面板,蓝图有几种类型,用TArray等容器写一个用于排序的Lamba表达式之类的基础知识。而是,尽量为大家深入浅出的去剖析,整个Lyra项目的运作流程,以及各个工业级框架和组件之间的协同关系等。

本文的目标是,能正常打开UE编辑器并运行Lyra就成功。

一、为什么是UE?

由于本文的目标非常简单,在开始之前我们还是先说明几个前置话题。

我拥有10多年Unity开发经验,过去也一直是在做Unity引擎相关的教程,那么这次为什么选择Unreal的项目做教程呢?从程序的角度来分析一下,主要有以下几点:

1. 引擎源码开放。
这一点对技术开发来说非常重要,非常非常重要。虽然引擎源码会大幅增加项目开发时的复杂度,从而导致学习和开发效率的锐减,但一旦你迈过这个门槛,从学习者变为熟练使用者之后,你才知道引擎源码对于开发者来说是多么提高效率的事情。你无需再盲猜每个功能背后的实现思路,你可以随意修改引擎源码完成特殊功能的适配,你可以随时断点来调试某个功能的调用顺序,你可以自己修复引擎版本自带的BUG,你甚至可以直接Copy引擎模块代码来实现一个比较接近的功能,等等。虽然我自己也能接触到其他引擎的源码,但绝大部分的开发同学是没有这个条件的。因此选择一个提供源码的引擎,对于开发者而言,会得到前所未有的控制力与安全感

2. 预置Gameplay框架。
从这一点上来说,我个人觉得是有利有弊的。如果大家接触过其他引擎就会知道,大部分引擎在开发项目的时候,是可以自由地定制项目的启动入口的,并且启动的流程也可以完全自定义。但是UE不是,它提供一套自己的Gameplay框架,开发者必须完全理解这套机制,并且在这个基础上穿插去做自己的定制流程。很难说这两种方式,谁更好。自由度高意味着,复用性低。而自由度低则意味着,可以举一反三。

也可以这么认为,当你理解了一个UE项目的启动流程之后,你就可以快速上手任何一个UE项目。但相比于可自由定制的项目来说,这增加了初学者的学习门槛,也是学习UE会卡关的主要问题。在其他引擎上,你花20个小时可能已经做出一个有点意思的小游戏了,而在UE上,20个小时可能连Gameplay框架还没学完。但总结来说: 用短期学习成本换取长期团队协作效率与项目可维护性的指数级提升,是非常有必要的,因为你不可能一直是一个人在战斗。

3. 强大完整的服务器。
UE采用的是服务器同构设计,同构设计代表的是服务器逻辑和客户端逻辑是同一套代码。也就是说服务器可以完全复用所有为该项目实现的框架和代码。以基于Actor级别的同步能力,和可靠的同步机制,让开发者完全不需要关心通信。使绝大多数的异常可锁定在游戏逻辑层,而无需反复排查底层同步机制,减少开发者在定位问题上的尝试,提高开发和调试效率。同时,也可以任意部署在本地或者直接用编辑器充当服务器来调试相关逻辑。但缺点也是有的,因为自己又当客户端,又当服务器,难免会在系统在开发的时候发生“精神分裂”,因为你要时刻切换自己的身份,思考某句代码是执行在权威服务器上,还是在本地客户端上。

4. 完整的无缝大世界方案。
无缝大世界,可以说是次世代开放世界游戏的标配了。在其他引擎中完成这个方案的代价非常高,而在UE中,只需在创建地图时,简单勾选几个选项,即可激活基础框架,配合Data Layers可快速实现昼夜/环境破坏等动态效果。这可以让开发者的精力从实现无缝大世界本身解放出来,全力投入内容创作与玩法设计上。

5. 影视级的动画系统。
内置Control Rig+Motion Matching等工业流水线,搭配IK/FK重定向的工具链,让中小团队也能产出3A级动画的丝滑表现。

6. 完善的编程规范和命名规范。
UE为所有代码和资产提供了一套命名规则,所有开发人员需要共同遵守,甚至部分规则如果不遵守还会强制报编译错误,因为代码可读性、可维护性是一个团队高效协作的利器

7. 蓝图。
是一把威力与风险并存的双刃剑。无需代码,策划/美术也能快速搭原型、配置数据,对UE新手友好。但从开发效率上和逻辑可读性上来说,远远落后于C++。当哪天你突然意识到蓝图实现某个功能很麻烦的时候,那么恭喜你,你现在是个程序员了。

8. 多线程安全。
在UE中,你可以通过Task Graph和AsyncTask提供高层抽象,在规范使用下大幅降低死锁/竞态风险。傻瓜式的封装,可以让你随意指定线程池(游戏/渲染/后台)来干活,完成后用委托(Delegate)再回调到Game线程去处理(非线程安全操作必须回主线程!)。

以上并不是全部的UE优势,UE最大的优势其实是他们每个版本都提供最前沿的技术,供开发者学习和了解,并且非常慷慨地给出所有资源和示例代码。甚至官网的文档都远远落后于代码的更新速度。

所以综合而言,选择UE,其实是选择一艘拥有核动力引擎的工业巨轮。启航虽慢(学习曲线陡),转向笨拙(框架约束),但其源码级的掌控力、成熟的Gameplay框架、高效的同构Server、革命性的大世界/动画方案、规范的代码环境、灵活的代码/蓝图模式、强悍的并发异步工具链等能力,让你在开发3A级巨制或大型多人游戏的惊涛骇浪中,拥有无与伦比的稳定性、生产力与技术上限。它逼你成长为“正规军”,过程虽让人头秃,但学成之日,你便是战场上的“王牌舰长”!

所以,就你了,Unreal,启动!

二、为什么是Lyra?

Lyra一直以来被当做UE官方的“初学者示例”,其实是存在认知错位的。它本质是Epic用来展示UE5工业级开发范式的技术演示,而非真正的零基础教程。它是一个完整的项目,包含各种顶级的框架设计和UE5最新的引擎特性。比如:

1.它有约15万行的代码和200+个蓝图,规模远超独立小游戏的标准。

2.多层抽象的基础框架,GameFeature+Experience+HotFix,再加客户端和服务器一体化的设计,复杂度非常高,需先熟练掌握插件系统/模块化设计

3.核心机制代码分散,学习难度高。被拆分为20+个独立的插件,对于能力复用和组合有较好的支持度。

4.使用数据驱动设计,一个简单的角色属性的修改,可能就要跨越数个配置文件。而非简单修改一个值。

一个典型的场景就是:

设计一个简单的复活队友的技能可能就要穿透至少5层:LyraHeroComponent → AbilitySystem → GameplayCue → 客户端预测 → 服务器RPC验证。

那么为什么Epic仍坚持推广Lyra呢?说些个人不负责任的猜想:

1. 技术展示。
强制开发者接触UE5前沿特性(如World Partition/MetaSounds, 等优秀能力)。

2. 生态绑定。
使用Lyra等于深度依赖Epic后端服务(如EOS好友,排名,成就等系统)。

3. 开发者筛选。
用高复杂度来过滤非目标用户(3A/多人游戏团队)。

  • 一个不负责任的数据显示,某个国外课程的后台数据表明,仅12%的UE自学者在Lyra项目上坚持超2周。

那么,最后回答一下,我们为什么要学习Lyra呢?我的答案是:

1.从技术提升角度来看,Lyra涵盖了UE5众多前沿且复杂的技术特性和架构设计。学习Lyra能够帮助开发者深入理解UE5的高级功能和工业级开发范式,掌握插件系统、模块化设计、数据驱动开发等关键技术,这些技术在大型游戏开发和复杂项目中具有极高的实用价值。例如,掌握了Lyra的架构设计思路,开发者在面对其他大型项目时,能够更高效地进行系统架构和模块划分。

2.从职业发展角度考虑,由于Lyra的学习难度较大,成功掌握它意味着开发者具备较强的技术学习能力和解决复杂问题的能力。正如前面调查数据所示,掌握Lyra的开发者在求职大公司和大型项目工作室时具有显著优势,能够获得更多的职业机会和更好的职业发展前景。

3.从行业趋势来看,随着游戏行业的发展,对游戏的品质和规模要求越来越高,UE5的工业级开发范式将逐渐成为行业主流。学习Lyra可以让开发者提前适应行业发展趋势,紧跟技术前沿,在未来的职业竞争中占据有利地位。

其实,大家也可从B站或者知乎上搜索Lyra相关的关键词,可以看到对一个子模块的讲解都可以作为一个技术大会的分享主题,也就证明了Lyra的含金量。再一个是,就目前来看,绝大部分UE5的项目,不管是什么类型的,都会或多或少参考Lyra的框架设计。比如,Lyra中完整实现了编辑器扩展,客户端和服务器交互和分离,构建多平台等能力,这些是无论做什么类型的游戏都有参考价值的。

三、教程形式

传统教程像在组装乐高说明书 —— 先逼你认遍所有“零件”,比如从UObject到Actor再到Component等,按照UE的复杂度来看,可能20小时后还在学基础概念。而那种技术大会分享更不用说,简直就是“量子速读现场” —— 满屏架构图咻咻乱飞,一张PPT涉及的知识点可能比他的头发还多。每章的知识点过的还非常快,来不及截图不说,截了也看不懂。

对于Lyra这种工业级的架构和业务来说,这两种就更不合适了,讲个GameState要先科普一个小时的UObject,这谁受得了呢?有可能你讲完Gameplay框架,UE都更新两个版本了。

而如果分模块击破也不靠谱,Lyra的核心代码分散在20多个核心的Plugin中,如果要按模块讲解,要就要把UE的插件-模块化的结构先讲清楚,再讲明白Lyra拆分插件的思路和优劣势,这也是耗时且性价比不高的事情。

所以真要按照传统线性的教学方式来的话,挫败感会很强,弃坑率也绝对会非常高。

而我这次的思路,会偏向于先上路再慢慢修正。也就是直接从Lyra运行的核心代码开始,按照顺序,代码执行到哪我们就拆解到哪。这样的好处就是,每一个介绍的代码你都知道它在当前运行时候是有用的。虽然坏处是会将知识点分散到多节课中,但每节课学到的就是项目当前在用的,知识点不浮空。

最后呢,本文还是以大家能看懂为主,在能看懂的基础上再去局部修改和替换小模块来验证自己的理解,以此为迭代才能真正的掌握。比如一个UI界面,当你看懂之后,可以先尝试修改上面的一个文字,然后添加或者修改一个自己导入的图片,或者直接加载一个自己写的UI等等。循序渐进地理解才能真正掌握。

四、环境准备

本次使用UE最新的版本,也就是5.6版本进行。请严格按照下面的步骤配置你的开发环境。

  • UE 5.6的下载和安装

1. 安装Epic Games启动器。
https://www.unrealengine.com/zh-CN/download

下载完成之后,像普通程序一样,双击安装。由于要额外下载内容,所以需要等待一会儿,也可以通过观看右边官方提供的安装视频进行安装。

UE引擎官方版本的安装只有这一个办法,当然当你有一定经验之后,可以到GitHub上下载引擎源码自己编译生成引擎也是可以的。

如果你有时间和话,也可以阅读官方提供的安装文档进行引擎安装:
https://dev.epicgames.com/documentation/zh-cn/unreal-engine/i...

如果你打开网页是英文的话,可以在这里切换语言:

启动器下载完成之后,需要登录才能正常使用。未登录状态下,运行启动器的界面是这样的,需要自行注册Epic账号进行登录,这一步就留给大家自行完成。

当你登录之后,看到的界面是这样的。

其中右边有三个页签,商城、库和虚幻引擎。前两个就跟Steam平台一样,展示的是作为游戏平台的内容,库则是你已经拥有的游戏。友情提示,Epic平台每周可以免费领取1~2款游戏,重大活动时候免费送大型游戏,可以保持关注。

然后我们要看的其实是第三个页签,也就是关于虚幻引擎的部分,点开页签后长这样。

这个页签下会发布很多UE引擎的动态和新闻。顶上的一排子标签的作用分别是:

  • News:热点资讯。
  • 示例:UE官方发布的各种技术演示工程,基本上都是完整可以运行的部分。
  • Fab:是EPIC多个资产商城的合集,可以下载大量的三方插件、代码和资产之类的。不过它需要在单独的页面打开才能看,启动器里只是一个跳转入口。你甚至可以在这里下载Unity的插件和资产。
  • 库:这个页签是引擎相关的管理页签。在这里可以下载新的引擎版本,和管理已经下载的引擎版本,比如我这里就已经下载好了5.6版本的引擎。

当你有已经安装好的引擎的时候,在右上角就会提示可以直接启动引擎,这是引擎启动的方式之一。当然如果安装了多个版本的话,右上角也可以切换版本。

如果没有引擎或者想要下载引擎,点击左边的“+”号,可以选择要下载的引擎版本。

选好引擎版本之后,点击安装就会弹出安装的一些选项,包括位置和选项。选项这里可以选择额外支持的部署平台,一般我们加上安卓和iOS就可以了。还有一个是调试符号,一般是用来调试引擎的,新手的话可以不管,这个要多增加几十G的空间。

都选好之后,点击安装,然后等待安装完成即可。

若要删除引擎,只要把鼠标移入标签卡里,就会出现卸载按钮,点击即可卸载。

  • VS的下载和安装

当引擎安装完毕之后,桌面上就会出现图标,同时右上角也会出现启动引擎的按钮。无论点击哪个都会打开引擎的预启动界面。第一次启动的时候,时间会很久,因为它要编译引擎启动所需要的材质,界面上会有进度和数量显示,需要耐心等待。

打开之后,这个界面长这样,主要是跟随引擎一起的模板、示例这些。然后可以管理和新建项目。

比如我要新建一个第三人称的示例项目就可以通过点击右侧Games标签来完成。

到这里就需要注意了,如果你是新手还不懂代码,只想用蓝图的话,现在就已经可以开工了。但是如果蓝图不能满足或者未来不能满足你的要求,就必须切换成 C++的版本。由于C++是代码,就必须要安装对应的编译环境才可以工作。这是UE附加的必要条件,只要你用代码,就必须要安装。一般我们是安装Visual Studio 2022的社区版本。因为它是免费的。

也可以手动去这里下载安装:
https://visualstudio.microsoft.com/zh-hans/vs/community/

另外当你切换任意一个示例到C++版本的时候,如果本地没有代码环境,那么会有一个黄字提示需要安装VS 2022,点击也会自动帮你下载。

安装VS的技巧可以参考官网,一些必要的组件需要勾选:
https://dev.epicgames.com/documentation/zh-cn/unreal-engine/s...

一些SDK版本,尽量选最新的。当然选多个也没问题,就是多占点空间而已。

当VS安装完成之后,UE开发必要的组件就已经准备好了。接下来我们就可以运行项目了。

  • Lyra示例工程的下载和安装

Lyra示例工程需要到Fab上先订阅,再在Epic启动器里下载。

Fab上直接搜索Lyra,找到图示的这个项目,点开之后加入到库里。点击在启动器查看,或者直接在启动器里搜索Lyra。

注意,这个库是引擎标签的库,不是最外层的游戏库。找到之后,点击创建,设置好目录,等待下载完成就可以了。

到这里Lyra的工程也就准备完成了。到设定的安装目录下就可以看到工程信息了。

由于Lyra是一个C++工程,在运行之前,必须要先编译才行。右键uproject文件,执行代码工程的生成,完成之后会有一个.sln的文件出现,代表生成成功。

这个时候双击Lyra,就可以打开Lyra工程了。当然,由于Lyra工程引入了大量新的材质,所以第一次启动也是会编译较长时间,请耐心等待,编译后会自动打开编辑器界面。

到这里,本文的目标就已经达成了。

后面这个点是我个人推荐的开发经验,并且我之后也都是会使用这个软件进行代码更新的。推荐大家继续看完,否则可能会影响后续课程的一些理解。因为不同IDE软件的操作和显示是不一样的。

  • Rider的下载和安装

Visual Studio是基础,但Rider专为UE深度优化,提供更智能的代码补全(理解UCLASS, UFUNCTION等)、更便捷的蓝图/C++互跳、更强大的反射信息查看、更直观的调试体验,以及更友好的项目结构管理。它能显著提升阅读和理解引擎和Lyra庞大代码库的效率。

访问以下地址下载Rider for Unreal Engine安装包。运行安装程序,按提示完成安装即可:
https://www.jetbrains.com/lp/rider-unreal/

Rider是一个付费软件,但个人非商业许可是免费的。第一次启动Rider的时候,会提示你选择许可。关联许可之后就可以正常使用Rider了。

在打开Lyra项目之前,推荐先下载一下这个插件,可以更好地关联UE:

不手动安装其实也行,Rider在检查项目完成之后,会提示你是否安装,他会有两个选择,一个是安装在引擎目录下,一个是安装在项目目录下。建议大家安装在引擎目录下,这样在创建其他项目的时候就不用再次安装了。

前面我们用双击uproject的方式打开编辑器,但作为程序员,你应该用IDE的方式打开编辑器。也很简单,用Rider打开它即可。

打开后,界面长这样,右上角两个按钮一个是正常启动,一个是调试模式。选择任何一个都可以。点击启动之后会先检查代码有没有编译,没有的话会先编译。编译好之后就会直接启动编辑器,效果跟双击uproject打开是一样的,但不一样的是调试模式就可以断点调试相关代码和逻辑。

那么到此,本文的内容就全部结束了。


这是侑虎科技第1946篇文章,感谢作者放牛的星星供稿。欢迎转发分享,未经作者授权请勿转载。如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。(QQ群:793972859)

作者主页:https://www.zhihu.com/people/niuxingxing

再次感谢放牛的星星的分享,如果您有任何独到的见解或者发现也欢迎联系我们,一起探讨。(QQ群:793972859)

【近些年,随着AI大模型的爆发式增长,千卡级AI集群成为常态,推动服务器功率密度持续攀升,服务器传统粗放式的功耗管理已无法满足能效要求,为解决数据中心的能耗管理问题,OurBMC社区及其理事单位飞腾信息技术有限公司在第三届开放原子大赛中设置"基于BMC的整机功耗智能管理"赛题,旨在探索BMC管理系统部署轻量级AI模型的技术路径,促进AI在OurBMC开源项目中的应用,为数据中心提供可落地的整机功耗智能管理方案。】

大赛自启动以来,汇聚了来自全国各地的78个队伍的130多位精英选手。选手们携数十份精彩作品,投身这场为期四个月的激烈实战竞技中。在此期间,各参赛队伍不仅积累了宝贵的实践经验,也深化了对比赛的理解与感悟。本期,社区特别邀请获奖企业团队分享 「走进OurBMC第三届开放原子大赛,共同践行开放包容、共创共赢的开源精神」,让更多人领略开源的魅力,感受技术的磅礴力量。

PART.01

1昆仑太科.jpg

参赛背景

团队长期致力于OpenBMC架构与嵌入式开发,在服务器温控场景中发现传统PID控制存在功耗与散热的平衡难题。通过OurBMC社区赛事通知渠道了解到本次比赛,希望以赛事为契机,将AI算法与BMC硬件管控深度融合,验证智能温控方案的可行性,同时借助开源平台与行业伙伴交流技术思路,推动BMC技术栈的创新升级。

核心方案

本项目聚焦于服务器智能温控系统中的单变量功耗智能管理,基于开源项目openbmc-OurBMC-24.12的phosphor-pid-control库基础上,引入AI驱动的动态预测与决策机制。项目基于BMC平台,深度集成了一套完全由C++实现、以梯度提升决策树(GBDT)为预测核心、以近端策略优化(PPO)为决策核心的自适应闭环控制系统。

数据采集采用双阶段策略:快速降温阶段与低功耗稳态调控阶段,实现从异常响应到节能运行的平滑过渡。通过温度预测模型对未来温度趋势进行高精度预测,并结合PPO强化学习生成节能导向的风扇转速建议,在保障设备安全运行的前提下,显著降低系统整体功耗。

控制策略采用安全优先的融合机制:最终风扇转速控制值指令取AI建议值与超温保障输出值中的较大者,实现“安全兜底+智能节能”的双重目标。该方案在保障设备可靠性的前提下显著降低风扇功耗,有助于提升数据中心能效比(PUE),助力绿色计算。

参赛过程及心得

本次参赛面临组队分工与赛题技术融合的双重挑战,团队通过明确“真实环境搭建-相关传感器适配-算法开发-工程部署-测试验证”职责分工高效协作。在赛题解析中,攻克了AI模型轻量化适配BMC嵌入式环境的难题。同时,团队成员平衡工作与备赛时间,利用碎片化时段开展模型训练与代码调试,深刻体会到技术落地需兼顾创新与实用性,开源协作模式更能加速技术迭代与问题解决。

我对社区说

感谢OurBMC社区搭建的开源交流平台,让我们能够基于社区成熟的技术栈进行创新实践。开源生态是BMC技术发展的核心驱动力,它打破了技术壁垒,让开发者得以共享经验、协同攻坚。BMC技术栈正朝着智能化、轻量化方向演进,期待未来能与社区伙伴深化合作,在硬件管控、能效优化等场景探索更多技术方案,共同推动开源BMC生态的繁荣发展,为绿色数据中心建设贡献力量。

PART.02

2移动云硬件团队.jpg

参赛背景

作为OurBMC社区成员单位,我们通过社区获知本次大赛的相关信息,组队参加本次大赛主要基于以下几点考虑:首先想通过参加本次大赛了解目前业界对于服务器智能功耗管理的最新研究成果,拓展自己专业能力;其次是分享移动云在这方面的成果,期待评审老师和同行能对我们的方案提出宝贵的意见,助力我们在服务器智能功耗管理领域不断前进,迈出新的高度。

核心方案

本次获奖作品是“基于BMC的智能功耗管理-SFC调速方案”,其核心思想是通过BMC收集服务器的关键工况信息,离线训练工况识别模型和温度预测模型,然后将这两个模型内置到BMC系统中。在服务器工作时,首先BMC获取服务器的关键工况信息,通过工况识别模型识别当前服务器的运行工况;然后在通过温度预测模型,基于当前的服务器工况预测关键部件的温度变化;再基于预测的温度变化信息,提前响应风扇转速,在满足温度约束的条件下,通过BMC调节风扇转速达到整体功率最低。

参赛过程及心得

本次大赛在接到赛题后,基于移动云在服务器功耗管理上的积累,我们迅速组建了一只技术实力互补、充满活力的团队,团队成员间展现出极高的协同性,通过紧密无间的合作,共同投入到赛题的深入解析之中。针对赛题要求,我们认为智能功耗管理不能影响BMC其他的核心功能,因此模型的轻量化,功耗管理的冗余措施必不可少。基于此,团队通过细致的分析和思维的碰撞,成功攻克了模型轻量化、预测准确度等多个技术难题,成功构建了基于BMC的智能功耗管理方案。同时,我们也看到了其他参赛队伍的优秀作品,这些优秀作品为我们后续在服务器智能功耗管理领域的研究提供了宝贵的参考与启示。

我对社区说

感谢OurBMC社区提供了一个如此卓越的平台,众多主流厂商纷纷投身于OpenBMC的开发浪潮中,让固件开发者得以在平台上深入探索BMC领域的奥秘。在这里,我们热切的期望与国内BMC相关领域的厂商携手合作,携手推进国产BMC技术的持续创新,共通促进国产BMC生态的繁荣发展。最后祝愿OurBMC社区蓬勃发展,越办越好!

PART.03

3百敖BMC.jpg

参赛背景

通过OurBMC社区了解到本次比赛。本次竞赛课题聚焦前沿AI技术领域,极具创新性与前瞻性,激发了团队的浓厚兴趣。随着人工智能在各行业深度渗透,将AI能力融合进BMC软件,正成为推动系统智能化演进的重要方向。参赛不仅是对自身技术能力的一次锤炼,更是与行业同行交流互鉴、共同探索的宝贵机会。

核心方案

本方案基于LSTM时序预测模型构建了一套智能化自适应温控决策机制。该模型通过持续采集分析温度数据与风扇转速的关系趋势,识别其内在模式与长期依赖关系,实现对未来温度变化趋势的前瞻性预测,并输出与之相匹配的风扇转速预测。同时,系统通过专门的融合决策模块,对LSTM的预测结果与PID的控制指令进行同步的比较与评估,动态地进行智能权衡与选择,最终下发风扇转速控制指令。

在确保设备散热需求完全满足、系统安全稳定运行的前提下,该系统实现了从“被动响应式控温”到“主动优化式控温”的转变,通过预测与反馈的闭环优化,有效平滑能耗曲线,减少不必要的功耗波动,达成散热效能与能源效率的最优平衡。

参赛过程及心得

由于BMC软件”小而美”的特殊性,芯片计算能力有限,存储空间受限,如何持续更新智能预测模型并兼容现有控速方案是最大的困难。我们依靠明确的晚间协作时段与高效异步沟通,将项目经验转化为比赛优势。这段经历再次印证,清晰的技术权衡与坚定的工程落地能力,往往比单纯追求技术新颖更为重要。

我对社区说

OurBMC社区通过持续举办开源大赛,为行业搭建了宝贵的交流平台,也让我们能够更深入地洞察技术前沿、把握创新脉搏。对此我们深表感谢,并将一如既往支持社区发展,共同推动行业进步。

PART.04

4信工所.jpg

参赛背景

我们与OurBMC有不解之缘,从第一届比赛开始便持续关注相关赛事,但由于博士学业繁忙遗憾错过。本次第三届比赛以功耗管理为主题,与我们近期在服务器能效优化方面的研究高度契合,且相关成果已发表在计算机体系结构领域的顶级期刊。因此,我们非常希望借助本次比赛,向开源社区展示我们的研究方案与团队实践经验,促进互相交流与学习,为国产自主可控BMC固件的发展贡献力量。

核心方案

我们的作品名称是HyperBMC,“Hyper”寓意超越传统服务器管理范式,强调BMC不再只是远程管理芯片,而是服务器智能管理引擎。通过在BMC芯片上部署深度学习模型,动态刻画计算需求与散热能力之间的平衡关系,进而触发调控决策;同时结合主机CPU与BMC之间的带内通信机制,协同管理风扇转速与CPU频率,从而实现服务器的精细化、智能化的功耗管理,在提升能效的同时保障性能与稳定性。

参赛过程及心得

尽管我们在基于BMC的功耗管理方面有一定的积累,但是面向本次比赛仍然遇到了许多挑战。一方面是软件版本升级与适配问题。我们团队只有OpenBMC 2.8.0的开发经验,将OurBMC 24.12版本编译到现有的平台,并且将我们之前的成果迁移上来,面临着Linux内核升级和Yocto工具链变化等诸多问题。另一方面是在嵌入式上运行深度学习的挑战。我们之前的方案是在远程控制器上运行传统的机器学习模型,在此次比赛中,我们想要充分挖掘嵌入式设备的性能,不仅将智能决策卸载到BMC,并且在BMC上直接推理深度学习模型。

我对社区说

非常感谢OurBMC社区搭建了一个开放、公平且有影响力的技术交流平台,使得我们研究团队有机会将最新的研究成果与各位同行分享。希望OurBMC社区能继续推动BMC相关的开源实践与生态建设,让更多开发者、研究者参与进来,共同打造一个更智能、安全和绿色的算力基础设施技术体系。我们也期待未来能进一步与社区合作,共同探索BMC在更多场景下的应用与扩展。最后,感谢OurBMC社区长期以来在BMC自主可控道路上的贡献,祝愿OurBMC系列比赛越办越好。

PART.05

5创新无限管芯微.jpg

参赛背景

管芯微是最早一批申请加入OurBMC社区的成员单位,长期活跃于社区活动。此次得知“基于BMC的整机功耗智能管理”赛题后,我们第一时间报名,参加比赛的初衷包括:一方面题目与我们联合团队正建设的广东赫曦原子智算中心高度契合;另一方面,我们希望通过比赛把社区的BMC轻量级AI部署经验应用到实际工作中,与同行一起探索降低PUE的新路径。

核心方案

作品方案面向原子级科学计算高性能服务器(赫曦I架构),设计了一套基于BMC的温度控制与功耗管理系统。该系统包含两个核心模块:单变量功耗智能管理和整机功耗智能管理。单变量功耗智能管理通过采集主板、CPU、GPU、APU等区域的温度、负载数据,采用ANN、CNN、LSTM-FNN等AI模型动态调节风扇转速组合,实现快速降温与低功耗温控。整机功耗智能管理通过LSTM模型预测CPU、GPU、内存等设备的负载峰值与低谷,动态调整CPU/GPU频率和电压,实现按需功耗分配。系统还支持增量学习、强化学习优化及阈值控制兜底,在保障计算性能的同时有效降低运行成本、提升能效。

参赛过程及心得

本次挑战赛自启动便锚定真实场景,涉及CPU、GPU及自研APU等多类硬件,需监测与调控的参数庞杂、手段各异;尤其是APU,必须经两级代理才能获取关键指标。如何把这些分散的监控手段熔于一炉,实现整机功耗的智能管理,成为最大难点。团队通过模块化设计与任务精细化分工,紧密协同,最终攻克了这一难题。由于采用联合组队,成员分处两地,大家积极配合、相互支持,克服时间紧、异地沟通难等障碍,确保在既定节点顺利完成赛题任务。

我对社区说

OurBMC社区把“开放”写进名字,更把“落地”刻进基因。比赛过程中,我们深度用到社区开源的框架和工具,真切体会到“代码面前无门槛”的魅力。希望社区继续围绕:一方面把功耗、安全、AI等前沿插件做成“积木”,让中小企业也能搭出高可靠方案;另一方面建立“赛题—社区—商业”正循环,让好的需求立刻变成可量产的主板固件。希望通过本次大赛,与大家一起把BMC从“远程开关”升级为“绿色算力中枢”。开源不是情怀,而是降低PUE的最短路径——让我们把这条路径越走越宽!

PART.06

6国科超算.jpg

参赛背景

我们通过开放原子开源基金会、OurBMC社区公众号了解到本次比赛,参赛初衷是当前AI大模型的爆发式增长,AI服务器集群成为常态,相较于传统服务器,功耗密度陡然攀升,传统粗放式的功耗管理已无法满足能效要求。在BMC管理系统里,引进AI功耗智能管理模块,根据主板关键元器件的温度、服务器OS的负载,对服务器的整机功耗,提供精准化、智能化的调控决策。

核心方案

获奖作品核心思想是通过轻量化AI技术,优化BMC风扇控制策略和功耗节能管理,实现高效散热与节能的平衡,采用如下关键机制:

  • 全场景数据采集:服务器覆盖空载、常规负载、高负载工况,确保数据采集完整性
  • 功耗建模与特征工程:基于硬件标定“风扇ID-对应硬件温度-PWM”映射表,构建实时功耗估算模型,简化特征维度,无需复杂计算,适配轻量化模型需求
  • 模型开发与训练
    超温阶段:开发LSTM多输出预测模型,实现快速响应温度趋势
    稳温阶段: 开发Q-Learning+能耗优化模型,实现稳态能效最优
  • 轻量化部署与测试:简化模型推理链路,控制延迟<10ms,部署异常兜底机制,确保模型推理失效或可置信度低时自动切换备用控温模式

参赛过程及心得

由于本次参赛团队成员涉及到不同专业领域,赛事前期AI方面的工程师和BMC开发工程师就赛题讨论存在一定的分歧,后经带队老师统一协调讨论,敲定最终实施的方案架构,团队成员即按照方案架构进行任务分配,开始采集数据、训练模型、搭建智能管理软件架构、部署测试,期间就模型训练结果不理想、数据采集有偏差等一系列问题,多次集中讨论,攻关,逐一解决。由于要兼顾公司下发的项目任务,为此我们每个人都为比赛付出巨大的精力和努力,但成果出来后的成就感让我们疲态尽扫,收获颇丰。

我对社区说

OurBMC作为国内首个开源的BMC固件栈社区,其开源精神和技术创新是值得我们所有相关从业人员学习的。社区近年陆续举办相关的BMC赛事,其竞赛背景均是服务器行业里高度关注的技术点,吸引了众多选手一同角逐,积极推动开源社区的发展。希望OurBMC社区能够发展的越来越好,拥有更加美好的未来!

关于OurBMC

OurBMC 社区是开发者交流和创新 BMC 开源技术的根社区,社区秉承 “开放、平等、协作、创新” 原则,坚持 “开源、共建” 的合作方式,旨在共同推进 BMC 技术快速发展,辐射上下游形成产业共振,加速构建繁荣的信息系统软硬件生态。

image.png

引言

从代码生成到自动化文档,人工智能已经开始渗透到软件开发生命周期的几乎每个阶段。但除了炒作之外,实际上发生了什么变化?我们询问了一群工程师、架构师和技术领导者,AI 辅助工具的兴起如何重塑软件开发的既定节奏,以及他们在现实世界中采用 AI 后学到了什么。

 

讨论嘉宾

  • Mariia Bulycheva——Intapp 高级机器学习工程师

  • Phil Calçado——Outropy 首席执行官

  • Andreas Kollegger——Neo4j 高级开发者倡导者

  • May Walter——Hud.io 创始人、首席技术官

 

InfoQ:AI 辅助工具的兴起对你们组织的软件开发过程有何影响?它们是否改变了你们对软件架构的思考方式?

 

Mariia Bulycheva:AI 辅助工具加速了原型设计,并减少了在重复编码任务上花费的时间,使我们的团队能够更多地关注架构决策和设计复杂的在线实验,这对于大规模迭代改进复杂的推荐系统至关重要。从数字平台典型的大量多模态数据中获得初步洞察也变得更快、更顺畅、更一致,因为我们可以将初始数据分析委托给了 AI。

 

我们工作的另一个非常重要的方面是跟上我们领域科学发展的快速步伐。每年,在顶级会议上都会发表数千篇新的研究论文,过去阅读它们并确定哪些可能与我们团队的日常 ML 任务相关是非常耗时的。今天,AI 工具提供了高质量的摘要,甚至突出显示哪些方法可能适用于我们的用例。这已经导致了几个新建模想法的快速实现,否则我们可能需要花费数周甚至数月的时间来发现和测试。

 

Phil Calçado:绝对有。我们运行的是一个消费者参与平台,其功能之多,任何正常人都无法全部记住。例如,最近,我们需要改变我们处理时区的调度方式。代码变更本身可能只有 10 行,但真正的工作是深入数百个涉及调度的地方,弄清楚每个地方的假设,并添加单元测试以断言调用站点不会中断,而是行为的变化。我们原以为这将是一个为期六个月的项目,因为我们需要逐步研究并进行小的更改。

 

有了像 Cursor 和 Claude Code 这样的工具,我们大大缩短了这个时间。它们帮助我们找出所有受影响的位置,为每个位置生成单元测试,并将推出分成按子系统分组的小 PR。每个 PR 都带有对所属团队的上下文敏感的描述——不仅仅是“修复调度,请审查”,而是解释为什么以及在他们的世界中预期的影响。

 

因此,尽管我们像其他人一样看到了原始代码输出的增加,但在我们这样成熟的、超大规模的系统中,最大的提升在于 AI 如何帮助我们研究自己的代码库,将无聊但必不可少的安全检查整合在一起,使系统性变更变得不那么可怕。

 

Andreas Kollegger:在我们组织中,所有员工现在都可以使用 AI 辅助工具。对于表面层级的界面设计,这些工具帮助我们更快地迭代,探索新想法,并解锁新方法,如氛围编码,专注于更高层次的设计和策略。

 

但我们也确实遇到了 AI 的局限性。像许多组织一样,我们发现大语言模型(LLM)在需要深厚领域专业知识和全局架构整体视图的高度专业化代码上挣扎。我们的代码库本身就超过了任何 LLM 上下文窗口的容量,而这些模型本身也没有在其中的独特复杂性上进行训练。简而言之,AI 不能发明它不理解的东西。因此,我们有意采取了一种以人为中心的方法:虽然 AI 帮助我们加速和增强,但推动软件架构突破的是我们工程师的专业知识。

 

May Walter:AI 辅助工具极大地缩短了从想法到工作代码的路径。一旦意图明确,迭代周期就会显著缩短。开发人员正在从代码的唯一作者转变为更像是管理者的角色——指导代理,验证输出,并确保需求得到真正满足。

 

AI 之前,架构是关于团队之间的所有权和可扩展接口。这引入了一个新的维度:上下文架构——设计代理生成生产就绪代码所需的输入、脚手架和护栏。上下文工程正在成为系统的核心部分,它简化了在复杂环境中快速构建的能力,如分布式和基于事件的系统。

 

但速度带来了一个新的瓶颈:为生产准备 AI 生成的变更。即使审查有了 AI 辅助,挑战也不再是关于发现语法错误,而是在大型、大规模的系统中验证意想不到的后果。

 

InfoQ:人工智能的采用如何影响团队内部的入职流程?你们团队或组织中的初级开发人员是否受到软件开发过程中采用人工智能的影响?

 

Mariia Bulycheva:人工智能工具可以通过提供即时的代码示例、文档摘要和测试建议,显著加快学习过程,这些都支持了初级开发人员。在处理个性化和推荐系统等复杂领域的团队中,这一点尤其有用,因为现在初级人员可以更快地探索新的代码库,而不必总是依赖高级工程师。同时,我们将他们与更有经验的同事配对,以确保他们学习潜在的基本建模和系统设计原则,而不仅仅是捷径。

 

Phil calado:我们刚刚让暑期实习生展示了他们的项目,几乎每个人都把人工智能称为救星。进入一个有 10 年历史的 Rails 代码库,其中包含数千个可移动的部分,这是令人生畏的。但是能够对 Cursor 或 Claude Code 说,“我是一名懂 Python 和 C++的大三学生,请用我熟悉的方式解释这个 Rails 代码”,这意味着他们可以在几周内提高效率,而不是仅仅把时间花在弄清楚基础知识上。

 

而且,不仅仅是实习生。在这个庞大的系统中,即使是高级工程师也需要比在小公司更多的准备时间。AI 并没有消除对系统的实际理解,但它确实减轻了“我们在哪里处理认证?”或“我们是否已经有了观察者模式的实现?”这类问题的压力。

 

当然,这里有一个问题。生成式 AI 擅长复制模式,这通常意味着我们不希望再看到的遗留风格和架构。因此,我们不得不适应。我们正在使我们的工作流程和架构更加适应 AI,并且我们已经开始将当前的指导方针直接嵌入到 Claude Code 和 Cursor 的智能体中。这样,当 AI 提供帮助时,它会引导人们走向现在,而不是过去。

 

Andreas Kollegger:人工智能的采用增强了我们的入职流程,特别是对于新接触图数据库的初级开发人员。虽然人工智能不能取代经验丰富的导师的指导,但它通过帮助新员工更快地上手,补充了我们现有的入职资源。

 

入职培训不仅仅是教授编码技能。它是关于建立领域专业知识的。编码能力很重要,但更重要的是理解要编写什么代码以及为什么。这就是为什么我们的入职开发人员,他们对代码库及其架构有深入的了解,在向初级团队成员传授专业知识和上下文方面发挥着关键作用。

 

May Walter:人工智能降低了贡献的障碍。现在,一个新开发人员可以在他们的第一天就写出可用的代码——这与早期工作仅限于样板或错误修复的日子相比,是一个戏剧性的转变。但真正的机会不在于速度;而是在于能力和范围的深度。

 

我最常听到的担忧是,人工智能有使入职变得肤浅的风险——初级人员可以在不理解代码为何以某种方式行为的情况下生成代码。我的经验恰恰相反。当代码生成与运行时反馈配对时,初级开发人员从一开始就接触到系统思维:架构在负载下的行为如何,依赖项如何相互作用,以及变化如何波及到业务结果。工程师成为智能体代码生成过程中的业务大使。

 

他们不再需要花几个月的时间来处理低价值的工作,而是能够处理团队的更多任务。如果做得好,这不会跳过步骤——它会加速步骤。有了正确的文化和期望设定,初级工程师可以更快地发展成为全面发展的工程师,因为他们不仅学习如何编写代码,还学习了为什么它在系统环境中很重要。

 

InfoQ:在你们团队或组织中,你们是否测量过 AI 辅助开发对生产力或质量的影响?你们学到了什么?

 

Mariia Bulycheva:我们在样板代码和单元测试生成方面看到了明显的生产力提升,甚至在为推荐系统设置模拟实验方面也是如此。然而,当处理影响大规模客户体验的关键系统时,真正的好处来自于将 AI 辅助与深度工程师参与结合起来。我们了解到,虽然 AI 提高了生产力,但质量仍然取决于仔细验证和清晰的指标和测试。

 

Phil Calçado::没有正式的测量。坦率地说,我不相信大多数抛出的“生产力”数字。在软件领域,你可以操纵指标,直到它们说出你想要的任何东西,而 AI 的炒作周期使这变得更糟。事实上,人们再次认真计算代码行数,只是为了增加一轮融资或提高股价,这是令人尴尬的。

 

Andreas Kollegger:在前端方面,我们看到了生产力的提升,特别是对于那些使用 Cursor 等工具的工程师。我们的许多工程师已经使用 AI 支持来更快地理解、进行表面编码和测试我们的代码库,但我们从 AI 中看到的真正影响是对开发人员体验的影响。通过使用 AI 工具来支持他们的一些活动,我们的工程师现在有更多的时间发挥创造力,并最终改进他们解决问题的方式,并为他们的工作创造新的方法。

 

May Walter:是的,我们了解到的第一件事是,大多数常见的度量标准并没有太大意义。公认的代码行数、提交次数、PR:AI 可以立即夸大这些数字,但它们只是工程生产力的虚荣指标。

 

真正的信号存在于下游。发布稳定性、事故频率、随叫随到的时间,甚至代码变更率,都能告诉我们是否真的在加快速度,或者只是在制造更多的脆弱性。AI 将速度转移到了管道的前端,但除非验证循环紧密,否则债务会在后期显现——以缺陷、回归和精疲力竭的团队的形式。

 

从第一天开始,通过持续的生产反馈,我们可以看到真相所在:功能开发变得更快,但审查周期变得更长了,部署后的错误也出现了。

 

教训是,AI 生产力需要一个学习曲线和迭代方法。一旦度量,可以逐步改进采用,以捕捉优势——同时避免因快速交付但存在稳定性问题窒息的陷阱。

 

InfoQ:为了有效地使用 AI 工具,你们的团队或组织中有哪些非技术方面的东西需要改变?

 

Mariia Bulycheva:最大的变化是在心态上。团队必须从期望 AI 建议是“正确”的,转变为将它们视为需要彻底验证、讨论和测试的起点。这种文化转变鼓励了实验和跨学科合作,将对确定性的关注转变为对探索的关注。在大规模个性化工作中,我们还需要与产品和法律团队就负责任的数据使用和可复制性达成一致。这些协议创造了护栏,使工程师能够安全地探索和部署 AI 辅助解决方案。

 

Phil calado:我认为关于生成式 AI 工具的最大事情是:这超越了编码。是的,像任何其他工具一样,你必须注意副作用。生成式 AI 可以很容易地生成内容:代码、PR 评论、技术规范、电子邮件、Slack 消息。它也使得总结大量文本并过滤掉非必要的内容变得非常容易。

 

这两个特性的结合创造了一个奇怪的激励:人们生成了大量的低信噪比内容,然后其他人再次使用 AI 将其过滤回去。这是极其无效的。我们已经开始内部讨论在制作内容时正确使用 AI 的方式。剧透:这不是让 AI 为你写作,而是使用 AI 帮助你写得更好。

 

Andreas Kollegger:我们在 AI 的早期阶段建立了一个 AI 伦理委员会,组织中的代表们更好地理解和指导 AI 如何影响我们的业务的每一个方面。所有技术都可以成为一股善的力量,但它也需要有意识的思考、行动和指导。

 

因为我们信任客户数据,我们的开发人员需要在引入 AI 作为助手的任何领域都应用更高的敏感性,从简单的计划文件和电子邮件线程到代码库本身。随着我们采用、集成和扩展 AI,我们所有的开发人员都必须确保人类判断,而不是 AI,指导和监督每一步。

 

May Walter:最大的变化不是技术上的,而是文化上的。开发人员自然会单独采用工具,但当 AI 被视为个人生产力技巧时,它的效果并不好。只有当它成为共享流程的一部分,有一致的验证步骤和清晰的责任时,它才会有效。此外,AI 工具在缺乏上下文时不会失败,而是产生不准确的回应,这可能会损害用户的信任并增加变更的摩擦。

 

在 10 名工程师的情况下,每个人都可以以自己的方式进行实验。在 100 名工程师的情况下,这种方法就会崩溃。不同的智能体独立生成代码会造成分裂和风险。我们转向了共同的设置和共享的工作流程,这样 AI 不仅仅是帮助个人更快地移动,而是使整个团队更快地移动。

 

InfoQ:你们在管理 AI 辅助编码方面设置了哪些护栏(文化、道德或技术),以及你们如何管理个人、团队和组织对 AI 输出的信任问题?

 

Mariia Bulycheva:我们将 AI 输出视为重复性或样板任务的“初稿代码”,这些代码总是经过单元测试和同行评审。在文化方面,我们强调责任:提交代码的开发人员负责,无论是否有 AI 辅助。对于机器学习工作流程,我们不信任 AI 直接生成模型,相反,在任何模型更改甚至可以考虑用于生产之前,我们依赖于针对既定基线的自动离线评估。这确保了 AI 驱动的贡献达到了与人类编写的代码相同的质量标准。

 

Phil calado:这仍然是一个非常初级的实践,所以我们一直在尝试不同的护栏和工具。在安全和合规方面,我们的立场从一开始就很明确:作为一个处理世界上一些最大品牌数据的上市公司,我们必须将相同的治理实践应用于 AI 编码工具,就像我们其他地方所做的一样。几年前,这意味着落后于曲线,但今天大多数供应商都有坚实的企业计划,所以我们可以安全地使用最先进的模型,而不会妥协安全性或可审计性。

 

文化上,我们很早就设定了期望:仅仅因为一个 AI 工具编写了变更,并不意味着它不是你的代码。你仍然拥有它,你需要把每一行都当作是你自己打出来的一样。这与使用 IntelliJ 的提取方法重构没有什么不同,在这种情况下,它可能自动化了机械操作,但你仍然需要理解并验证结果。

 

Andreas Kollegger:大型企业软件可以提供防止 AI 生成错误的保障,但更高的准确性、上下文和可追溯性是使 AI 输出可解释和可验证的关键,而不仅仅是性能。这就是为什么我们引入了一个广泛的测试计划,涵盖了从单个单元测试到详尽的生产级验证的所有内容。

 

与此同时,我们的工程师在纪律和创新之间保持平衡至关重要。我们鼓励工程师尝试各种想法,探索可能尚未准备好投入生产的项目。这种环境允许快速迭代和创造力,同时确保只有最有价值和经过充分测试的创新才能过渡到生产。其结果是一种独特的平衡:保持客户的信任和稳定,同时不断推进图驱动的创新,使 AI 更准确、透明和可解释。

 

May Walter:我们必须赢得对 AI 输出的信任,而获得信任的唯一方法便是创造上下文。每个 AI 生成的变更都经历了与人类编写的代码相同的标准——审查、测试、验证——但有一个额外的要求:它必须在运行时证明自己。

 

对我们来说,信任不是来自对模型的信念;而是来自观察代码在现实世界条件下的行为。新版本的性能和旧版本一样吗?它是否引入了新的错误或在负载下改变了性能?当运行时上下文持续可用时,AI 就不再是黑盒。它变成了一个可以信任的伙伴,因为它与工程师依赖相同的信号进行推理。

 

InfoQ:你们认为软件开发团队低估了 AI 编码工具的哪些方面?你们认为有哪些当前的 AI 增强型开发工作流程或模型被过度炒作,哪些仍然没有得到充分利用?

 

Mariia Bulycheva:许多团队低估了上下文管理的重要性,因为 AI 的效果取决于你提供的上下文(代码库、文档、架构、在线测试的实验设置)。在大型系统中,这意味着不仅要管理代码片段,还要管理模型性能数据、日志和实验历史,以有效指导 AI 工具。过度炒作:AI 据说取代了工程判断的“一键式开发”。未充分利用:AI 辅助调试、实验设置和复杂 ML 工作流的文档记录,这可以大幅降低长期维护成本。

 

Phil Calçado:现在 AI 中有很多空洞的炒作,很难挑出一个罪魁祸首。但大多数团队都低估了的这一点是:AI 编码工具不是一个单程的魔法盒子。你不能只是向它们抛出一个提示,就期望得到一致、正确的结果。

 

这是任何真正构建 AI 产品的人都知道的痛苦教训。无论你的提示工程有多聪明,有效使用 LLMs 来自于结合工作流程并确保正确的上下文在正确的时间可用。否则,你只是在掷骰子。

 

我在以前为一个流行的代码审查工具构建 AI 管道时亲眼目睹了这一点。模型可能已经记住了所有写过的 Python 书籍,但如果你问 10 个开发人员“正确的方法”去做某事,你会得到 11 个答案。如果没有你的代码库、组织标准和实际目标的上下文,LLM 就不知道哪个适用。这就是为什么你会得到完全不同的解决方案,甚至是对立的——这取决于当你提问时,概率之神想要倾向于哪一边。

 

Andreas Kollegger:许多软件开发团队低估了 AI 编码工具可以简化开发人员最不喜欢的任务,比如编写测试和文档。虽然 AI 编码演示承诺低代码和无代码,通常看起来微不足道或不可靠,但它们展示了 AI 如何将自然语言和代码之间进行转换,这对于自动化繁琐的任务和重复的设置是理想的。类似地,有一种专门用于项目初始化和代码生成的编码工具。

 

有一种工作流程被夸大了,但却没有得到充分利用,那就是让编码智能体通宵运行,并在早上检查他们的工作。我不建议在无人监督的情况下重构新产品功能或大量代码,但编码智能体非常适合一个定义良好的 GitHub 问题,它有良好的讨论,一个孤立且可重现的例子,以及一个可测试的修复。

 

May Walter:大多数团队低估的是,模型已经足够好(并且正在变得更好)——缺失的成分是组织上下文。等待“更好的模型”是一种分心。真正的挑战是设计提供生成生产级代码所需的上下文的系统:你的架构、编码标准、数据边界和业务优先事项。如果没有这些,即使是最好的模型(或工程师)也会表现不佳。

 

另一方面,今天被过度炒作的是原始代码生成和静态代码审查。这些工作流程在演示中看起来令人印象深刻,但它们并没有解决大型组织中软件工程最难的部分:调试和质量保证。智能体仍然缺乏运行时上下文,并且很少有工具来评估哪些更改在业务影响方面真正关键。

 

这个差距很重要,因为更快的代码生成意味着更多的更改流入生产环境——而且没有更强大的流程来决定要监控什么,团队冒着为了脆弱性而牺牲速度的风险。未充分利用的前沿不是更快地编写代码,而是构建验证循环和运行时感知工具,以在这些更改部署之前增加确定性。

 

结论

从这次讨论中得出的第一个,也许是最重要的结论是,尽管在软件开发过程中采用 AI 工具无疑降低了贡献的门槛,但它仍然是一个乘数,而不是灵丹妙药。只有与强大的组织环境相结合,AI 才能增强生产力。基于 AI 的工程有潜力成为软件开发的核心,就像 CI/CD 管道曾经一样。然而,架构、编码标准和实验脚手架是成功采用 AI 的支撑支柱。

 

与此同时,随着 AI 工具的发展,组织中开发人员的角色也从代码作者转变为系统编排者。新采用的策划、验证和集成 AI 输出的过程并没有取代软件工程这门手艺;相反,它增强了它。批判性思维和架构意识比以往任何时候都更重要。

 

当然,采用任何新技术都会带来陷阱,对于 AI 和基于 AI 的工具也是如此。降低贡献的进入门槛也意味着增加了浅薄理解和生产次品代码的风险,这可能对初级开发人员的职业发展和整个组织产生负面影响。指导和运行时反馈是重要的护栏,以及文化和伦理保障:AI 输出必须被视为初稿,人类必须对其负责。当涉及到 AI 时,信任不是授予的:它是一个过程,通过测试、同行评审、运行时验证和透明度赢得。

 

成功的指标也必须重新思考,因为 AI 夸大了所有传统的生产力指标。有意义的信号来得更晚:稳定性、变动、事件,以及有多少时间可以释放给创造力和架构。将 AI 扩展视为一个协作过程,而不是个人生产力的提升,这需要协调的工作流程和对周围流程的更高成熟度。

 

无论好坏,很明显,AI 带来的变化已经到来,正在重塑软件开发的工艺。仍然有未被充分利用的方面,但上下文设计和运行时感知工具已经是下一个架构前沿。从长远来看,AI 竞赛的赢家将是那些将其整合到具有问责制、信任和能够以负责任的方式共同发展的团队级流程中的人。

 

原文链接:

https://www.infoq.com/articles/ai-developers-rewriting-software-process/

TVM 现已更新到 0.21.0 版本,[TVM 中文文档]已经和新版本对齐。

Apache TVM 是一个深度的深度学习编译框架,适用于 CPU、GPU 和各种机器学习加速芯片。更多 TVM 中文文档可访问 →[Apache TVM]

在线运行 TVM 学习教程

链接是:https://hyper.ai/notebooks/48919?utm_source=Distribute&utm_medium=Distribute-TVM&utm_campaign=Distribute-TVM-260126

Relax 与 TVM IR 都包含一系列优化传递(optimization passes),用于改进模型在特定设备上的性能指标,例如推理平均时间、内存占用或功耗。这些优化包括标准优化与机器学习特定优化,如常量折叠(constant folding)、死代码消除、算子布局变换、算子融合、缓冲区处理和循环变换等。每个传递都是基于收集的分析结果进行的 IR-to-IR 转换。

然而,随着 TVM 的快速发展,越来越需要一种系统化且高效的方式来管理这些传递。此外,一个通用的框架能够在 TVM 栈的不同层次(例如 Relax 和 tir)之间管理传递,这为开发者快速原型化和集成新传递铺平了道路。

本文档介绍了这种基础设施的设计,它结合了生产级编译器中用于管理优化传递的方式,以及现代深度学习框架用于构建层次化结构的风格。

例如,许多现有的生产级编译器(如 GCC 与 LLVM) 采用「传递管理器(pass manager)」来高效管理传递执行。最初传递数量较少时管理很简单,但成熟编译器可能包含数百个独立传递。外部用户往往希望添加自定义传递,并能正确调度,而无需手动修改固定顺序。

类似地,现代深度学习框架(如 Pytorch 与 MXNet Gluon)也倾向于通过SequentialBlock实现类似「传递式」层构建机制。 借助这些构造,框架能够轻松将模块或层添加到容器中,从而快速搭建神经网络。

TVM 的传递基础设施设计灵感主要来自 LLVM 的层次化传递管理器 以及流行深度学习框架的模块化容器。 该系统的主要目标包括:

  1. 支持更灵活的优化编排,让用户能自由构建自定义优化流水线。
  2. 提供便捷的调试机制。
  3. 让开发者无需手动解决传递之间的依赖。
  4. 简化新传递的实现方式,例如允许用户直接用 Python 实现一个传递,由系统自动管理其执行。

设计概述

系统重点关注可扩展性,使用户能快速添加新传递而不破坏兼容性。 其结构包括后端与前端:后端实现核心逻辑,前端则提供简单的 API 供用户创建与控制优化流程。

C++ 后端

我们提供 PassInfo对象来存储单个传递所需的基本信息:name为传递名,opt_level指示该传递在哪个优化级别启用,required表示执行该传递前所需的其他传递(详见include/tvm/ir/transform.h)。 在注册传递时,开发者可以指定传递名称、优化级别与依赖。 opt_level可帮助系统在给定优化级别下判断某个传递是否需要执行; required字段用于自动解析传递依赖。

class PassInfoNode : public Object {
  ffi::String name;
  int opt_level;
  ffi::Array<ffi::String> required;
};

PassContext

PassContext 携带优化传递所需的关键信息。例如,它包含错误报告系统,方便优化作者诊断失败原因。 PassContext也取代了旧的 BuildConfig(用于配置编译选项,如优化级别、必需/禁用传递等)。例如,我们可以配置在 opt_level=3 下执行所有传递,并通过disabled_pass=xx 禁用某些传递;系统会聚合该级别的所有传递并排除被禁用的项。PassContext还提供对所有传递进行"检测(instrumentation)"的能力,见 pass_instrument_cpp_backend

该类支持 Python with 语法,便于在给定配置下执行优化。 同时,用户可以通过 PassContext::Current()在线程安全的方式获取当前上下文, 因为系统使用线程本地存储PassContextThreadLocalStore 来保存上下文对象。

class PassContextNode : public Object {
 public:
  int opt_level{2};
  tvm::ffi::Array<tvm::Expr> required_pass;
  tvm::ffi::Array<tvm::Expr> disabled_pass;
  mutable ffi::Optional<DiagnosticContext> diag_ctx;
  ffi::Map<ffi::String, Any> config;
  ffi::Array<instrument::PassInstrument> instruments;
};

class PassContext : public NodeRef {
 public:
  TVM_DLL static PassContext Create();
  TVM_DLL static PassContext Current();
  TVM_DLL void InstrumentEnterPassContext();
  TVM_DLL void InstrumentExitPassContext();
  TVM_DLL bool InstrumentBeforePass(const IRModule& mod, const PassInfo& info) const;
  TVM_DLL void InstrumentAfterPass(const IRModule& mod, const PassInfo& info) const;
  /* 其他字段省略 */

 private:
  // 进入 pass 上下文作用域
  TVM_DLL void EnterWithScope();
  // 离开 pass 上下文作用域
  TVM_DLL void ExitWithScope();

  // 用于支持 Python `with` 语法
  friend class tvm::With<PassContext>;
};

struct PassContextThreadLocalEntry {
  /*! rief 默认 pass 上下文 */
  PassContext default_context;
  /*! rief 当前 pass 上下文 */
  std::stack<PassContext> context_stack;
  PassContextThreadLocalEntry() {
    default_context = PassContext(make_node<PassContextNode>());
  }
};

/*! rief 线程本地存储,用于保存 pass 上下文 */
typedef dmlc::ThreadLocalStore<PassContextThreadLocalEntry>
     PassContextThreadLocalStore;

Pass 构造

传递(Pass)基础设施以分层结构设计,可在 Relax/tir 程序的不同粒度上工作。 系统定义了一个纯虚类PassNode,作为各种优化传递的基类。此类包含多个必须在子类中实现的虚函数,适用于模块级、函数级或顺序传递级别。

class PassNode : Object {
  virtual PassInfo Info() const = 0;
  virtual Module operator()(const IRModule& mod,
                            const PassContext& pass_ctx) const = 0;
};

该函数对象定义了传递的执行方式: 每个传递都在特定上下文 PassContext下作用于一个 IRModule, 并以 Module 到 Module 的方式实现。因此,所有传递都以模块为单位更新整个 IR。

系统实现了多个 PassNode 子类来支持不同类型的优化: 包括函数级传递、模块级传递与顺序传递(sequential pass)。 每个子类本身都可充当一个传递管理器,例如:它们可以收集所需传递并执行,或基于元信息建立依赖图。完整定义见src/ir/transform.cc

模块级传递

模块级传递主要用于全局或过程间优化(IPO),类似于 LLVM 中的模块传递。Relax 中一些典型需要全局视图的优化(如 A-normal form 转换、lambda 提升)就属于此类。 在该级别,用户可以在模块中添加或删除函数。

class ModulePassNode : PassNode {
  PassInfo pass_info;
  std::function<Module(Module, PassContext)> pass_func;
  Module operator()(const Module& mod, const PassContext& pass_ctx) const final;
  // 其他成员/方法省略
};

pass_info 存储模块传递的相关信息,pass_func 定义实际优化逻辑。例如,在模块上执行死代码消除可在 pass_func 中实现,它将删除模块中未使用的函数。 此字段被设计为「打包函数(packed function)」, 因此优化逻辑既可用 C++ 实现,也可用 Python 实现。

函数级传递

函数级传递用于实现 Relax/tir 模块中函数内的优化。它一次提取模块中的一个函数进行优化,输出优化后的 Relax Function 或 tir PrimFunc。多数优化都属于此类,如 Relax 的公共子表达式消除、推理简化,或 tir 的向量化与内存扁平化。

函数级传递仅作用于单个函数(Relax 或 tir),因此无法通过此类传递添加或删除函数,因为其不具备全局信息。

class FunctionPassNode : PassNode {
  PassInfo pass_info;
  std::function<Function(Function, Module, PassContext)> pass_func;
  Module operator()(const Module& mod, const PassContext& pass_ctx) const final;
  bool SkipFunction(const Function& func) const;
  // 其他成员/方法省略
};

pass_info 与模块级传递相同。 pass_func接受函数与模块作为输入,可在函数上执行优化; 函数若被注解为SkipOptimization,将被跳过。

顺序传递(Sequential Pass)

SequentialPass 类似于 PyTorch 的 nn.Sequential,可包含多个顺序执行的传递。

class SequentialPassNode : PassNode {
  PassInfo pass_info;
  // 需要执行的传递列表
  ffi::Array<Pass> passes;
  bool PassEnabled(const PassInfo& info) const;
  Module operator()(const Module& mod, const PassContext& pass_ctx) const final;
};

以下展示顺序传递的执行逻辑:系统会按照传递添加的顺序依次执行。

Module SequentialNode::operator()(const Module& module,
                                  const PassContext& pass_ctx) const {
  Module mod = module;
  for (const Pass& pass : passes) {
    ICHECK(pass.defined()) << "Found undefined pass for optimization.";
    const PassInfo& pass_info = pass->Info();
    if (!PassEnabled(pass_info))  continue;
    for (const auto& it : pass_info->required) {
      const auto* name = it.as<tvm::ir::StringImm>();
      ICHECK(name);
      mod = GetPass(name->value)(mod, pass_ctx);
    }
    mod = pass(mod, pass_ctx);
  }
  return mod;
}

在执行传递前,系统会判断该传递是否启用:首先检查是否被用户禁用,其次查看是否被显式声明为必需。若仍未确定,则根据 opt_level 判断是否执行。

执行时,系统会根据传递名从注册表中获取对应实现:

Pass GetPass(const std::string& pass_name) {
  using tvm::runtime::Registry;
  std::string fpass_name = "relax.transform." + pass_name;
  const std::optional<tvm::ffi::Function> f = tvm::ffi::Function::GetGlobal(fpass_name);
  ICHECK(f.has_value()) << "Cannot find " << fpass_name
                        << "to create the pass " << pass_name;
  return (*f)();
}

系统还提供辅助函数用于创建各类传递,并暴露给 Python 前端:

Pass CreateFunctionPass(
    std::function<Function(Function, IRModule, PassContext)> pass_func,
    int opt_level,
    ffi::String name,
    ffi::Array<ffi::String> required);

Pass CreatePrimFuncPass(
    std::function<PrimFunc(PrimFunc, IRModule, PassContext)> pass_func,
    int opt_level,
    ffi::String name,
    ffi::Array<ffi::String> required);

Pass CreateModulePass(
    std::function<IRModule(IRModule, PassContext)> pass_func,
    int opt_level,
    ffi::String name,
    ffi::Array<ffi::String> required);

Pass Sequential(tvm::ffi::Array<Pass> passes, PassInfo pass_info);

传递注册

前文介绍了不同粒度的传递和编译上下文。 下面展示如何注册一个传递。以常量折叠(constant folding)为例, 它用于在 Relax 函数中折叠常量(实现位于 src/relax/transforms/fold_constant.cc)。

该传递提供了 Expr 到 Expr 的转换 API:

Expr FoldConstant(const Expr& expr);

要将其注册到传递基础设施中,首先需要确定传递的粒度。常量折叠作用于函数级,因此通过 CreateFunctionPass 创建:pass_func 以打包函数形式返回,用于对 [IRModule]{.title-ref} 中的每个函数调用该转换 API。 {} 表示该传递没有前置依赖;若有依赖,开发者需明确列出。

同时,注册名为 "relax.transform.FoldConstant" 的 API 入口,使该传递可被 C++ (例如以上的 GetPass )与 Python 访问:

namespace transform {

Pass FoldConstant() {
  auto pass_func =
      [=](Function f, IRModule m, PassContext pc) { return ConstantFolder::Fold(f, m); };
  return CreateFunctionPass(pass_func, 0, "FoldConstant", {});
}

TVM_FFI_STATIC_INIT_BLOCK() {
  namespace refl = tvm::ffi::reflection;
  refl::GlobalDef().def("relax.transform.FoldConstant", FoldConstant);
}

}  // namespace transform

为方便其他 C++ 模块调用,在include/tvm/relax/transform.h中声明:

TVM_DLL Pass FoldConstant();

传递检测(Pass Instrument)

传递检测机制用于分析传递本身,例如统计执行时间与内存占用,或观察 IR 如何被改变。

我们在 PassContext 生命周期中引入四个检测点:

TVM_DLL void InstrumentEnterPassContext();
TVM_DLL void InstrumentExitPassContext();
TVM_DLL bool InstrumentBeforePass(const IRModule& mod, const PassInfo& info) const;
TVM_DLL void InstrumentAfterPass(const IRModule& mod, const PassInfo& info) const;

InstrumentEnterPassContext 在进入 PassContext 作用域时调用。

InstrumentExitPassContext 在离开 PassContext 或执行发生异常时调用。当通过 :pytvm.transform.PassContextoverride_instruments 覆盖检测器时也会触发,见pass_instrument_overriden

InstrumentBeforePass 在传递执行前调用; 若该传递应执行,则在执行后调用 InstrumentAfterPass。其伪代码如下:

if (pass_ctx.InstrumentBeforePass(ir_module, pass_info)) {
  new_ir_module = run_pass(ir_module, pass_ctx);
  pass_ctx.InstrumentAfterPass(new_ir_module, pass_info);
  return new_ir_module;
}

PassInstrument接口允许你在上述四个阶段插入自定义逻辑。 可向单个PassContext 注册多个检测器实例,它们将按 instruments指定的顺序依次调用。

接口定义如下:

namespace instrument {

class PassInstrumentNode : public Object {
 public:
  ffi::String name;
  virtual void EnterPassContext() const = 0;
  virtual void ExitPassContext() const = 0;
  virtual bool ShouldRun(const IRModule& mod, const transform::PassInfo& info) const = 0;
  virtual void RunBeforePass(const IRModule& mod, const transform::PassInfo& info) const = 0;
  virtual void RunAfterPass(const IRModule& mod, const transform::PassInfo& info) const = 0;
  /* 其他字段省略 */
};

class PassInstrument : public ObjectRef {
 public:
  TVM_FFI_DEFINE_OBJECT_REF_METHODS_NULLABLE(PassInstrument, ObjectRef, PassInstrumentNode);
};

}  // namespace instrument

Python 前端提供了便捷方式来实现 PassInstrument,见pass_instrument_py_frontend

在一个 PassContext 中,某个 PassInstrument 实例的调用顺序如下:

with PassContext(instruments=[pi])  # pi 为某个 PassInstrument 实现
    pi.EnterPassContext()

    if pi.ShouldRun(Pass1):
        pi.RunBeforePass()
        Pass1()
        pi.RunAfterPass()

    if pi.ShouldRun(Pass2):
        pi.RunBeforePass()
        Pass2()
        pi.RunAfterPass()

    pi.ExitPassContext()

以下简述 PassInstrument 与 PassContext 方法之间的关系,详见 src/ir/transform.cc

  • InstrumentEnterPassContext

    • EnterPassContext() 按传入 instruments 的顺序执行。
    • 若执行中抛出异常,PassContext 会清空所有已注册的检测器。
    • 然后对已成功执行 EnterPassContext() 的检测器依次调用 ExitPassContext()
    • 例如,注册了 A、B、C 三个检测器,A 成功,B 抛异常,则 C 不会执行;随后调用 A 的 ExitPassContext()
  • InstrumentExitPassContext

    • 各检测器的 ExitPassContext() 按 instruments 顺序执行。
    • 若发生异常,instruments 会被清空。
    • 抛出异常后注册的检测器不会执行 ExitPassContext
  • InstrumentBeforePass

    • 若该传递未被显式列为"必需",则会调用 ShouldRun
    • 若未被 ShouldRun 阻塞,则按顺序调用 RunBeforePass
    • 该函数返回布尔值,指示该传递是否应执行。
    • 若发生异常,将立即抛出;Python 依靠上下文管理器安全退出(确保各检测器的 ExitPassContext 被调用;C++ 见 include/tvm/support/with.h)。
  • InstrumentAfterPass

    • 按顺序调用 RunAfterPass
    • 若发生异常,将立即抛出;依靠上下文管理器或 With 类(include/tvm/support/with.h)安全退出。

内置检测器

系统内置若干检测器(标注 TODO 的尚未实现):

  • PassTimingInstrument(见 src/ir/instrument.cc

    • 用于分析各传递的执行时间。
  • PrintIRBefore(TODO)

    • 在传递执行前打印 IR。也可通过 :pytvm.transform.PrintIR{.interpreted-text role="func"} 在传递周围插入打印实现;但使用检测器无需修改传递序列。
  • PrintAfter(TODO)

    • 在传递执行后打印 IR。

Python 前端

前端仅需少量 API 即可创建并执行传递(完整实现见python/tvm/relax/transform/transform.pypython/tvm/ir/transform.py)。后端将根据提供的信息决定如何创建 Pass 对象。

PassContext

Python 前端为 PassContext 提供了包装以支持 with 语法,并提供current 静态方法:

@tvm_ffi.register_object("transform.PassContext")
class PassContext(tvm.runtime.Object):
    def __enter__(self):
        _transform.EnterPassContext(self)
        return self

    def __exit__(self, ptype, value, trace, config):
        _transform.ExitPassContext(self)

    @staticmethod
    def current():
        """Return the current pass context."""
        return _transform.GetCurrentPassContext()

PassContext用于配置编译选项(优化级别、必需/禁用传递等),并可传入配置字典,以便不同传递读取需要的数据(如回退设备信息、循环展开步数/深度等)。若要从 config 中获取某项配置,其键名需通过TVM_REGISTER_PASS_CONFIG_OPTION 注册,例如循环展开传递:

TVM_REGISTER_PASS_CONFIG_OPTION("tir.UnrollLoop", UnrollLoopConfig);

详见src/tir/transforms/unroll_loop.cc

Python 中的传递检测

使用装饰器(python/tvm/ir/instrument.py)可以快速实现 PassInstrument。 推荐使用装饰器方式而非继承:

  • enter_pass_ctx:进入 PassContext 时执行;
  • exit_pass_ctx:退出 PassContext 时执行;
  • should_run:在传递执行前调用,返回该传递是否应执行;
  • run_before_pass:传递执行前调用;
  • run_after_pass:传递执行后调用。

可通过 :pytvm.transform.PassContext 的 instruments 参数注册实例。更多示例见use pass instrument教程。

覆盖当前 PassContext 中的检测器

override_instruments 方法可覆盖当前 PassContext 中的 instruments。例如,当未显式创建新 PassContext 而直接运行传递时,仍可将检测器注册到全局上下文:

cur_pass_ctx = tvm.transform.PassContext.current()
# 覆盖 PassInstrument 实例
cur_pass_ctx.override_instruments([pass_inst])
mod = pass_seq(mod)
result = pass_inst.get_result()

注意:调用 override_instruments 时,旧检测器的 exit_pass_ctx会被调用,随后新检测器的 enter_pass_ctx 会被调用。

从底层引擎优化角度,深入剖析 Hono 路由器的极致性能奥秘

引言

在众多 JavaScript Web 框架中,Hono 以其极致的性能表现脱颖而出。特别是在 Cloudflare Workers、Deno 等边缘计算环境中,Hono 的路由匹配速度在同类框架中名列前茅。这背后的秘密是什么?答案就藏在它的 RegExpRouter 实现中。

⚠️ 重要提示:本文重点讨论 RegExpRouter 的核心原理。在实际应用中,Hono 使用 SmartRouter 自动组合多种路由器(RegExpRouter、TrieRouter、LinearRouter)以达到最佳性能。

本文将通过一个精简的实现,深入剖析 Hono 路由器的核心原理,帮助你理解:

  • 为什么 RegExp 路由比传统路由快
  • 路由是如何预编译的
  • 参数提取的巧妙设计

📌 关键要点

项目说明
核心原理将路由预编译为正则表达式,利用引擎底层优化
性能优势减少 JS 层开销,一次原生调用完成匹配
最佳环境Cloudflare Workers、Deno 等边缘计算平台
实际架构SmartRouter + RegExpRouter + TrieRouter 组合
权衡考虑Node.js 环境性能打折扣,路由注册较慢

传统路由的性能瓶颈

在理解 Hono 的优化之前,我们先看看传统路由的处理方式:

// 传统路由的典型实现
function matchRoute(path, routes) {
  for (const route of routes) {
    // 1. 字符串分割
    const pathParts = path.split('/');
    const routeParts = route.path.split('/');

    // 2. 逐段比较
    if (pathParts.length !== routeParts.length) continue;

    const params = {};
    let matched = true;

    // 3. JS 层面的循环和条件判断
    for (let i = 0; i < pathParts.length; i++) {
      if (routeParts[i].startsWith(':')) {
        params[routeParts[i].slice(1)] = pathParts[i];
      } else if (pathParts[i] !== routeParts[i]) {
        matched = false;
        break;
      }
    }

    if (matched) return { route, params };
  }
  return null;
}

性能问题在哪里?

  • ❌ 大量的字符串操作(splitslice
  • ❌ 多层循环和条件判断,全在 JavaScript 层面执行
  • ❌ 每个请求都要重复这些操作

Hono 的解决方案:下沉到引擎层

Hono 的核心思想非常简单却极其巧妙:

将路由匹配逻辑从 JavaScript 层下沉到 JavaScript 引擎底层(C++ 实现的正则表达式引擎)

1. 预编译阶段(应用启动时)

在应用启动时,Hono 会将路由路径转换为正则表达式:

// 路由路径:/user/:id/posts/:postId
// 转换为正则:/^\/user\/([^/]+)\/posts\/([^/]+)$/

const paramNames: string[] = [];

const regexPath = path.replace(/:([a-zA-Z0-9_]+)/g, (_, paramName) => {
  paramNames.push(paramName); // 记录参数名
  return '([^/]+)';           // 替换为捕获组
});

const pattern = new RegExp(`^${regexPath}$`);

关键点:

  • :id([^/]+):匹配除 / 外的任意字符
  • 参数名被保存在 paramNames 数组中
  • 这个"昂贵"的编译过程只在启动时执行一次

2. 匹配阶段(请求到来时)

当请求到来时,只需要一行代码:

const match = pattern.exec(path);

这行代码的魔法:

  • exec 是 JavaScript 引擎的原生方法,由 C++ 实现
  • ✅ 正则引擎在底层进行了高度优化(状态机、JIT 编译等)
  • ✅ 无需手动分割字符串、无需 JS 层循环
  • ✅ 参数提取通过捕获组自动完成

3. 参数提取

匹配成功后,参数提取也非常简洁:

const params: Record<string, string> = {};
route.paramNames.forEach((name, index) => {
  params[name] = match[index + 1]; // match[0] 是完整匹配
});

完整实现解析

让我们看完整的简化实现,包含所有必要的类型定义:

// ============ 类型定义 ============

/**
 * 路由处理函数类型
 * @param params - 从 URL 中提取的参数对象
 */
type Handler = (params: Record<string, string>) => void;

/**
 * 路由信息接口
 */
interface Route {
  method: string;        // HTTP 方法(GET, POST, etc.)
  path: string;          // 原始路径模式(如 /user/:id)
  handler: Handler;      // 路由处理函数
  paramNames: string[];  // 参数名数组(如 ['id', 'postId'])
}

/**
 * 匹配结果接口
 */
interface MatchResult {
  handler: Handler;
  params: Record<string, string>;
}

// ============ 核心路由器实现 ============

export class DemoRegExpRouter {
  // 存储预编译的正则表达式和路由信息
  private routes: { pattern: RegExp; route: Route }[] = [];

  // 注册路由:预编译
  add(method: string, path: string, handler: Handler) {
    const paramNames: string[] = [];

    // 将 :param 转为正则捕获组
    const regexPath = path.replace(/:([a-zA-Z0-9_]+)/g, (_, paramName) => {
      paramNames.push(paramName);
      return '([^/]+)';
    });

    const pattern = new RegExp(`^${regexPath}$`);
    this.routes.push({ pattern, route: { method, path, handler, paramNames } });
  }

  // 匹配路由:执行正则
  match(method: string, path: string) {
    for (const { pattern, route } of this.routes) {
      if (route.method !== method && route.method !== 'ALL') continue;

      const match = pattern.exec(path); // 核心:原生正则匹配

      if (match) {
        const params: Record<string, string> = {};
        route.paramNames.forEach((name, index) => {
          params[name] = match[index + 1];
        });
        return { handler: route.handler, params };
      }
    }
    return null;
  }
}

实战示例

const router = new DemoRegExpRouter();

// 注册路由
router.add('GET', '/user/:id', (params) => {
  console.log(`获取用户详情,ID: ${params.id}`);
});

router.add('GET', '/user/:id/posts/:postId', (params) => {
  console.log(`获取用户 ${params.id} 的文章 ${params.postId}`);
});

// 匹配请求
const result = router.match('GET', '/user/123/posts/456');
if (result) {
  result.handler(result.params);
  // 输出: 获取用户 123 的文章 456
}

性能优势分析

为什么 RegExpRouter 更快?

  1. 减少 JS 执行开销:从多层循环降到一次原生调用
  2. 引擎优化:正则引擎经过数十年优化,包含 JIT、状态机等技术
  3. 减少内存分配:无需频繁创建数组、对象
  4. 一次性编译:预编译阶段完成所有准备工作,请求时零额外开销

性能对比概览

方案实现层次执行效率适用场景
传统路由JavaScript 层中等通用场景
RegExpRouter引擎底层极快边缘计算、高并发
📊 实际 Benchmark:根据 Hono 官方测试,在 Cloudflare Workers 和 Deno 环境中,Hono 是同类框架中最快的。但在 Node.js 环境中,由于适配器开销,性能优势会打折扣。

Hono 真实实现的进一步优化

本文的简化版每个路由都是独立的正则表达式。而 Hono 的真实实现采用了多路由器协同的策略:

1. SmartRouter(智能路由器)

Hono 默认使用 SmartRouter,它会:

  • 根据路由特征自动选择最快的路由器
  • 动态组合 RegExpRouter、TrieRouter、LinearRouter
  • 检测路由模式并持续使用最优方案

2. 路由器分工

路由器适用场景特点
RegExpRouter动态参数路由最快,但不支持所有模式
TrieRouter复杂路由模式支持所有模式,性能优秀
LinearRouter少量路由简单可靠,路由少时够用

3. 实际优化策略

  • 静态路由优先/about/contact 等使用 Map 快速查找
  • 路由预分组:按 HTTP 方法分组,减少匹配次数
  • 延迟编译:RegExpRouter 编译较慢,适合应用启动时初始化,不适合无服务器环境的冷启动

这些优化使得 Hono 在处理数百个路由时仍然保持极高性能。

局限性与权衡

RegExpRouter 并非银弹,在使用时需要注意以下限制:

RegExpRouter 的局限

  • 不支持所有路由模式:某些复杂的路由规则无法用简单正则表示
  • 注册阶段较慢:路由编译需要时间,不适合每次请求都重新初始化的环境(如某些无服务器冷启动场景)
  • 调试难度:编译后的正则表达式可读性较差,出错时难以定位
  • 模式约束:如带严格约束的参数 /:id(\\d+) 需要额外处理

运行环境差异

环境性能表现原因
Cloudflare Workers⭐⭐⭐⭐⭐ 极快原生 Web 标准 API
Deno⭐⭐⭐⭐⭐ 极快原生 Web 标准 API
Bun⭐⭐⭐⭐ 很快高性能 JS 运行时
Node.js⭐⭐⭐ 中等需要适配器转换
💡 选型建议:如果你的应用运行在边缘计算环境(Cloudflare Workers、Vercel Edge Functions 等),Hono 是绝佳选择。如果是传统 Node.js 服务器,Fastify 可能是更好的选择。

但对于大多数 Web 应用场景,RegExpRouter 的性能与功能平衡已经足够优秀。

总结

Hono 的 RegExpRouter 通过"预编译 + 引擎下沉"的策略,在特定环境中将路由匹配性能提升到极致:

核心优势

  1. 预编译:启动时一次性将路由转为正则,摊销成本
  2. 引擎下沉:利用 C++ 实现的正则引擎,避免 JS 层开销
  3. 参数捕获:巧妙利用正则捕获组,无需手动解析
  4. 智能组合:SmartRouter 自动选择最优路由器,兼顾性能与功能

设计哲学

这种设计哲学值得我们思考:性能优化的终极目标,往往是让"热路径"代码尽可能多地运行在更底层的优化环境中

对于 Web 框架来说,路由匹配就是最热的路径之一。Hono 在边缘计算环境中找到了这个问题的最优解,同时通过多路由器架构保证了广泛的适用性。

最佳实践

  • 边缘计算/Serverless:Hono 是首选(Cloudflare Workers、Deno Deploy)
  • 高并发场景:充分发挥 RegExpRouter 性能优势
  • ⚠️ Node.js 环境:考虑性能权衡,或选择 Fastify 等专门优化的框架
  • ⚠️ 复杂路由规则:了解 RegExpRouter 的限制,必要时使用 TrieRouter

延伸阅读:

技术资源:


📝 文章说明

本文基于对 Hono 框架的学习和研究编写,代码示例为简化版实现,用于教学目的。实际的 Hono RegExpRouter 实现更加复杂和优化。

性能数据和对比来自 Hono 官方文档和社区测试,具体数值会因测试环境、负载类型、运行时版本等因素而异。实际使用时请根据自己的场景进行测试。

如果您发现文中有任何不准确之处,欢迎指正。

(所有项目的详细介绍,都可以在我公众号搜到相应的介绍文章)

纯AI底层原理项目

文章介绍链接:
https://mp.weixin.qq.com/s/cATjUcO2uoi8Knim6ZKb5w

通过此项目,一共可以衍生出三个子项目,含金量非常之高。大家可以看看简历书写,是否感兴趣

完整项目简历


子项目---MCP server部分

子项目---整体mcp开发

子项目---a2a开发

操作系统项目

在应届生校招面试中,对基础知识的拷打,系统知识部分占据了极其重要的一环。(校招面试基础知识,一般就是拷打语言、操作系统、计算机网络、还有自己写的额外学的东西)

那这个时候,如果操作系统学习的好,学的深入,远超同龄人,那面试基本已经成功三分之一了。

那怎么说明操作系统算学习的好呢,无非就是深入底层,深入内核。 学习内核源码,尝试改编。

针对这,星球里目前有两个项目:

协程框架

一个是协程框架项目(底层语言到寄存器,操作系统hook机制,内核模块编写)

同时有详细的学习路线、学习资料、学习代码、简历书写,以及面经

如下图:

Linux性能监控项目

(和星球同学一起整理的分享给大家)

目前大家都在强调自研,自研操作系统。尤其新能源,智能座舱都在自研操作系统。

那怎么自研的,从0到1,肯定是首先要借鉴下目前好的操作系统(安卓),以及对底层的模块熟悉,会编写内核模块。

并且既然是监控项目了,肯定要对底层的一些指标进行监控,监控内核。了解要学习的中间件

以及对一些性能怎么进行测试等等。通过此项目,将会让你对操作系统的掌握,更上一层楼

同时有详细的学习路线、学习资料、学习代码、简历书写,以及面经

如下图:

计算机网络项目

作为另一个面试中被拷打的重点。大多数人对于网络的学习都是停留在传输层、应用层上。你学,他学,大家都学了,那怎么突出你掌握的深度,实现对其他人的降维打击呢。

那就往深的学,往底层的学。是不是可以学学底层协议呢,学学底层内核网络协议栈呢。

通过这个学习,你会了解内核中对协议的一些实现、以及用户态怎么与内核网络协议栈进行交互,以及怎么监控内核网络协议栈。对网络部分实现对同龄人甚至面试官的降维打击。

并且此项目也融合了AI的东西,引起了RAG技术,进行了多种RAG的实现方式。与AI结合,符合潮流

同时有详细的学习路线、学习资料、学习代码、简历书写,以及面经

如下图:


项目介绍文章:

项目介绍视频

https://www.bilibili.com/video/BV1jbx2zgE7h/?spm_id_from=333....

后端项目(AI智能云存储)

很多学生学cpp,但是又要找后端岗位、服务端开发的工作。

这个时候就需要你有crud经验,作为一个cpp选手(cpp主要就是搞底层、 嵌入式的)。证明自己有后端经验,那最好的证明就是证明自己有个后端的项目

并且很多人学cpp,也是因为时间来不及,想速成。c++最大的优势就是可以学习较少的东西,就可以做出一份很不错的简历出来,投入到找工作行列中。(用少量的时间就可以达到找工作的要求)

但是简历项目必不可少,这个时候有个简单同时也有含金量的项目至关重要。

那就可以做个后端项目,比较简单。也有含金量,之前全程辅导23/24/25届的学生,单纯用这一个项目,并且用的还是基础版本(目前进行了一次迭代,新增了使用docker、k8s一键部署,以及也增加了AI的东西),就可以找到满意的工作。

同时有详细的学习路线、学习资料、学习代码、简历书写,以及面经

如下图:

游戏项目

(和星球同学一起整理的分享给大家)

很多人学cpp可能是为了想找游戏相关的工作,但是苦于没有合适的项目,这里 给大家介绍两个项目。

一个是框架类的项目

一个是落地的项目

分布式ECS游戏后端框架

实现了一个游戏开发框架,一个黑盒子,底层框架,供游戏开发者使用。复用了很多功能

具体内容可以看下面的图片:

游戏姿势识别项目

从游戏开发应用、中间框架层、底层硬件封装、sdk调用,一条龙自主实现。

主打对整体的一个流通,可通各个层级岗位,万金油

如下图:

一站式编程平台项目

此项目主要用于为大家编程学习,提供编程练习环境。带大家从小白一步一步蜕变成编程大牛,而不是一个只会背的八股选手


qt项目

正在研发中,争取年前上线

其他收集的开源免费的基础项目

免费开源给大家,不要被一些人忽悠,拿着这些开源项目说自研,忽悠大家,忽悠钱就算了。还忽哟大家把线程池、内存池当作项目,耽误大家前程。




等等其他项目在开发中

知识星球介绍(公认的cpp c++学习地)

星球名字:奔跑中的cpp / c++

专注cpp/c++相关求职领域的辅导

加入星球福利,后续如果有其他活动、服务,不收费,不收费,可以合理赚钱就收取下星球费用,但是不割韭菜,保持初心

感兴趣的微信扫下面的码,然后下载知识星球app登录即可

(1)高质量的项目合集






同时如果项目,遇到任何困惑也会第一时间进行解答的

(2)高质量精确性八股资料


(3)详细的学习路线

(4)活跃的学习氛围,星球打卡不只是一个形式,而是每天观看,针对同学们的学习情况提出合理化的建议,同时也有高质量的星球微信内部群


(5)星球提问简历修改,提供意见的同时,还会给安排一对一腾讯会议辅导

(6)星球同学offer情况,以及对应学习情况,给大家提供参考

(7)全网最全cpp相关面经整理

(8)编程实战能力提升平台(大家都可以使用的,免费的)

访问网址 cppagancoding.top

星球同学的评价

(9)每周也会进行直播答疑,同时有时也会给星球内部同学开一些知识、路线分享会。

具体可以看B站放的视频,up名字:cpp辅导的阿甘

(10)奖励金激励,会根据大家打卡学习/ 面经打卡整理情况,每个月每个季度发放奖励金。有的人陆陆续续已经获得了数千月的奖励金,是加入星球费用的数十倍了

等等,可能还有一些其他服务,目前没想起来的,以及后续也会增加的服务

本文由mdnice多平台发布

C++什么会得到像我这样老年人的喜爱?

首先 C++这个语言表达力及其丰富,以至于初学者不知所措,经常会看到不认识的语法,这是在其它语言不太会经历到的。 但是它所有的复杂性都服务于一个目标,抽象(abstraction)。抽象是一个高级的思考过程,它试图从杂乱无章中找到模式。

不知各位有没有用过 boost json ,json 仅有几种有限的数据类型,大部分语言有类(class),用它来抽象这些数据类型也挺不错,c++也是 OOP 。 但是 C++还有 std::variant,就是说如果一个东西只可能有固定的几个类型,那么用std::variant来抽象更恰当(也可能更快,更不容易错,或者无法错)。

其它比如shared_from_this等都是为解决问题而生,如果你没有碰到问题,那么你就不会深入理解shared_from_this。它是为了在异步环境中让对象自己保持活着,不然异步回调时如果对象已经销毁,就会 UAF 。

namespace certctrl {

class UpdateHandler : public IHandler, 
                      public std::enable_shared_from_this<UpdateHandler> {
private:
  certctrl::ICertctrlConfigProvider &config_provider_;
  customio::ConsoleOutput &output_;
  client_async::HttpClientManager &http_client_;
  certctrl::CliCtx &cli_ctx_;
  std::shared_ptr<AgentUpdateChecker> update_checker_;

  // Platform detection
  std::string detect_platform();
  std::string detect_architecture();
  
  // Update workflow steps
  monad::IO<bool> check_for_updates(const std::string &current_version);
  monad::IO<bool> confirm_update();
  monad::IO<void> perform_update();
  monad::IO<std::string> download_update(const std::string &download_url);
  monad::IO<void> install_update(const std::string &downloaded_file);
  monad::IO<void> backup_current_binary();
  monad::IO<void> replace_binary(const std::string &new_binary_path);
  
  // Helper methods
  std::string get_current_binary_path();
  std::string generate_backup_path();
  bool verify_downloaded_file(const std::string &file_path, const std::string &checksum_url);

public:
  UpdateHandler(certctrl::ICertctrlConfigProvider &config_provider,
                customio::ConsoleOutput &output,
                client_async::HttpClientManager &http_client,
                certctrl::CliCtx &cli_ctx,
                std::shared_ptr<AgentUpdateChecker> update_checker);

  std::string command() const override;
  monad::IO<void> start() override;
};

}

当然这里仅仅举几个例子,每一个特性都是为解决问题而设计的。

说到为什么年长者更喜欢 c++,我估计可能和大脑的抽象能力相关,我不是脑科学专家,我还问了 chatgpt ,它的答复:

情况	结果
纯逻辑、非经验性的抽象任务(数学推理、形状类比、无语言图形测试)	年轻人通常更强
基于经验的抽象总结、模式识别	年长者可能更强
需要同时抽象 + 处理大量新信息的任务	年轻人更快
需要抽象 + 基于经验的判断	年长者表现可能更佳

所以更准确的结论应该是,经验丰富的编程者可能会选择 C++。 如果你是初学者,不要为 C++的复杂度困扰,这需要一个过程,一个进步的过程。

时光要追溯到2010年。在那之前,我一直用Visual Basic语言编写3D小游戏,乐在其中。那时我觉得Basic已经是非常简单的计算机语言了,但心中始终有一个疑问:有没有比Basic更友好、更适合少儿的编程语言呢?

于是我开始在网络上持续寻找。果然,不久后我遇到了Scratch 1.4版——那只来自美国麻省理工学院的小猫,一下子抓住了我的心。从此,我踏上了少儿编程教育的道路:2013年开办了编程培训班,2015年将Python正式纳入教学体系,到2018年时,我已积撰写了相当丰富的Python教学材料。

在教学过程中,我逐渐感到Python内置的turtle(海龟作图)模块功能有些局限。于是,我打开它的源代码文件turtle.py,仔细研究其结构,并从2019年开始,基于Python turtle模块持续开发一个更强大的扩展——Python精灵模块(sprites)。这个模块的核心是一个叫做Sprite的类,它大幅增强了海龟的功能,例如实现了像素级的碰撞检测等。如今,任何人都可以通过pip install sprites来安装并使用它。

最近几年,我将更多精力投入信息学奥赛的教学中,整日“苦思冥想”各种算法难题。2025年8月,暑假班结束后,我又开始思考另一个问题:如果C++的入门教学能像Python turtle一样直观、有趣,那不就能为中国更多青少年打开编程的大门吗?

为此,我在GitHub上搜索已有的类似成果,也尝试了一些用C或C++编写的“类turtle”库,例如小熊猫C++内置的C语言海龟作图、GoC等,甚至购买了相关教材准备投入教学。但最终,我并没有采用它们。原因何在?

小熊猫C++中的海龟作图功能,作者显然缺乏Python少儿编程的教学背景。我曾联系他,希望将命令设计得接近Python turtle的风格,但毕竟不能一直麻烦别人,后来也就作罢。GoC则为了降低输入难度,将命令简化为单个或两个字符(如pen.o),其命令集较小,功能也相对有限。它主要依赖在线环境,作者并未提供独立的编辑器(早期离线版需搭配Notepad++使用)。GoC更像为信息学奥赛选拔苗子而设计的前置课程——网上甚至有人建议一、二年级就开始学习。如果你确定要走信奥路线,这或许可行;否则,并不适合普通学生。

这里涉及一个关键的教育认知问题:并非所有孩子都适合在低龄阶段接触C++。神经科学研究表明,大脑前额叶皮层(负责逻辑、规划与抽象思维)发育较晚,通常到青春期才趋于成熟。​ 有些孩子认知发展稍晚,若过早强制学习C++这类抽象程度高的语言,容易导致挫败感,甚至产生“习得性无助”。相反,在中低年级通过图形化编程(如Scratch)进行多感官、具象化的学习,能更好地刺激大脑不同区域,促进思维灵活性和创造力的发展。等到年龄增长、认知准备更充分时,再接触C++,往往事半功倍。​ 现实中,不少学生直到高中阶段才在逻辑思维上“开窍”,这恰恰说明大脑发育有其自然节奏,教育应当顺应而非违背它。

那么问题来了:对于大多数普通学生而言,如果一二年级接触图形化编程,三四年级学习Python,那么到了合适年龄,该如何顺畅地过渡到C++?市面上是否存在一套针对普通学生、能完美衔接既有体系的C++课程?或许有,但可能不公开或需付费。无论如何,我决定亲手打造一个——“金窝银窝,不如自己的草窝”。

首先面临的是技术选型。如果基于OpenGL,虽然强大,但学习成本较高;我也尝试过EasyX,并做出了原型,但因其底层控制不足而放弃;之后考虑过raylib(基于SDL2封装)和SFML,它们功能丰富,但封装程度较高,不利于我深入底层实现教育定制化的需求。最终,我选择了SDL2——这是一个工业级的跨平台库,接口相对底层,自由度大,掌控力强,正好符合我的开发理念。

于是,我以SDL2为基础,开始了漫长的开发与调试。最初叫它“C++ Sprites库”,后来正式定名为“C++精灵库”。为降低使用门槛,我还专门开发了配套的pxC++编辑器,并制作了Dev-C++ 5.11的升级包,使其能更好地融入中小学现有的C++教学环境。

如今,C++精灵库不仅完整继承了Python turtle的简洁API与教育基因,更在其基础上进行了优化与扩展,比如:

· 新增fill命令,支持区域填充;

· 通过函数重载,使pencolor等命令既支持字符串参数,也支持RGB/整数参数,更加灵活;

· 设计penshade(阴影度)、pensat(饱和度)、penvalue(明度)、penhsv(HSV色彩模型)、penalpha(透明度)等色彩控制方法;

· 加入贝塞尔曲线与样条曲线绘制功能,让有美术天赋的学生也能轻松创作复杂图形。

本质上,C++精灵库是Logo语言教育理念在C++领域的延续与升级。它借鉴Python turtle的友好界面,并依托SDL2的工业级能力,为学生搭建了一座从趣味编程通向真实开发的桥梁。你可以把Python turtle和C++精灵库看作一对“亲兄弟”——无论先学哪一个,再学另一个时都会产生“似曾相识燕归来”的亲切感。这种一脉相承的设计,实质是一种“双倍赋能”:既降低了学习新语言的心理门槛,又让学生在潜移默化中理解编程底层的共通逻辑。

正因为如此,当我让它们在外观和命令上如此相似时,请不要惊讶。更有价值的是,由于C++精灵库直接基于SDL2开发,学生可以在掌握基础作图后,无缝接入SDL2的更高级功能,进而探索游戏开发、交互媒体等更广阔的应用场景。这种从教育到实战的平滑过渡,是其他同类C++图形库难以比拟的。

这条路,我还会继续走下去。只愿这只从“海龟”蛋里孵出的“精灵”,能飞入更多中国少年的编程梦中,陪伴他们从好奇走向热爱,从图形走向算法,从今天走向未来。

佬们,做了个 agent 管理监控工具,c++ 开发,目前只支持 mac,agent 支持 opencode、claude code 和 codex 感兴趣可以看看



📌 转载信息
转载时间:
2026/1/21 22:25:51

经常看到有人问数据库连接工具,我平时一直在用 jookdb,比较简单轻便,也可以支持单元格复制和数据迁移。
但它商业版对连接数有限制(我这边常用场景会超过 10 个连接),而开源版我也没法顺利打包跑起来,所以干脆自己在 jookdb 开源项目基础上动手做了个分支版:OpenDBKit

先声明:项目还很原始,Bug 和不完善的地方肯定不少,更像我自用的半成品,发出来主要是求建议/求拍砖。

GitHub:
https://github.com/jsnjfz/OpenDBKit

目前大概能干啥

  • 多连接管理 + 资源树(库/表/常用操作)
  • SQL 查询 + 结果展示
  • 表数据浏览/筛选/排序/复制,支持直接编辑
  • 一些基础的表结构查看/编辑(还在补齐)
  • 导出 CSV/TSV/XLSX (够用但不算完善)

我是怎么写出来的

  • 基于:jookdb 开源代码(感谢原作者)
  • 实现方式:全程是 Codex + Claude Code 的 vibe coding

现状与致歉

必须坦诚地告诉大家,目前的版本还非常原始。

因为主要是靠 AI 生成,代码风格可能不够统一,逻辑上也存在不少 bug 和待优化的地方。它现在能跑通基本的连接和查询,但离“成熟的生产力工具”还有很长的路要走。

目前的特点:

  • 基于 JookDB:继承了其清爽的界面和 Qt 的高性能。
  • 完全开源:移除了原版的构建限制,旨在打造一个自由的社区版本。
  • 轻量级:启动速度快,内存占用低(得益于 C++)。

求反馈

由于代码还比较“稚嫩”,如果大佬们在看源码时发现写法奇怪的地方,请轻喷😂,也欢迎提 PR 帮我(和 AI )修修 Bug 。

另外提醒:不建议拿它对生产库做高风险操作,重要数据先备份、能只读就只读。

感谢 🙏


📌 转载信息
原作者:
jsnjfz
转载时间:
2025/12/30 10:15:27

视频截图

课程目录:

课时1 1 C++程序设计概述 34:54
课时2 2.1 C++的数据类型1 31:21
课时3 2.2 C++的数据类型2 16:45
课时4 2.3 C++的运算符 45:42
课时5 3.1 C++数据的输入与输出 41:13
课时6 3.2 C++的流程结构概述 17:36
课时7 3.3 if选择结构应用 10:23
课时8 3.4 switch选择结构 31:19
课时9 3.5 循环结构概述 27:34
课时10 3.6 循环结构应用实例1 14:05
课时11 3.7 循环结构应用实例2 07:50
课时12 3.8 循环结构应用实例3 12:20
课时13 3.9 循环结构应用实例4 09:44
课时14 4.1 函数的定义 试看 19:24
课时15 4.2 函数的嵌套调用 22:08
课时16 4.3 函数的递归调用 11:25
课时17 4.4 内置函数 06:07
课时18 4.5 函数的重载 06:14
课时19 4.6 函数模板 11:18
课时20 4.7 有默认参数的函数 13:18
课时21 4.8 变量的作用域 03:44
课时22 4.9 变量的存储类别 13:52
课时23 5.1 一维数组的定义与引用 试看 41:44
课时24 5.2 二维数组的定义与引用 20:14
课时25 5.3 一维数组名作实参 26:13
课时26 5.4 二维数组名作实参 10:00
课时27 5.5 字符数组操作 36:33
课时28 5.6 字符串类string 06:57
课时29 5.7 string应用举例 15:40
课时30 6.1 指针变量的定义 17:24
课时31 6.2 指针变量的应用 19:34
课时32 6.3 指针与一维数组 21:22
课时33 6.4 指针与二维数组 15:33
课时34 6.5 函数指针变量 18:04
课时35 6.6 指针数组与指向指针的指针 15:26
课时36 6.7 const 指针 22:19
课时37 6.8 引用 11:54
课时38 7.1 结构体变量的定义 试看 22:58
课时39 7.2 结构体变量应用举例 13:49
课时40 7.3 结构体构成单链表 22:37
课时41 7.4 结构体元素作为参数传递 15:23
课时42 7.5 动态存储分配 19:02
课时43 7.6 共用体 11:24
课时44 7.7 枚举型的定义与应用 30:38
课时45 8.1 类和对象概述 42:05
课时46 8.2 类和对象分析 试看 19:45
课时47 8.3 类和对象应用举例 10:45
课时48 9.1 构造函数的定义 14:19
课时49 9.2 带参数的构造函数 11:19
课时50 9.3 析构函数的定义与应用 11:45
课时51 9.4 对象的赋值与复制 30:10
课时52 9.5 静态成员 37:33
课时53 9.6 友元 27:03
课时54 9.7 类模板的创建与应用 17:33
课时55 9.8 类和对象应用举例 33:54
课时56 10.1 运算符重载的定义与应用 试看 27:32
课时57 10.2 友元运算符重载 06:59
课时58 10.3 重载双目运算符 15:00
课时59 10.4 重载单目运算符 29:56
课时60 10.5 输入输出流的重载 25:50
课时61 11.1 继承与派生的定义 试看 26:28
课时62 11.2 派生类的构造函数与析构函数 12:29
课时63 11.3 有子对象的派生类构造函数 11:08
课时64 11.4 多层派生时的构造函数 12:38
课时65 11.5 多重继承产生的二义性 28:33
课时66 11.6 虚基类的应用 24:16
课时67 12.1 多态的引出 试看 17:47
课时68 12.2 虚析构函数 21:57
课时69 12.3 纯虚函数与抽象类经典案例1 24:22
课时70 12.4 纯虚函数与抽象类经典案例2 45:38
课时71 13.1 标准输入输出流 试看 31:40
课时72 13.2 对ASCII文件的输出操作 26:17
课时73 13.3 对ASCII文件的读取操作 14:08
课时74 13.4 对ASCII文件的操作实例 25:13
课时75 13.5 对二进制文件的读取操作 21:07
课时76 14.1 异常的定义与应用 试看 28:04
课时77 14.2 在函数嵌套的情况下使用异常处理 19:04
课时78 14.3 在异常处理中处理析构函数 21:17
课时79 14.4 命名空间 14:21

[tinl2v]
解压密码:
www.eenot.com
[/tinl2v]