AI+系列:AI时代,AI给的代码有坑,咋整-《你的“数据工厂”引来了狼:当一千个机器人要同时看图时——协同编辑的血泪进化史》- 三-1-(9):
@[TOC]AI+系列:AI时代,AI给的代码有坑,咋整-《你的“数据工厂”引来了狼:当一千个机器人要同时看图时——协同编辑的血泪进化史》- 三-1-(9):
AI代码时常有坑,在AI时代,我们需要培养自己透过现象看本质的功力,如果只是一味的复制粘贴AI给的代码,而没有识别出AI给的代码有没有什么问题、有没有什么坑、有没有什么优化的点,这是一件风险很大的事。AI泛滥,系统、精准、可追溯的底层知识,反而变得极其稀缺,溢价更高。
—片尾有彩蛋哦
(AI升级篇)你的“数据工厂”引来了狼:当一千个机器人要同时看图时——协同编辑的血泪进化史
推荐语: 看不懂OT和CRDT?没关系。这是一个关于“如何在1000个机器人同时画画时,不让它们打起来”的热血故事。从最笨的“排队锁”到最聪明的“无冲突算法”,我们用造机器人的逻辑,帮你彻底搞懂现代协同技术的核心。
成功带来的新“灾难”
自从你的“AI数据工厂”V4版本上线,你给他们生成的几万张带标注的3D零件图,直接把他们机器人的抓取成功率从70%干到了95%。
老板很开心,给你发了奖金。你以为可以歇两天了。
结果,麻烦找上门了。
一天下午,你正悠闲地喝着咖啡,看着屏幕上你的数据工厂正在吭哧吭哧地渲染一张张法兰盘图片。突然,企业微信狂闪,是机器人部署组的负责人老王。
“小C,江湖救急!!!”
“咋了王哥,数据不够?我让工厂再给你渲一万张?”
“不是数据的事!我们把模型部署到工厂产线上了,但出大事了!”
原来,他们训练了一个超级视觉大模型,部署在中央服务器上。产线上有几十个高清摄像头,从不同角度同时拍摄流水线上的螺栓,然后把视频流实时传给这个模型做检测。
他们最初的方案,就是你在V1版本时也会想到的笨办法:
- 摄像头A、B、C同时拍到了一颗螺栓。
- 它们都把图片扔给服务器。
- 服务器上的单个模型实例忙不过来,就简单粗暴地排队。A先处理,B和C等着。
- 处理结果返回:A说“这是坏螺栓”,B说“这是好螺栓”。但等B的结果返回时,螺栓已经随着流水线走远了,机械臂抓了个空。
“小C,这完全没法用啊!我们需要多个机器人共享一个‘大脑’,同时看、同时想、同时动,而且想法还不能打架!”
你听完,手里的咖啡顿时不香了。这哪是简单的数据生成问题,这分明是计算机科学领域最经典的难题之一——分布式协同一致性问题。
你看着自己写的那个完美的单机版“数据工厂”,突然意识到,真正的挑战现在才开始。你不仅仅要造一个“会看”的机器人,更要造一个“一群机器人一起看、一起商量、绝不内讧”的智慧群体。
于是,你再次踏上了征途。
🧬 V1:最原始的想法——“全量覆盖”与“抢麦克风”
【故事·演进逻辑】
老王的需求很直接:多个摄像头(我们叫它们“智能体”或者“客户端”吧)要把自己看到的结果,同步到一个共享的状态里。这个状态,比如,就是一张虚拟地图,上面标注了每个螺栓的实时位置和状态。
你最先想到的法子,和你当初写第一个3D查看器时一样简单粗暴。
方案一:无锁覆盖。 谁最后一个把结果告诉服务器,服务器就用谁的结果刷新整个地图。
- 结果: 灾难。摄像头A刚报告“1号螺栓在坐标(10, 20)”,摄像头B紧接着报告“2号螺栓在坐标(30, 40)”。但因为网络延迟,B的报告后到,它直接覆盖了A的报告。在服务器的地图上,1号螺栓凭空消失了。机械臂按照这个地图去抓,直接撞到了1号螺栓上。数据完全丢失,这就是协同的噩梦。
你赶紧改进。
方案二:悲观锁。 你给整个地图加了一把大锁。就像一个麦克风,谁拿到谁说话。
- 摄像头A想更新地图?可以,先向服务器申请“麦克风”。服务器说“好,给你”。在A拿着麦克风期间(比如20毫秒),其他所有摄像头(B、C、D)只能干看着,它们的更新请求全部被驳回。
- A用完,释放“麦克风”,B、C、D再开始抢。
- 结果: 碰撞是避免了,但产线直接慢成了幻灯片。几十个摄像头大部分时间都在排队等锁,真正的“实时性”荡然无存。老王说:“你这系统还不如我手动一个个看呢。”
⚠️ AI 在此处的“弱智”坑点:异步网络下的“状态倒退”
如果你现在问AI:“嘿,帮我写个简单的Python WebSocket服务,让多个客户端能一起更新一个共享的JSON对象。”
AI会毫不犹豫地给你吐出如下代码逻辑,并且100%会写错:
# AI 生成的典型错误代码逻辑 shared_state = {} async def handler(websocket): async for message in websocket: data = json.loads(message) # 错误点1:直接覆盖。AI无法理解“部分更新”和“全量覆盖”的区别 shared_state.update(data) # 错误点2:盲目广播。它把刚从A收到的状态,直接原封不动发给B await broadcast(json.dumps(shared_state))AI为什么必然在这里翻车?
AI的思维是线性的、理想的。它无法在脑海中模拟并发和网络延迟这两个魔鬼。
- 历史翻车现场:
t=0s,客户端A发来{"bolt_1": "ok"}。服务器状态变为{"bolt_1": "ok"},并广播给所有人。t=0.1s,客户端B发来{"bolt_2": "defect"}。t=0.15s,由于网络延迟,A才收到B在t=0.1s时的广播。但此时,A在t=0.12s又本地更新了,发来了{"bolt_1": "recheck"}。- 服务器收到A的
recheck,广播出去。- 灾难发生: A自己收到了服务器广播的
{"bolt_1": "recheck"},而这个状态里根本没有B刚报告的bolt_2!于是,A的本地地图上,bolt_2的状态又回滚到了旧值,甚至直接消失。这就是状态倒退。AI写的代码里完全没有“版本”和“因果关系”的概念,它只会用最新的消息生硬地覆盖一切。用这种代码,你的机器人群体迟早会精神分裂。
🚄 V2:第二代演进——操作变换(OT),“不传结果,传指令”
【故事·演进逻辑】
“全量覆盖”和“悲观锁”都完蛋了,你开始查阅资料。你发现了Google Docs等实时协作文档背后的早期核心技术——操作变换(Operational Transformation, OT)。
OT的核心思想很美:我们不传递最终的地图状态,我们只传递改变地图的“操作指令”。
还是那个螺栓地图的例子。现在,摄像头A和B不再说“地图现在是xxxx”,而是说:
- A说:“在ID为
bolt_1的条目之后,插入新螺栓bolt_3。” - B说:“删除ID为
bolt_2的条目。”
服务器收到这些指令后,不是简单地执行,而是要根据指令到达的顺序,进行数学变换,确保每个人执行后得到的地图都完全一致。
你看懂了一个经典的OT变换例子:
- 服务器认为文档(地图)原始状态是
[bolt_1, bolt_2]。 - 客户端A想在位置1(
bolt_1后)插入bolt_3。指令A:Insert(1, bolt_3)。 - 客户端B想删除位置0(
bolt_1)的螺栓。指令B:Delete(0)。 - 服务器先收到了A的指令,执行后状态变为
[bolt_1, bolt_3, bolt_2]。 - 然后,服务器收到了B的指令。关键点来了! B想删除位置0的螺栓。如果服务器傻傻地在当前状态
[bolt_1, bolt_3, bolt_2]上直接执行Delete(0),删除的仍然是bolt_1,这是正确的。 - 但如果是并发的呢? 如果A和B同时发给对方,没有经过服务器。A收到B的
Delete(0),在自己原始状态[bolt_1, bolt_2]上执行,删掉了bolt_1。然后A再执行自己的Insert(1, bolt_3),得到[bolt_2, bolt_3]。而B这边,B先执行自己的Delete(0),状态变为[bolt_2],然后B收到A的Insert(1, bolt_3),执行后得到[bolt_2, bolt_3]。 - 奇迹发生了,两边状态一致了!这就是OT的魔力,它通过动态调整操作的参数(比如把
Insert(1...根据先执行的Delete(0)变换成Insert(0...)),来保证最终一致性。
你花了一个通宵,用白板推演了各种插入、删除、修改的组合,感觉这个数学模型简直完美无瑕。
但是,当你试图把它写成代码时,你差点把键盘砸了。因为你发现,这玩意儿太™复杂了!
- 组合爆炸: 你不仅要处理“插入”和“删除”的并发,还要处理“修改属性”(比如把螺栓状态从“OK”改成“缺陷”)的并发。每增加一种操作类型,需要处理的并发冲突组合就指数级上升。
- 数学地狱: 你必须保证你的变换函数
transform(op1, op2)满足TP1和TP2这两个天杀的数学性质。稍微漏掉一种边界情况,比如“A和B同时删除同一个螺栓,但A在删除后又立即在同一个位置插入了新螺栓”,你的系统就会在沉默中产生错误,并把这个错误像病毒一样传播给所有后续的操作,最终导致所有人的地图状态彻底分叉(Divergence),再也合不拢。
⚠️ AI 在此处的“灾难”坑点:无法通过形式化验证的“幻觉收敛”
如果你让AI:“请用JavaScript实现一个OT算法,能处理文本的并发插入和删除。”
AI会非常自信地给你生成一个看起来极其专业的函数,大概长这样:
// AI 生成的典型错误 OT 函数 function transform(op1, op2) { if (op1.type === 'insert' && op2.type === 'insert') { if (op1.position <= op2.position) return op1; else return { ...op1, position: op1.position + 1 }; } else if (...) { ... } // ... 一连串复杂的if-else }AI为什么必然在这里写错?
因为编写正确的OT算法,本质上是在做数学证明,而不是写工程代码。AI只能从GitHub上学习代码的“形状”,但无法理解其背后的形式化逻辑。
- 历史翻车现场: AI写的代码,在处理“A在位置5插入,B在位置5删除”这种标准冲突时,看起来没问题。但如果你问它:“如果A在位置5到10删除了6个字符,而B同时在位置8插入了一个字符,会发生什么?”
- AI 100%会算错最终的偏移量。它不是多算1,就是少算1。而这个“1”的误差,在OT的链式状态依赖下,会被无限放大。因为文档的每一个后续状态,都是基于前一个状态正确变换而来的。一步错,步步错,最终整个文档都会变成乱码。
- AI缺乏数学完备性思维,它根本无法穷举并证明其
if-else覆盖了所有并发冲突的边界情况。它生成的OT代码,在真实的生产环境压力测试下,活不过三秒。
🌳 V3:第三代演进——初代CRDT,“给每个字发个永不磨灭的身份证”
【故事·演进逻辑】
OT这条路,走得你心力交瘁。你感觉自己不像个工程师,倒像个在证明哥德巴赫猜想的数学家。
在一个深夜,你把OT的白板笔一摔,怒吼道:“老子不玩这相对位置的数学游戏了!我要玩绝对位置!”
你的新思路,就是后来被称为 CRDT(无冲突复制数据类型) 的雏形。
核心思想极其简单粗暴:
- 不再说“在
bolt_1后面插入”。 - 而是,当系统诞生时,就给地图上的每一个可能的元素,都分配一个全球唯一且永不改变的身份证(ID)。
- 这个ID怎么来的?可以是你设备唯一ID + 一个单调递增的时钟,保证了绝对的全球唯一。
- 地图的状态,不再是一个数组
[bolt_1, bolt_2],而是一个包含了所有“有身份证元素”的集合,每个元素都带着它的“身份证”和“我排在谁后面”的信息。
比如,你想删除bolt_1,你不是真的把它从集合里删掉。你只是在bolt_1的元素上,盖一个“已删除(墓碑)”的戳。这样,即使另一个摄像头在你删除之后,发来了一个“在bolt_1后面插入bolt_3”的迟到指令,系统依然能找到bolt_1这个锚点,把bolt_3正确地接上去。
这完美地解决了OT的并发冲突问题! 因为每个人都在一个基于全球唯一ID的集合上操作,而这些操作(增、删、改)都是可以交换顺序的(满足交换律、结合律、幂等性)。不管你以什么顺序执行这些带身份证的操作,最终的结果都完全一样。这叫强最终一致性。你不再需要那个该死的中央服务器来当裁判了,P2P(点对点)协同成为了可能!
你兴奋地把这个“身份证+墓碑”的系统实现了出来,老王他们一试,果然再也没出现过状态分叉。
但好景不长。部署上线跑了没两天,运维就找来了。
“小C,你这程序干嘛呢?才跑了俩小时,吃了50个G的内存!”
你一拍大腿:“坏了!墓碑!”
⚠️ AI 在此处的“隐蔽”坑点:生产环境直接OOM的“内存泄露型”CRDT
如果你对AI说:“帮我在Go里实现一个基于LWW(最后写入胜出)的CRDT Map,用于协同编辑。”
AI会很高兴地给你写出一个结构清晰、逻辑正确的实现。它会严格地实现
delete操作:即在map的entry里设置一个is_deleted: true的标记,而不是真的删掉它。AI为什么必然在这里写错?
AI只管逻辑正确,不管资源上限。它在写CRDT时,100%会忽略“垃圾回收”和“墓碑裁剪”。
- 历史翻车现场: 在你那个螺栓产线的场景里,摄像头每秒钟要产生成百上千次状态更新。一个螺栓从“出现”到“被取走”,状态变化可能只有几十秒。
- 但是,AI写的CRDT会忠实地保留每一个螺栓的所有历史状态和最终的那个“已取走”的墓碑。一个小时,两个小时……内存里堆满了成百上千万个带着UUID和墓碑的“螺栓幽灵”。
- 这些墓碑删又不能真删,因为理论上可能有一个离线的摄像头,三天后突然连上来,发了一个“在三天前的那个螺栓后面再插入一个”的指令。为了这理论上的“因果完整性”,你必须保留所有墓碑。
- AI根本不会写、也写不好复杂的状态向量(Version Vector)裁剪算法和安全垃圾回收机制。它给你的代码,就是一个逻辑上完美、工程上会导致内存溢出(OOM)的定时炸弹。
🚀 V4:最新一代——现代高性能CRDT,“不仅是身份证,还是压缩饼干”
【故事·演进逻辑】
面对内存爆炸,你只能继续进化。你找到了现代CRDT的经典实现,比如 Yjs 和 Automerge 背后的思想。它们就是第四代,也就是我们现在用的终极形态。
你发现,它们之所以能解决内存问题,是因为做了几个颠覆性的优化:
- 从“单字符”到“数据块”(Chunking): 不再给每个状态变化都发一个带UUID的身份证。而是把连续的状态更新(比如一个螺栓在10秒内的轨迹),打包成一个“块(Item)”。只给这个块一个身份证,内部是紧凑的二进制数据。
- 列式压缩(Columnar Encoding): 不再用JSON这样的文本来存ID、时间戳、数据。而是像列式数据库一样,把所有条目的“ID”存一列,所有“时间戳”存一列,然后用高效的二进制格式压缩。几百兆的元数据,瞬间能压成几十K,网络传输无压力。
- 聪明的墓碑清理: 引入了更复杂的依赖追踪。系统能智能地判断,某个“螺栓幽灵”的墓碑已经不再被任何其他“活着”的数据所需要了,然后就可以安全地、彻底地把它从内存和磁盘中抹去。
你把底层的协同引擎从自己写的“内存炸弹”,换成了成熟的 Yjs 库。整个世界清净了。内存占用稳如老狗,同步速度快如闪电。
但是,当你试图把Yjs和你的前端3D可视化界面(比如一个用Three.js做的实时螺栓位置图)绑定时,新的问题又来了。
⚠️ AI 在此处的“工程”坑点:死循环与不可逆的“编辑器状态雪崩”
虽然你现在用的是成熟的Yjs库,但你得写胶水代码把它和你的应用状态绑定。这时,你如果偷懒去问AI:
“写一段React Hook,把Yjs的Y.Map实时同步到我的本地State,并且保证任何一边的修改都能同步到另一边。”AI会掉进协同编程里最经典的陷阱——事件回环。
AI为什么必然在这里写错?
AI无法理解多源事件驱动的因果关系。它在处理“双向绑定”时,逻辑必然混乱。
- 历史翻车现场:
- 你在3D界面里拖拽了一个螺栓。
onDrag事件触发,AI写的代码把这个新坐标{x:10, y:20}应用到Yjs的Y.Map里。- Yjs检测到本地更新,触发了一个
observe事件,通知所有监听者:“嘿,地图变了!”- 而AI写的代码恰好就在监听Yjs的
observe事件,目的是为了同步远程更新。它收到这个事件后,不管三七二十一,又把{x:10, y:20}写回到你的本地State里。- 本地State一更新,又可能触发
onDrag或类似的事件……或者更直接地,Yjs的更新-监听-再更新,瞬间形成一个无限死循环(Maximum call stack size exceeded)。就算AI聪明一点,加了个
if (origin !== 'local')来判断,防住了死循环。但更隐蔽的坑是光标和选择状态(Selection)的丢失。当远程的一个更新插入了新数据,AI写的绑定代码可能会粗暴地重绘整个界面,导致你正在拖拽的螺栓突然松手,或者你正在查看的菜单突然关闭。AI根本无法精准地处理这种需要精细控制的UI状态同步。
💡 总结:你,该如何驾驭AI,造出真正的群体智能?
故事讲到这里,你从单机版的“数据工厂”,一步步深入到了多机协同的核心。你发现,AI可以帮你写一个“能跑”的Demo,但在构建可靠的、生产级的分布式系统时,它充满了逻辑盲区。
这也正是你这个“AI数据工厂”进化的终极意义:
你不仅要教会一个机器人看世界,更要教会一群机器人如何一起看世界,并且拥有一个共同的、一致的、永不分裂的“群体意识”。
如果你想用AI来帮你写这部分的代码,记住,绝不能再问“帮我写个协同编辑”。你必须像一位首席架构师一样,用精准的“防御性提示”来约束它,堵死它所有的弱智退路:
你可以这样向AI提问:
“请帮我写一段TypeScript代码,用于将 Yjs 的
Y.Map状态同步到一个 React 组件的本地useState中,并且支持双向绑定。请注意:
- 死循环防护: 必须通过检查
Y.Map事件中的transaction.origin属性,严格区分操作是来自本地还是远程。严禁出现因事件回环导致的双向绑定死循环(Maximum call stack size exceeded)。- 精确更新与性能: 在应用远程变更时,不能粗暴地全量覆盖本地State。必须使用细粒度的更新(例如,只更新变化的键值对),以避免不必要的组件重渲染,并防止用户正在进行的UI交互(如拖拽、文本选择)被中断或重置。
- 冲突解决的可观测性: 提供一个中间件或回调机制,当发生并发的属性修改冲突时(例如,两个客户端同时修改了同一个螺栓的状态),能捕获并记录这个冲突事件,并明确告知用户系统是如何根据CRDT的规则(如最后写入胜出LWW)来解决的。请证明你的代码没有引入状态分叉的风险。”
解决方案:
- 只要丢给它一个工业 CAD 模型(比如 STL文件),它就能自动在虚拟空间中 360° 环绕拍照,瞬间吐出:
- 📸 RGB 真实渲染图:rgb/frame_XXXX.png
- 🏷️ 像素级语义分割 Mask (基于曲率算法,自动认出哪里是螺栓、孔洞、法兰):mask/mask_XXXX.png
- 📏 深度图(Depth) (告诉机器人距离多远),depth/depth_XXXX.png + .raw
- 📐 6DoF 相机位姿 (告诉机器人从哪个角度抓),camera_poses.json
- 📂 最后直接打包成 AI 训练最爱吃的 COCO/YOLO 格式。
- label_legend.txt【类别ID→名称→RGB颜色映射】、description.json【DeepSeek-V3 视觉API生成零件特征描述】
实际效果:
- 想看视频:
huhb_synthetic_data
- 不想看视频:也有图片:












【还有附带的:camera_poses.json、label_legend.txt、manifest.json,具体内容见附录】
巨人的肩膀:
- OpenGL 4.6 Specification
- Vulkan 1.3 Specification
- Khronos Group SPIR-V Whitepaper
- 历代GPU架构白皮书(NVIDIA Fermi至Blackwell,AMD GCN至RDNA 4)
AI时代,除了刷题,怎样能从一道题里榨出架构能力、底层知识、出题人视角以及与工作结合的价值…
你是不是做过或者刷过:
• LRU 缓存:实现哈希链表,LinkedHashMap。CRDT中常需用类似结构追踪最近访问或变更的图元ID,这种复合数据结构的设计思路值得学习。
#include <unordered_map>
using namespace std;
// 定义双向链表节点
struct DListNode {
int key, value;
DListNode* prev;
DListNode* next;
DListNode(int k, int v) : key(k), value(v), prev(nullptr), next(nullptr) {}
};
class LRUCache {
private:
int capacity; // 最大容量
int size; // 当前大小
unordered_map<int, DListNode*> map; // 哈希表:key -> 节点
DListNode* head; // 伪头结点(最近使用)
DListNode* tail; // 伪尾结点(最久未用)
// 将节点添加到头部
void addToHead(DListNode* node) {
node->prev = head;
node->next = head->next;
head->next->prev = node;
head->next = node;
}
// 移除指定节点
void removeNode(DListNode* node) {
node->prev->next = node->next;
node->next->prev = node->prev;
}
// 将节点移到头部(表示最近使用)
void moveToHead(DListNode* node) {
removeNode(node);
addToHead(node);
}
// 移除尾部节点(最久未用),并返回该节点
DListNode* removeTail() {
DListNode* node = tail->prev;
removeNode(node);
return node;
}
public:
LRUCache(int capacity) {
this->capacity = capacity;
this->size = 0;
head = new DListNode(0, 0);
tail = new DListNode(0, 0);
head->next = tail;
tail->prev = head;
}
int get(int key) {
if (map.find(key) == map.end()) return -1; // 不存在
DListNode* node = map[key];
moveToHead(node); // 存在则移到头部
return node->value;
}
void put(int key, int value) {
if (map.find(key) == map.end()) {
DListNode* newNode = new DListNode(key, value);
map[key] = newNode;
addToHead(newNode);
size++;
if (size > capacity) {
DListNode* removed = removeTail();
map.erase(removed->key);
delete removed;
size--;
}
} else {
DListNode* node = map[key];
node->value = value;
moveToHead(node);
}
}
};
一、这道题和工作的隐秘联系
做 CAD 多端实时协同,最核心的挑战是什么?状态同步 + 缓存管理。
想想看:
- 用户的图纸可能很大,但屏幕上只显示当前视口的内容——这不就是一个"最近最常用"的缓存问题吗?
- 多个用户操作同一个图纸,哪些图元数据应该保持在内存里?哪些可以惰性加载?这就是 LRU 的变体。
- 你的协同冲突检测,需要快速判断一个图元是否被锁定/修改——哈希表 + 双向链表的结构正好是高效查找 + 有序维护的经典组合。
这道题不是一道面试题,它是你未来设计"图纸图元缓存管理器"的原型。
二、按AI时代的 8 条思路,把这道题"吃透"
1. 当出题人:重新定义问题边界
不要满足于"AC 了这道题"。问自己:
“LRU 对 CAD 场景真的够好吗?”
CAD 图纸里有些图元虽然"最近没用",但它是重要参照(比如图层基线),不应该被踢出缓存。这时候,你能不能设计一个加权的 LRU?——给关键图元加"保护权重",普通图元用纯 LRU。
你从解题者变成了出题人:定义了"图纸缓存淘汰"这个更有价值的问题。
2. 技术人的切入点:从 LRU 看到更深的模式
这道题的 map + 双向链表 结构,其实揭示了一个通用模式:
"你需要 O(1) 查找 + O(1) 插入 + O(1) 删除 + 维护顺序"时,就是哈希表配链表的舞台。
联想你的工作:
- 图元的显示顺序(z-order)可以用这个结构维护
- 操作历史(undo/redo)可以用双向链表,结合哈希表快速定位到某一步
- 协同编辑的"游标位置表"可以用类似结构管理
你不是学了一道题,而是学到了一个"设计模板"。
3. 技术够用就行:你不需要手写所有变体
这道题的代码,AI 确实 3 秒能生成。但 AI 不会替你决策:
- 你的 CAD 缓存容量应该设多大?取决于图纸复杂度和设备内存——这需要你根据业务定义
- 淘汰策略选 LRU 还是 LFU?选纯 LRU 还是加权 LRU?——这是架构决策
- 关键路径上,
removeTail能不能用内存池优化,避免频繁 new/delete?——这是性能洞察
技术够用,是指你能"调用"这些知识做决策,而不是非得徒手写出来。
4. 架构能力:把 LRU 放进你的系统蓝图
现在画一张图:你的 CAD 协同插件,哪些地方需要"缓存-淘汰"机制?
可能的答案:
- 图元数据缓存(LRU)
- 网络同步包缓冲(环形队列,本质也是"满则淘汰")
- 渲染显示列表(视口内图元的快速索引)
- Undo/Redo 栈(深度限制,旧的丢弃——也是一种 LRU)
当你能把这些串起来,你就不是"会写 LRU 的人",而是"理解缓存无处不在的架构师"。
5. 掌握底层:这道题里的"坑"和"美"
你贴的代码看起来没问题,但底层细节值得深挖:
- 内存管理:频繁
new/delete会不会造成内存碎片?能否用对象池优化? - 并发安全:如果多线程同时
get/put,这段代码立刻崩溃。要加什么锁?读写锁还是分段锁?——这直接对应你协同编辑中的线程模型。 - 数据结构之美:为什么用"伪头尾节点"?它消除了所有边界判断——
addToHead不需要检查 head 是否为空。好的设计把特殊情况消灭在结构里。
下次让 AI 生成 LRU 代码,你能一眼看出它有没有伪头尾节点,判断它是否优雅。这就是"看 AI 给的美不美"。
7. 机会更多:这道题是你的"跳板"
LRU 的思想可以迁移到:
- 操作系统:页面置换算法(你懂 LRU,就能很快理解 Linux 的 Clock 算法、LRU-K 等)
- 数据库:Buffer Pool 管理(MySQL 的 Buffer Pool 用的就是 LRU 变体——分代 LRU)
- 前端:Keep-Alive 组件缓存(Vue 的 keep-alive 默认用 LRU 控制缓存组件数)
有了这道题做锚点,你转去学数据库、操作系统、前端,都能找到"熟悉感"。
8. 看 AI 给的对不对、美不美
现在,打开 ChatGPT,让它"用 C++ 实现线程安全的 LRU Cache"。你会收到一堆代码。然后你审视:
- 锁的粒度对吗?是不是 put 整个函数一把大锁,导致高并发下性能极差?
- 有没有用
std::shared_mutex实现读写分离? - 淘汰策略是否可插拔?(策略模式——如果让你设计,你会把淘汰策略抽象成接口吗?)
你能批判 AI 代码的那一刻,你就不再是 AI 的使用者,而是 AI 的指挥者。
用出题人视角定义问题,用架构能力嵌入系统,用底层知识审查AI,用软实力表达决策。AI时代,这种“吃透一道题”的能力,才是你不可替代的价值。
系列文章规划:
- ((AI升级篇)OpenGL渲染与几何内核那点事-(二-1-(14):你的3D查看器,是怎么一步步先试着造个数据工厂,向学会“教”机器人看世界的而努力)
- (让 C++ 程序长出大脑:从“语音遥控器”到具身智能 Agent 的进化之路)------OpenGL渲染与几何内核那点事------(二-1-(15))
别再喂垃圾数据了!从3D查看器到AI数据工厂,一位工程师的“数据观”进化四重奏)------OpenGL渲染与几何内核那点事------(二-1-(16)) - AI+系列:AI时代,AI给的代码有坑,咋整-《如何把玩分布式里面的协同》- 三-1-(8)
- AI+系列:AI给的代码有坑,咋避坑系列-《当你的数据工厂被AI代码坑到怀疑人生——递归与浮点数的避坑指南》- 三-1-(7)
代码仓库入口:
- github源码地址(https://github.com/AIminminAI/Huhb3D-Viewer)。
- gitee源码地址(https://gitee.com/aiminminai/Huhb3D-Viewer)。
本文涉及:
- https://github.com/AIminminAI/Huhb3D-Viewer/blob/main/src/core/tool_registry.cpp
- https://github.com/AIminminAI/Huhb3D-Viewer/blob/main/src/agent/AIAgentController.cpp
- 如果想像唠嗑一样,去了解一些小知识,快去看看视频吧:
- 认准一个头像,保你不迷路:
- 抖音:搜索“GodWarrior”
- 快手:搜索“AIYWminmin”
- B站:搜索“宇宙第一AIYWM”
您要是也想站在文章开头的巨人的肩膀啦,可以动动您发财的小指头,然后把您的想要展现的名称和公开信息发我,这些信息会跟随每篇文章,屹立在文章的顶部哦
附录:
camera_poses.json
[
{
“frame_id”: 0,
“position”: [0.0, 0.0, 5.0],
“rotation_euler”: [0.0, 0.0, 0.0],
“fov_degrees”: 45.0,
“view_matrix”: [
[1.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 1.0, -5.0],
[0.0, 0.0, 0.0, 1.0]
],
“projection_matrix”: [
[2.414, 0.0, 0.0, 0.0],
[0.0, 2.414, 0.0, 0.0],
[0.0, 0.0, -1.002, -0.200],
[0.0, 0.0, -1.0, 0.0]
]
},
{
“frame_id”: 1,
“position”: [1.18, 0.0, 4.86],
“rotation_euler”: [0.0, -13.6, 0.0],
“fov_degrees”: 45.0,
“view_matrix”: [
[0.972, 0.0, 0.236, -0.0],
[0.0, 1.0, 0.0, 0.0],
[-0.236, 0.0, 0.972, -5.0],
[0.0, 0.0, 0.0, 1.0]
],
“projection_matrix”: [
[2.414, 0.0, 0.0, 0.0],
[0.0, 2.414, 0.0, 0.0],
[0.0, 0.0, -1.002, -0.200],
[0.0, 0.0, -1.0, 0.0]
]
}
]
label_legend.txt
Semantic Label Color Legend
Category -> (R, G, B) in 0-255 range
0 FreeSurface 127 127 127
1 HorizontalPlane 0 0 255
2 LateralPlane_X 0 255 0
3 LateralPlane_Z 255 0 0
4 NearHorizontal 255 255 0
5 NearLateral_X 255 0 255
6 NearLateral_Z 0 255 255
7 Degenerate 255 127 0
8 Reserved1 127 0 255
9 Reserved2 0 127 255
manifest.json
{
“version”: “2.0”,
“generator”: “Huhb3D-SyntheticDataPipeline”,
“rgb_count”: 100,
“mask_count”: 100,
“depth_count”: 0,
“has_legend”: true,
“has_ai_description”: false,
“has_camera_poses”: false
}
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)