<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
            <title type="text">一面之猿网</title>
            <subtitle type="text">让这个世界，因为我，有一点点的不一样</subtitle>
    <updated>2026-03-14T17:58:52+08:00</updated>
        <id>http://www.chunel.cn</id>
        <link rel="alternate" type="text/html" href="http://www.chunel.cn" />
        <link rel="self" type="application/atom+xml" href="http://www.chunel.cn/atom.xml" />
    <rights>Copyright © 2026, 一面之猿网</rights>
    <generator uri="https://halo.run/" version="1.6.1">Halo</generator>
            <entry>
                <title><![CDATA[做之前不敢想的 CGraph]]></title>
                <link rel="alternate" type="text/html" href="http://www.chunel.cn/archives/cgraph-unbelievable-2025" />
                <id>tag:http://www.chunel.cn,2026-03-01:cgraph-unbelievable-2025</id>
                <published>2026-03-01T16:15:51+08:00</published>
                <updated>2026-03-14T17:58:52+08:00</updated>
                <author>
                    <name>Chunel</name>
                    <uri>http://www.chunel.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p>各位朋友大家好啊，我是小纯，很久没写博客了。过去的一年（2025年）里，更多的还是通过视频的形式做一些项目内容的更新。但每年给大家拜年和做总结的习惯，不能丢下。</p><p>过去的这一年，是我工作最忙的一年，也是危机感最强的一年。一路上的跌跌撞撞，好在跟着团队一起，都扛过来了。也正是这样的成长，使得去年，我虽然没有把很多精力投在CGraph中，但回看，还是完成了很多我之前不敢想的功能和突破。</p><p><strong>我们新增了6位开发者，发行了3个release版本，适配了4种不同的编程语言，支持了数不清的项目落地</strong>。让我一一到来。</p><blockquote><p>坚持惯例，先上地址：<a href="https://github.com/ChunelFeng/CGraph" target="_blank">https://github.com/ChunelFeng/CGraph</a></p></blockquote><h2 id="pycgraph" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>pycgraph</td></tr></table></h2><p>去年年初，我开始到处旅游。巧的是（真心没有刻意设计路线），正好去了长沙和重庆，见到了A神和守夜大佬。届时正好 <a href="https://github.com/nndeploy/nndeploy" target="_blank">nndeploy</a> 团伙也在做 项目的python化，在他们的帮助和指导下，我开始重整 CGraph 的python版本。</p><p>我刚开始定的目标，是完成类似 CGraph-lite 的，可以通过python做dag调度，和参数传递的版本即可。可接下来的进度，远超了我的预期。</p><p>三月份的时候，我们发布了第一个可用的 pycgraph 版本，可以说此时，我们已经完成了预设的目标。</p><p>五月份，<strong>我们完成了对cpp版本全量功能 python化</strong>，并且提供了pip安装方式，便于用户操作。同时，还支持了 cpp 和 python 混合编程的模式，并且在B站发布了极简的使用教程（B站搜索 pycgraph 即可）。</p><p><img src="http://www.chunel.cn/upload/2026/03/image.png" alt="image" /></p><p>不仅是python版本，社区的朋友们，还实现了包含 CSharp，Java，Go语言版本的简易版本，希望让不同语言的朋友都可以无缝的尝试起来。</p><p>最近 vibe coding 的概念大火，我们也会尝试去 Rust 和 Swift 等多种语言的版本，努力扩展一下自己的边界 和 CGraph 的项目簇。</p><h2 id="%E6%9C%AC%E5%9C%B0%E4%BF%9D%E5%AD%98" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>本地保存</td></tr></table></h2><p>这几年，关于pipeline本地保存和加载的事情，我听了很多人提类似的需求。大概思路，都是说 设定一个 json or xml 的配置，用户通过修改配置，来控制 pipeline 的执行逻辑。</p><p>我一直不做这个事情，一方面是由于我们不想引入三方库，不太好做类似json格式的解析。另一方面我总觉得，我们自行设定字段，对用户来说也有一定的学习成本。比如，配置的key，是叫 <code>node</code>，还是叫 <code>Node</code>，还是叫 <code>GNode</code> ，这本身就是很难知会到用户。</p><p>而且，这一块如果我们下场做的话，就要搞全功能的，每个功能都要验证一遍。但用户很可能仅需要少量功能的配置（比如 node + param就够了），所以就一直没做。</p><p>直到后来有一天，我接触了 cpp 的反射实现。我发现，这种用户不可读存储方式，正好可以实现 struct（其实也就是 pipeline）的无痛存储。基于这个思路，我们完成了包含CGraph所有模块的 一键存储和加载，不需要用户做任何key字段的设定。</p><p>哈，之前一直是我用做色丶图的思路去优化手头工作的，<strong>这一波算是工作反哺开源了吧</strong>。形成一套 组合拳？哈哈。</p><h2 id="%E6%94%AF%E6%8C%81apache%E9%A1%B9%E7%9B%AE" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>支持apache项目</td></tr></table></h2><p>后来，我们玩大了！！！我们遇到了 apache的 HugeGraph 团队的童鞋，彼时他们正好需要做类似 skills 任务编排。调研了 LlamaIndex，LangGraph 等一众项目后，觉得不是臃肿，就是功能缺失。但初步上手pycgraph，轻量级、并行化和高性能的特点，非常match他们的需求。</p><p><img src="http://www.chunel.cn/upload/2026/03/image-1773481873849.png" alt="image-1773481873849" /></p><p>对于这种重量级的用户，我们当然是尽全力支持。有一段时间，我们每周六晚上都固定开会交流进度，讨论遇到的问题和接下来的计划。有一个小插曲，9月底的时候，小雨同学发现了 pycgraph中有一处 region中无法继续插入 region的问题，导致他们无法构建复杂的深度dag结构，但cpp版本中，却不存在这个问题。而我记得，我之前在cpp版本中，刻意的去修复过类似问题啊！！！</p><p>经过定位后，我意识到整体注册链路的设计还是有点问题，索性就重新设计了element 的插入流程，彻底解决了这个问题——还记得十一假期的第一天早上，我因为这个事情，搞到了3点多。</p><p>现在，HugeGraph 项目已经从孵化器毕业，成为了 apache 顶级项目，也非常高兴和apache团队的童鞋一起度过了一阵有意思的时光。有重量级的项目背书，CGraph 的稳定性也是上了一个台阶。</p><blockquote><p>参考文章：<a href="https://hugegraph.apache.org/cn/blog/2025/10/29/agentic-graphrag/" target="_blank">Agentic GraphRAG：模块化架构实践</a></p></blockquote><p>不光是开源项目，我们还支持了 智驾的，无人机的，机器人的，AI的，互联网的大大小小至少100+公司，都丝滑落地。好几个团队给我们提过bugfix 和 optimize，非常感谢。有一个有意思的事情，身边有几个同事离开了，我惊讶的发现，他们去的地方都用过CGraph。还有朋友在老东家用CGraph，觉得挺顺手，跳槽后又把这一套推荐到了新公司——代码出现了人传人的现象，哈哈。</p><h2 id="%E6%80%BB%E7%BB%93" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>总结</td></tr></table></h2><p>2025年，虽然感觉在开源社区没有像前几年那么发力，但回头总结的时候，却发现，很多那些想做的功能，和不知道怎么做的困难，基本上都done了。跟之前不同，我们很少再做和同类产品的性能benchmark，更多的只跟之前的自己对比。<strong>不再盲目的追逐【更快】，而是更加关注【更稳】</strong>。这可能也是项目从少年气盛到中年沉稳自然而然的过度。</p><p><img src="http://www.chunel.cn/upload/2026/03/image-1773477891806.png" alt="image-1773477891806" /></p><p>不变的是，我们持续为行业做贡献，给用户支持，认真听取用户反馈和建议。CGraph 还有很多问题，一些是我知道的，可能更多的是我还不知道的——不然过年那几天，我也不会一直待在家里bugfix——当前的 vibe coding 发现不了，更解决不了（当然了，所有开源的 or 公司的项目都会有问题，不影响大家选型和使用）。</p><p>而走到现在这一步，我也可能算是黔驴技穷了。像CGraph这种传统手艺的framework，总有一天会被AI取代，AI不仅可能快速生成一个 CGraph Pro，甚至可能快速生成一个 Flink Pro 或 pytorch Pro 。真到了那一天（哈，可能现在已经到了吧），我们现在做的所有事情都变得那么没有意义，没有价值。</p><p>等那一天回忆起来，我们这几年做的事情，唯一的意义和价值，可能就是我们真的真心实意，踏踏实实的做过吧。祝福大家和CGraph项目都能在新的时代，跟上节奏，找到新的增长点和爆点。</p><p>下图是个人微信，还有打赏码，亲，您介意扫一下么，哈哈。小纯在这里给大家比心了。</p><center><p><img src="http://www.chunel.cn/upload/2020/10/mmqrcode1602771241876-546dc2b18b5044d2a2808c5d19ad2394.png" alt="mmqrcode1602771241876" /></p></center><pre><code>                                                             [2026.03.14 by Chunel]</code></pre><h4 id="%E6%8E%A8%E8%8D%90%E9%98%85%E8%AF%BB" tabindex="-1"><strong>推荐阅读</strong></h4><ul><li><a href="http://www.chunel.cn/archives/cgraph-run-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——执行逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-loop-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——循环逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-param-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——参数传递</a></li><li><a href="http://www.chunel.cn/archives/cgraph-condition-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——条件判断</a></li><li><a href="http://www.chunel.cn/archives/cgraph-aspect-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——面向切面</a></li><li><a href="http://www.chunel.cn/archives/cgraph-function-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——函数注入</a></li><li><a href="http://www.chunel.cn/archives/cgraph-message-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——消息机制</a></li><li><a href="http://www.chunel.cn/archives/cgraph-event-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——事件触发</a></li><li><a href="http://www.chunel.cn/archives/cgraph-timeout-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——超时机制</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-1-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（一）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-2-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（二）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-3-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（三）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-4-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（四）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-5-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（五）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-6-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（六）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-performance-1" target="_blank">纯序员给你介绍图化框架的简单实现——性能优化（一）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-performance-2" target="_blank">纯序员给你介绍图化框架的简单实现——性能优化（二）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-distance-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——距离计算</a><br /><br><br></li><li><a href="http://www.chunel.cn/archives/listen-to-coder" target="_blank">CGraph 主打歌——《听码农的话》</a></li><li><a href="http://www.chunel.cn/archives/cgraph-anniversary-introduce" target="_blank">聊聊我写CGraph的这一年</a></li><li><a href="http://www.chunel.cn/archives/cgraph-awesome-cpp" target="_blank">从零开始主导一款收录于awesome-cpp的项目，是一种怎样的体验?</a></li><li><a href="http://www.chunel.cn/archives/cgraph-compare-taskflow-v1" target="_blank">炸裂！CGraph性能全面超越taskflow之后，作者却说他更想…</a></li><li><a href="http://www.chunel.cn/archives/cgraph-max-para-size" target="_blank">以图优图：CGraph中计算dag最大并发度思路总结</a></li><li><a href="http://www.chunel.cn/archives/cgraph-kunanniversary-introduce" target="_blank">一文带你了解练习时长两年半的CGraph</a></li><li><a href="http://www.chunel.cn/archives/cgraph-extended-dag" target="_blank">CGraph作者想知道，您是否需要一款eDAG调度框架</a></li><li><a href="http://www.chunel.cn/archives/cgraph-remove-redundancy-link" target="_blank">降边增效：CGraph中冗余边剪裁思路总结</a></li><li><a href="http://www.chunel.cn/archives/cgraph-pycgraph-v1" target="_blank">最新码坛爽文：重生之我在国外写CGraph(python版本)</a><br /><br></li></ul><hr /><h4 id="%E4%B8%AA%E4%BA%BA%E4%BF%A1%E6%81%AF" tabindex="-1">个人信息</h4><ul><li>微信: ChunelFeng</li><li>邮箱: <a href="mailto:chunel@foxmail.com" target="_blank">chunel@foxmail.com</a></li><li>个人网站：<a href="http://www.chunel.cn" target="_blank">www.chunel.cn</a></li><li>github地址: <a href="https://github.com/ChunelFeng" target="_blank">https://github.com/ChunelFeng</a></li></ul><p><img src="http://www.chunel.cn/upload/2021/08/image-a63a5ca7d9104993a51c19d88e25454d.png" alt="image" /></p>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[最新码坛爽文：重生之我在国外写CGraph(python版本)]]></title>
                <link rel="alternate" type="text/html" href="http://www.chunel.cn/archives/cgraph-pycgraph-v1" />
                <id>tag:http://www.chunel.cn,2025-03-02:cgraph-pycgraph-v1</id>
                <published>2025-03-02T16:51:02+08:00</published>
                <updated>2025-03-03T00:45:09+08:00</updated>
                <author>
                    <name>Chunel</name>
                    <uri>http://www.chunel.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p>各位朋友，大家好，我是Chunel，很高兴在这里和大家见面了。我是从2020年开始在开源社区写代码的，很高兴这些年认识大家，也很高兴自己做的东西也被大家熟悉和喜爱，也在几乎所有设计到编程的各行各业有了多个实际落地落地案例。也听到了来自四面八方的各种声音，让我对自己和项目，都有了更明确的认识，同时更清晰的规划下一步的计划和接下来的方向。</p><center><p><img src="http://www.chunel.cn/upload/2025/03/image-1740922121404.png" alt="image-1740922121404" /></p></center><p>这些年，我是有每年新年都有写文章给大家做回顾和拜新年的习惯的。今年因为各种原因，过年没有回家，而是选择了出国旅行。在国外也是一直坚持更新项目的好么，有图为证，哈哈。回来之后又是一堆事情，所以稍微耽误了一些。不过，该有的总结和展望，虽迟但到。</p><p>坚持惯例，先上地址：<a href="https://github.com/ChunelFeng/CGraph" target="_blank">https://github.com/ChunelFeng/CGraph</a></p><h2 id="2024%E5%B9%B4%E5%9B%9E%E9%A1%BE" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>2024年回顾</td></tr></table></h2><p>去年一年，CGraph 在功能、性能和稳定性上，又有了非常大的提升。我们发布了3个release版本，丰富了pipeline的执行策略、编排逻辑、执行逻辑；基于数据结构和操作系统的理论，提升了整体运行性能的同时，极大了降低了运行时的cpu负载；引入了新的编译方式；提供极简的 <a href="https://github.com/ChunelFeng/CGraph-lite" target="_blank">CGraph-lite</a> 版本；并且对一些很细节的功能，做了增删改和性能优化，以便更好的迎合和适配大家的需求。</p><p>去年这一年，伴随着项目的完善和影响力的提升，我们同样支持了很多公司和团队的项目落地，极大的简化了大家做任务编排和算子化的难度。其中不乏一些行业头部的公司，就拿我稍微熟悉一些的智驾领域，我知道的实际落地的公司，就至少有3家，并且都丝滑落地，一致好评。看一张我们的用户提供的构图，这些逻辑如果自己直接写的话，那会有多大的工作量？更可怕的是，后期会有多大的维护和交接工作量，你懂的。</p><center><p><img src="http://www.chunel.cn/upload/2025/03/image-1740918479210.png" alt="image-1740918479210" /></p></center><p>我们还坚持做一些技术输出的工作，CGraph项目自身，我通过迭代文档和视频，来介绍最新的功能点和优化点。同时我们也组织了9次社区内的技术分享，内容设计编译器、端开发、图调度、nlp、大模型推理和部署等多个领域，感谢大家的整理和分享。我也肉身入局，约见了多位来着社区的朋友，和大家面对面进行深入浅出的交流。也希望接下来一年有机会，给大家分享一点我的所知所想。</p><h2 id="cgraph%E7%9A%84python%E7%89%88%E6%9C%AC" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>CGraph的python版本</td></tr></table></h2><p>单独列出来说PyCGraph 的事情，因为这个事情对项目的昨天、今天和明天都有事关重大。早在2年前，我们小团队就一起做过这方面的尝试，但限于自身水平和对cpp with python 交互的理解，结果并没有太多水花。今年是蛇年，我们又重燃了 CGraph 的python版本的兴趣，并且随着A神和守夜大佬的加入和指导，我们也解决了之前遇到的几乎所有难题，前景一下子就光明了起来。举杯敬大家。</p><center><p><img src="http://www.chunel.cn/upload/2025/03/image-1740925466759.png" alt="image-1740925466759" /></p></center><p>其实我们也知道，如果还一直focus在调度、切换、图结构、操作系统这些方面继续深耕和优化的话，我们可能还能做出来一些提升（当然，这些内容我们肯定也会持续的学习和进行），但对项目、对用户的帮助和体感已经没有那么大了，而且这是一条非常非常艰辛的道路。就好比跑步吧，如果你100米跑到了10s，已经是非常牛批了。如果你还想再进一步，那将会是非常艰难的。但如果你在继续训练跑步的基础上，又去练习跳高和跳远，那你就将在有速度优势的基础上，快速的掌握一大波新的运动技能和技巧。至于下一步，你的新技巧甚至可能形成反哺（or 倒逼？哈哈），帮你进一步在跑步方面突破。</p><p>如果说上面的例子不是很准确，那我再举一个例子。如果你想在篮球方面做到世界第一人，那一定是非常难的对吧。但如果你想在唱、跳、rap、篮球这四个领域的综合维度做到第一，那… 好了，不说了，去踩缝纫机了。</p><center><p><img src="http://www.chunel.cn/upload/2025/03/image-1740919515309.png" alt="image-1740919515309" /></p></center><p>至于现状，我们来通过一个非常简单的demo展示一下 PyCGraph 的用法</p><pre><code class="language-python">import timefrom datetime import datetimefrom PyCGraph import GNode, GPipeline, CStatusclass MyNode1(GNode):    def run(self):        print(&quot;[{0}] {1}, enter MyNode1 run function. Sleep for 1 second ... &quot;.format(datetime.now(), self.getName()))        time.sleep(1)        return CStatus()class MyNode2(GNode):    def run(self):        print(&quot;[{0}] {1}, enter MyNode2 run function. Sleep for 2 second ... &quot;.format(datetime.now(), self.getName()))        time.sleep(2)        return CStatus()if __name__ == &#39;__main__&#39;:    pipeline = GPipeline()    a, b, c, d = MyNode1(), MyNode2(), MyNode1(), MyNode2()    pipeline.registerGElement(a, set(), &quot;nodeA&quot;)    pipeline.registerGElement(b, {a}, &quot;nodeB&quot;)    pipeline.registerGElement(c, {a}, &quot;nodeC&quot;)    pipeline.registerGElement(d, {b, c}, &quot;nodeD&quot;)    pipeline.process()</code></pre><p>哈哈，单纯看demo，是不是感觉很熟悉。我们尽可能1比1的复刻 cpp版本的接口和功能用法，减少大家的使用成本和入门负担。也会尽可能做到让大家在工程中同时使用cpp和python混合开发。</p><p>其实这并不简单，其中涉及到 python本身就没有的模板概念，python中对象的申请和析构，cpp中如何提供对应的适配 等一系列问题。上述问题，当前我们都有了相应的解法，我们也会持续pk接下来的遇到的一切问题，目标就是今年可以完成 CGraph中所有功能的 python 版本迁移。</p><p>我们最近也会很快推出最新的 v3.0.0 版本，让大家先把体验起来。也欢迎大家多多给出意见和帮助。有了python版本，之前大家多次提到的配置生成，方案管理等内容，就会简单多了，不是么？还有就是大家如果想做一些后台服务部署、或者算法验证啥的，那不就是一个 pip install xxx + PyCGraph 就可以解决的事情了么，哈哈。现在的我，甚至相信，今后大家会更多的使用和喜欢 PyCGraph，而不是 CGraph本身，嘿嘿。</p><h2 id="2025%E5%B9%B4%E5%B1%95%E6%9C%9B" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>2025年展望</td></tr></table></h2><p>新的一年，自己的事情也非常多，也有了很多计划的改变，开始想着去尝试一些之前没尝试过的内容。比如，我会想，是不是的经商or投资头脑太差了，需要恶补一下。我也会想，自己这些年，周末天天在咖啡厅里闷头敲代码，看似一直在刷性能，学技术，但这是不是就是自己的舒适区。要不要去接触一些别的内容，比如学一学调酒？</p><center><p><img src="http://www.chunel.cn/upload/2025/03/image.png" alt="image" /></p></center><p>这个时候就需要有各个领域的朋友和大佬，带一带了。希望大家不嫌弃小白，可以带我入局。毕竟，我不会，但我愿意学啊，哈哈哈。</p><p>接下来的一年，我会遇到自己职业生涯中的最大挑战和危机。好在自己所在的团队和方向都很不错，在行业里也很有竞争力，期待和大家一起乘风破浪，继续征程，攻克难关。在开源社区，我们要努力去实现下一篇爽文的内容：【爱上CGraph是每个码农的宿命】。<strong>我知道，我们需要的不仅是 Python，更需要有 Passion 和 Patient ，这就是我常说的3P原则吧</strong></p><p>最后祝愿大家在新的一年里，一切都好，小纯在这里给你比心了。</p><center><p><img src="http://www.chunel.cn/upload/2020/10/mmqrcode1602771241876-546dc2b18b5044d2a2808c5d19ad2394.png" alt="mmqrcode1602771241876" /></p></center><pre><code>                                                             [2025.03.02 by Chunel]</code></pre><h4 id="%E6%8E%A8%E8%8D%90%E9%98%85%E8%AF%BB" tabindex="-1"><strong>推荐阅读</strong></h4><ul><li><a href="http://www.chunel.cn/archives/cgraph-run-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——执行逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-loop-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——循环逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-param-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——参数传递</a></li><li><a href="http://www.chunel.cn/archives/cgraph-condition-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——条件判断</a></li><li><a href="http://www.chunel.cn/archives/cgraph-aspect-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——面向切面</a></li><li><a href="http://www.chunel.cn/archives/cgraph-function-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——函数注入</a></li><li><a href="http://www.chunel.cn/archives/cgraph-message-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——消息机制</a></li><li><a href="http://www.chunel.cn/archives/cgraph-event-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——事件触发</a></li><li><a href="http://www.chunel.cn/archives/cgraph-timeout-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——超时机制</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-1-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（一）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-2-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（二）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-3-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（三）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-4-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（四）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-5-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（五）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-6-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（六）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-performance-1" target="_blank">纯序员给你介绍图化框架的简单实现——性能优化（一）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-performance-2" target="_blank">纯序员给你介绍图化框架的简单实现——性能优化（二）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-distance-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——距离计算</a><br /><br><br></li><li><a href="http://www.chunel.cn/archives/listen-to-coder" target="_blank">CGraph 主打歌——《听码农的话》</a></li><li><a href="http://www.chunel.cn/archives/cgraph-anniversary-introduce" target="_blank">聊聊我写CGraph的这一年</a></li><li><a href="http://www.chunel.cn/archives/cgraph-awesome-cpp" target="_blank">从零开始主导一款收录于awesome-cpp的项目，是一种怎样的体验?</a></li><li><a href="http://www.chunel.cn/archives/cgraph-compare-taskflow-v1" target="_blank">炸裂！CGraph性能全面超越taskflow之后，作者却说他更想…</a></li><li><a href="http://www.chunel.cn/archives/cgraph-max-para-size" target="_blank">以图优图：CGraph中计算dag最大并发度思路总结</a></li><li><a href="http://www.chunel.cn/archives/cgraph-kunanniversary-introduce" target="_blank">一文带你了解练习时长两年半的CGraph</a></li><li><a href="http://www.chunel.cn/archives/cgraph-extended-dag" target="_blank">CGraph作者想知道，您是否需要一款eDAG调度框架</a></li><li><a href="http://www.chunel.cn/archives/cgraph-remove-redundancy-link" target="_blank">降边增效：CGraph中冗余边剪裁思路总结</a><br /><br></li></ul><hr /><h4 id="%E4%B8%AA%E4%BA%BA%E4%BF%A1%E6%81%AF" tabindex="-1">个人信息</h4><ul><li>微信: ChunelFeng</li><li>邮箱: <a href="mailto:chunel@foxmail.com" target="_blank">chunel@foxmail.com</a></li><li>个人网站：<a href="http://www.chunel.cn" target="_blank">www.chunel.cn</a></li><li>github地址: <a href="https://github.com/ChunelFeng" target="_blank">https://github.com/ChunelFeng</a></li></ul><p><img src="http://www.chunel.cn/upload/2021/08/image-a63a5ca7d9104993a51c19d88e25454d.png" alt="image" /></p>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[纯序员给你介绍图化框架的简单实现——性能优化（二）]]></title>
                <link rel="alternate" type="text/html" href="http://www.chunel.cn/archives/cgraph-performance-2" />
                <id>tag:http://www.chunel.cn,2024-11-10:cgraph-performance-2</id>
                <published>2024-11-10T14:33:42+08:00</published>
                <updated>2024-11-17T00:22:59+08:00</updated>
                <author>
                    <name>Chunel</name>
                    <uri>http://www.chunel.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p>大家好，我是不会写代码的程序员 Chunel，很高兴在这里跟大家见面了。</p><p>前阵子，我们发布了 <a href="https://www.bilibili.com/video/BV1gwWAekEAy/?spm_id_from=333.337.search-card.all.click&amp;vd_source=2c7baed805c6cb33d630d5d4546cf0be" target="_blank">并行调度框架 CGraph 和 taskflow 性能对比实测</a> 视频，验证了在各种常见情况下，色图的劲爆性能。让我们意识到，色图已经进入了领域的无人区。如果之前我们要做的，是更好的学习和借鉴别人的优点的话，那今后更重要的是不断的找到自身的缺点和不足，提升自己的同时，也可以反哺别人了。</p><p>本文主要介绍，在这个 8乘8的全连接性能测试用例中，<a href="https://github.com/ChunelFeng/CGraph/blob/main/test/Performance/test-performance-04.cpp" target="_blank">test-performance-04.cpp</a> ，我们从 v2.6.0版本的 11.5s，优化到当前的7.2s过程中，做了哪些改进吧。</p><p>首先，还是先上代码：<a href="https://github.com/ChunelFeng/CGraph" target="_blank">https://github.com/ChunelFeng/CGraph</a></p><h2 id="%E4%BC%98%E5%8C%96%E5%B9%B6%E5%8F%91%E9%80%BB%E8%BE%91" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>优化并发逻辑</td></tr></table></h2><p>我们知道，将逻辑放到多个线程中执行，而不是串行，可以有效的提升性能。色图也正是为这件事情应运而生的，但需要面对一个问题：如何更好的设计并发逻辑，尽可能的将可以并行的逻辑并发，减少串行依赖的消耗。</p><p>在老版本（今后通指v2.6.0版本）中，有这样的设计：在每次 pipeline-&gt;run() 函数运行开始的时候，先依次重置每个 element 的依赖信息，如下图：</p><p><img src="http://www.chunel.cn/upload/2024/11/image.png" alt="image" /></p><p>这个执行逻辑，在普通的 pipeline中，耗时基本可以忽略不计，因为重置 element 的函数耗时非常少。但是，在这个测例中有64个element，也就是这个重置逻辑，需要串行64次。特别是由于测试的element逻辑，都是 i++ 这种情况，这个处理的耗时就很热点了。热点到 如果我们使用 <code>perf top</code> 去查看的话，会发现 beforeRun() 这个函数的耗时，居然是整个流程中最多的。</p><p>我们来分析一下这个问题。首先，element是根据依赖逻辑执行的，或并行或串行。是不是可以把重置逻辑，放到每个 element 具体执行之前（或之后）就可以了。这样的话，既能保证每次执行完成信息被恢复，从而下次执行的时候，逻辑也是正常的；又能保证不需要依次执行 beforeRun()，减少了串行的开销。</p><p>还有一点，我们想，这个8x8 的逻辑中，最前面的 8个element，实际上是没有任何依赖逻辑的。也就是说，这8次 beforeRun() 执行，实际上是冗余的。</p><p><img src="http://www.chunel.cn/upload/2024/11/image-1731226128521.png" alt="image-1731226128521" /></p><p>我们来按照每次执行 beforeRun() 为1ms来计算，最初的串行版本是64ms。而最新的版本中，首先，减少了第一列的8个 element的8次计算，后面的7列（每列8个并发element）中，在最极限的理论下，是可以每列都并行的，也就是一共 7ms。</p><blockquote><p>btw：最新版本中，element的 beforeRun() 方法，改名为 refresh() 了。内容不变</p></blockquote><h2 id="%E7%B2%BE%E7%AE%80%E6%97%A0%E7%94%A8%E5%AE%9A%E4%B9%89" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>精简无用定义</td></tr></table></h2><p>我们知道，定义一个变量耗时是很少的。但是，在一些极限性能情况下（哈哈，我说的就是进入无人区之后），就是一件需要考量的事情了。比如，常见的 std::function&lt;&gt; 和 lambda表达式 功能基本一样，但是前者的耗时远高于后者。</p><p>我们在做火焰图观测的时候，我们发现 v2.6.0版本中，生成和销毁 std::package_task 的过程中，实际上也是有一定的耗时的。</p><p><img src="http://www.chunel.cn/upload/2024/11/image-1731227779066.png" alt="image-1731227779066" /></p><p>项目刚开始使用 std::package_task 来包装任务，主要是由于静态引擎的执行过程中，需要获取element执行的 返回值信息。但这个设定，动态引擎正好不需要。于是，我们直接提供了新的 threadpool::execute() 接口，功能一样，只是不在通过 package_task 向上游返回运行结果值。</p><p><img src="http://www.chunel.cn/upload/2024/11/image-1731228799662.png" alt="image-1731228799662" /></p><p>可以很明显的看出来，这样一来不仅减少了 std::package_task 的构造销毁耗时，整体的执行性能也上来了。原先版本中，std::package_task::operator() 占整体耗时的 29%，现在红框中的逻辑，仅占17%，而这两个地方的功能，对于动态执行是完全一样的。</p><h2 id="%E5%B9%B3%E6%9B%BF%E5%8A%A8%E6%80%81%E8%BD%AC%E6%8D%A2" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>平替动态转换</td></tr></table></h2><p>我们知道，如果想尝试将 Base* 转义成 Devide* 的话，需要用到 dynamic_cast 这个方法。色图中，在获取 GParam参数的时候，有必须通过 Base* -&gt; Devide * 的方式，来获取执行类型的指针，还需要再类型不匹配的情况下，返回 nullptr，所以用到了 dynamic_cast。但这个方法本身，是有较多的消耗的。这在CGraph这种基础组件中，是要尽可能避免的。</p><pre><code class="language-cpp">Base* b = new Devide();Devide* d = dynamic_cast&lt;Devide *&gt;(b);</code></pre><p>在新版本中，我们修改了 这一块的实现。通过 typeinfo 来判断，传入的 type 和 对应的GParam的 type是否匹配，如果匹配的话，通过 static_cast 来转就好了。不匹配的话，返回 nullptr，跟之前逻辑保持完全一致。</p><pre><code class="language-cpp">Base* b = new Devide();Devide* d = (typeid(Devide) == typeid(*b)) ? static_cast&lt;Devide *&gt;(b) : nullptr;</code></pre><p>我们在 Linux 环境上实测了一下，新版本的耗时，仅有 dynamic_cast 强转的 30%左右。主要原因，就是没有了 dynamic_cast 逻辑中，最耗时的那个函数调用。</p><p><img src="http://www.chunel.cn/upload/2024/11/image-1731770131415.png" alt="image-1731770131415" /></p><p>这里也想引申出另外一个话题，新版本可以在所有的场景下，完全平替 dynamic_cast 么？有对这一块比较理解的小伙伴，欢迎指教。</p><h2 id="%E5%86%99%E5%9C%A8%E6%9C%80%E5%90%8E" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>写在最后</td></tr></table></h2><p>这篇文章选取的几个点，更多的都是我们在跟过去的自己做对比。长期开发和维护色丶图的经历，让我会更有意识的去关注一点非常细小的可能产生问题的点。</p><p>最近在工作中，也经常要处理一些性能问题。项目和系统复杂度上去之后，问题的定位就变得更加困难和棘手。特别是对于一些低概率偶现的热点问题，往往需要各种工具、各种知识杂交起来，好几个团队联手埋头苦看，才能最终分析定位和解决。在这种情况下，将尽可能多的信息 有规律的组织起来、可视化、可解释性 就变得非常重要。有好用的工具，总结形成有理论支撑的、可复用的经验思路，可能会让我们在这个领域走的更稳更久一些。</p><p>总结一下这篇文章介绍的功能中，我们用到的工具：</p><ol><li>godbolt：用于查看不同实现对应的汇编结果</li><li>uftrace：用于查看系统函数中的耗时分布</li><li>perf：用于发现进程的热点函数，主要用到 perf top 功能</li><li>火焰图：查看进程中每个部分的耗时情况</li><li>benchmark：用于dynamic_cast 改进前后的性能对比</li><li>perfetto：在查看偶现热点问题中，非常好用，推荐</li></ol><p>我们性能优化的主题，将会一直持续下去，攒了一些比较有意义的方法，就会更新这个系列的文章。这又是一个非常大的话题，入门简单，深入很难。有兴趣的朋友，欢迎添加我个人微信，随时交流指教。小纯给你比心了哦</p><center><p><img src="http://www.chunel.cn/upload/2020/10/mmqrcode1602771241876-546dc2b18b5044d2a2808c5d19ad2394.png" alt="mmqrcode1602771241876" /></p></center><pre><code>                                                             [2024.11.17 by Chunel]</code></pre><h4 id="%E6%8E%A8%E8%8D%90%E9%98%85%E8%AF%BB" tabindex="-1"><strong>推荐阅读</strong></h4><ul><li><a href="http://www.chunel.cn/archives/cgraph-run-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——执行逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-loop-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——循环逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-param-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——参数传递</a></li><li><a href="http://www.chunel.cn/archives/cgraph-condition-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——条件判断</a></li><li><a href="http://www.chunel.cn/archives/cgraph-aspect-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——面向切面</a></li><li><a href="http://www.chunel.cn/archives/cgraph-function-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——函数注入</a></li><li><a href="http://www.chunel.cn/archives/cgraph-message-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——消息机制</a></li><li><a href="http://www.chunel.cn/archives/cgraph-event-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——事件触发</a></li><li><a href="http://www.chunel.cn/archives/cgraph-timeout-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——超时机制</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-1-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（一）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-2-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（二）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-3-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（三）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-4-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（四）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-5-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（五）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-6-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（六）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-performance-1" target="_blank">纯序员给你介绍图化框架的简单实现——性能优化（一）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-distance-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——距离计算</a><br /><br><br></li><li><a href="http://www.chunel.cn/archives/listen-to-coder" target="_blank">CGraph 主打歌——《听码农的话》</a></li><li><a href="http://www.chunel.cn/archives/cgraph-anniversary-introduce" target="_blank">聊聊我写CGraph的这一年</a></li><li><a href="http://www.chunel.cn/archives/cgraph-awesome-cpp" target="_blank">从零开始主导一款收录于awesome-cpp的项目，是一种怎样的体验?</a></li><li><a href="http://www.chunel.cn/archives/cgraph-compare-taskflow-v1" target="_blank">炸裂！CGraph性能全面超越taskflow之后，作者却说他更想…</a></li><li><a href="http://www.chunel.cn/archives/cgraph-max-para-size" target="_blank">以图优图：CGraph中计算dag最大并发度思路总结</a></li><li><a href="http://www.chunel.cn/archives/cgraph-kunanniversary-introduce" target="_blank">一文带你了解练习时长两年半的CGraph</a></li><li><a href="http://www.chunel.cn/archives/cgraph-extended-dag" target="_blank">CGraph作者想知道，您是否需要一款eDAG调度框架</a></li><li><a href="http://www.chunel.cn/archives/cgraph-remove-redundancy-link" target="_blank">降边增效：CGraph中冗余边剪裁思路总结</a><br /><br></li></ul><hr /><h4 id="%E4%B8%AA%E4%BA%BA%E4%BF%A1%E6%81%AF" tabindex="-1">个人信息</h4><ul><li>微信: ChunelFeng</li><li>邮箱: <a href="mailto:chunel@foxmail.com" target="_blank">chunel@foxmail.com</a></li><li>个人网站：<a href="http://www.chunel.cn" target="_blank">www.chunel.cn</a></li><li>github地址: <a href="https://github.com/ChunelFeng" target="_blank">https://github.com/ChunelFeng</a></li></ul><p><img src="http://www.chunel.cn/upload/2021/08/image-a63a5ca7d9104993a51c19d88e25454d.png" alt="image" /></p>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[降边增效：CGraph中冗余边剪裁思路总结]]></title>
                <link rel="alternate" type="text/html" href="http://www.chunel.cn/archives/cgraph-remove-redundancy-link" />
                <id>tag:http://www.chunel.cn,2024-09-08:cgraph-remove-redundancy-link</id>
                <published>2024-09-08T10:31:41+08:00</published>
                <updated>2024-09-10T11:27:44+08:00</updated>
                <author>
                    <name>Chunel</name>
                    <uri>http://www.chunel.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p>大家好，我是不会写代码的纯序员——Chunel。之前，我们跟大家分享过，基于图论如何计算CGraph中dag的最大并行度<a href="http://www.chunel.cn/archives/cgraph-max-para-size" target="_blank">(最大并行度文章链接)</a>，该思路也有幸被腾讯的童鞋采纳并运用在项目中。希望我们的内容，能够帮助到更多做类似事情的朋友，也随时欢迎大家交流指教。</p><p>今天，我们继续依靠图论，来对CGraph中的dag进行裁边的优化，以达到更优图的目的。按照惯例，首先上源码：</p><blockquote><p><a href="https://github.com/ChunelFeng/CGraph" target="_blank">https://github.com/ChunelFeng/CGraph</a></p></blockquote><h2 id="%E9%97%AE%E9%A2%98%E6%8F%8F%E8%BF%B0" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>问题描述</td></tr></table></h2><p>我们先来描述一下问题哈。我们知道，色图已经在各行各业被广泛落地使用，也有很多人针对现有的逻辑进行二次开发。也正是因为被使用的多了，很多问题被提了出来。</p><p><img src="http://www.chunel.cn/upload/2024/09/image.png" alt="image" /></p><p>比如说在机器视觉，特别是检测场景，就会遇到上图这种 缺口、形变、脏污的组合拳。当 作者组合好之后，使用 perf 逻辑打印出来耗时之后，我们会发现，这张图有点奇怪，但又说不上哪里有问题。</p><p>让我们聚焦到红框内部，来细看一下吧。框里有4个节点和5条边。这其中，实际上有很多冗余连接。比如吧，A和C之间，有两条路线（AC和AB-&gt;BC），很容易看出。这个AC的连接（对应边2）是多余的，因为无论这条边 连或者不连，C都需要等B执行完后，才开始执行，而B又要等A结束才开始。同样的情况，还出现在AD和 AB-&gt;BD 的情况，和图中其他几乎各个位置。</p><p>我们知道，在CGraph中，是没有显示的边的概念的，所有的node都是通过依赖关系来决定执行顺序。在这种依赖关系明显变多的情况下，虽然不会导致执行异常，但会增加每次执行时解图的工作量。虽然这点工作量（1us之内），针对动则10+ms 的算子逻辑来说，可以忽略不计，但站在一个合格的中间件开发工程师这一层来看，明显是多余了。</p><h2 id="%E8%A7%A3%E5%86%B3%E6%80%9D%E8%B7%AF" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>解决思路</td></tr></table></h2><p>明确问题之后，就考虑如何实现这个功能。刚开始的时候，也能想到，通过链路，来标记依赖关系。但想来想去，总感觉离彻底解决这个问题有一步的距离。</p><p>不过，有了之前计算最大并行度的经验，我们就知道这样的问题该请教谁了[/狗头] 。对，我们又请教了当时还是在读研究生， 现在已经是互联网大厂算法工程师的 <a href="https://github.com/Hanano-Yuuki" target="_blank">倪童鞋</a>，得到了一条非常有用的公式：</p><p><img src="http://www.chunel.cn/upload/2024/09/image-1725779550211.png" alt="image-1725779550211" /></p><p>解释一下，节点V的所有父节点（例 Ui 和Uj）中，如果其中任意一个是另外一个的祖先（包含父节点），则删除该祖先节点（Uj）和当前节点(V)的连接。这样说如果还觉得有点抽象的话，我们来通过一个具体的例子，来说明这个问题。</p><p><img src="http://www.chunel.cn/upload/2024/09/image-1725781569220.png" alt="image-1725781569220" /></p><p>看上面这张图哈。左上角是一个DAG，包含了三条联通路径，见左下角。依据路径信息，我们可以生成右边的联通矩阵。这里针对矩阵稍作解释：</p><ol><li>横向表示单个节点，纵向表示该节点是否是横向的后继。用图中蓝色背景的那个1来说吧，这个说明B节点是A节点的后继。反过来就说A是B的前驱——可以是父亲，也可以是祖先。</li><li>矩阵表示一个有向图，中轴线上的值，都是0，表示自己不是自己的祖先。</li><li>如果M[x][y] = 1，则 M[y][x] 一定是0</li></ol><p>得到这个矩阵之后，我们就开始具体的裁边操作了:</p><ol><li>选取第一个节点V</li><li>遍历当前节点的所有的一级父节点（Ua…Uz），如果这些节点中，如果Uj 是Ui 的祖先，则删除Uj和 V之间的连接</li><li>依次遍历所有的节点，重复完成上述操作</li></ol><p>再具体一点：<br />当遍历到B点的时候，发现B只有一个直系father，也就是A。不满足条件，继续遍历。<br />当遍历到D点的时候，发现A和B都是直系father，然后就看A和B之间的关系，发现A也是B 的祖先，则删除A和D之间的连边。</p><h2 id="%E7%BB%93%E6%9E%9C%E5%88%86%E6%9E%90" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>结果分析</td></tr></table></h2><p>好了，讲到这里，应该已经讲清楚了裁冗余边的所有流程，接下来的就是把逻辑实现一下就好了。</p><p>在最新版本的CGraph中，已经有具体的实现了。全局搜索一下 <code>GTrimOptimizer</code>，就可以看到对应的代码逻辑。如果不想看实现代码的话， 直接调用 GPipeline 的 <code>trim()</code> 接口即可。顺便说一下，函数的返回值是被裁掉的边的条数。当 pipeline中有group，group中多余的边，也会被裁掉。</p><pre><code class="language-cpp">void demo_trim() {    GPipelinePtr pipeline = GPipelineFactory::create();    GElementPtr a, b, c, d = nullptr;    pipeline-&gt;registerGElement&lt;MyNode1&gt;(&amp;a, {}, &quot;nodeA&quot;);    pipeline-&gt;registerGElement&lt;MyNode1&gt;(&amp;b, {a}, &quot;nodeB&quot;);    pipeline-&gt;registerGElement&lt;MyNode1&gt;(&amp;c, {a}, &quot;nodeC&quot;);    pipeline-&gt;registerGElement&lt;MyNode1&gt;(&amp;d, {a, b}, &quot;nodeD&quot;);    // A和D之间有连边    pipeline-&gt;trim();    pipeline-&gt;dump();    // 从dump的结果就可以看出来，A和D之间的边，被裁掉了    GPipelineFactory::remove(pipeline);}</code></pre><p><img src="http://www.chunel.cn/upload/2024/09/image-1725784989641.png" alt="image-1725784989641" /></p><p>多余的边是裁掉了，我们再来简单分析一下效果哈。分两种情况来看：</p><p>第一种情况，上述例子中的情况，裁掉了一条边，怎么说呢，虽然整个图的确是好看了一些，但是对整体执行耗时，基本没有任何影响。原因上文也说了，相比于node的执行逻辑，这一点图计算耗时，只能说是九牛一毛，基本没有任何影响。</p><p>第二种情况，大家应该知道，CGraph针对纯串行、纯并行逻辑都有一些执行优化。如果裁边之后，正巧变成了这种结构，整体调度性能会有意想不到的提升。</p><p>具体trim前后的执行耗时对比，我就不测了。在实际落地的过程中，还是更建议结合 perf 去优化具体node的耗时，或者根据耗时去更合理的编排组合dag逻辑。前阵子工作中，就遇到了一个真实的问题。我针对之前做过的一个组件做了各种优化，但看整体进程的性能，并没有什么提升。后来分析了一下才发现，我处理的这一块耗时仅占整体的1%左右，瓶颈根本就不在这里。</p><p>整了好长时间，经验和技术是攒到了，n+1也快到手了。哈哈，开玩笑哈，我建议大家工作中，要时刻关注kpi，但不要只面向kpi编程哈，个人的思考创新、经验积累，和对团队的潜在的提升的可能性 也同样重要。我相信，很多爆炸性的突破，看着像是瞬间出现的，实际上都是这些内容积累到一定程度，自然然而发生的。</p><h2 id="%E5%86%99%E5%9C%A8%E6%9C%80%E5%90%8E" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>写在最后</td></tr></table></h2><p>看到这里，相信你应该应该已经了解，CGraph在针对DAG结构中，剪裁冗余边的思路和实现方式。是不是没有用的知识，又增加了一点点。我自己构造了很多场景，实测剪裁逻辑都是正确的。我们水平有限，如果您发现上述思路在某种情况下有逻辑漏洞，或者是验证过程中，发现个别反例，导致裁边之后，整体逻辑不对了，请随时联系我们并指正。</p><p>又让我想到了这几年，行业里如火如荼的裁员潮，基本上所有公司都在通过裁员的方式降本增效，仿佛裁掉20%的人，就能及时止损20%一样。但实际上，有没有一种可能，人头数并不是公司陷入困境的主要原因？这背后，行业的波动起伏，公司的管理、战略方向和创新力，对人才的聚集力，是更重要的原因呢？而在这个过程中，一个人、一条边能有多大的影响呢？</p>  <center> <p><img src="http://www.chunel.cn/upload/2021/12/image-ebe41ac3914e4241bed5ecfedbcf0f2d.png" alt="image" /></p>  </center><p>如果您对我们的项目感兴趣，非常欢迎通过下面的方式，和我们取得联系，以便于随时技术交流。也欢迎给我们的项目star、fork and pr哈，感谢您看到这里，小纯给你比心了。</p><center><p><img src="http://www.chunel.cn/upload/2020/10/mmqrcode1602771241876-546dc2b18b5044d2a2808c5d19ad2394.png" alt="mmqrcode1602771241876" /></p></center><pre><code>                                                             [2024.09.08 by Chunel]</code></pre><h4 id="%E6%8E%A8%E8%8D%90%E9%98%85%E8%AF%BB" tabindex="-1"><strong>推荐阅读</strong></h4><ul><li><a href="http://www.chunel.cn/archives/cgraph-run-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——执行逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-loop-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——循环逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-param-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——参数传递</a></li><li><a href="http://www.chunel.cn/archives/cgraph-condition-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——条件判断</a></li><li><a href="http://www.chunel.cn/archives/cgraph-aspect-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——面向切面</a></li><li><a href="http://www.chunel.cn/archives/cgraph-function-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——函数注入</a></li><li><a href="http://www.chunel.cn/archives/cgraph-message-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——消息机制</a></li><li><a href="http://www.chunel.cn/archives/cgraph-event-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——事件触发</a></li><li><a href="http://www.chunel.cn/archives/cgraph-timeout-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——超时机制</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-1-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（一）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-2-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（二）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-3-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（三）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-4-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（四）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-5-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（五）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-6-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（六）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-performance-1" target="_blank">纯序员给你介绍图化框架的简单实现——性能优化（一）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-distance-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——距离计算</a><br /><br><br></li><li><a href="http://www.chunel.cn/archives/listen-to-coder" target="_blank">CGraph 主打歌——《听码农的话》</a></li><li><a href="http://www.chunel.cn/archives/cgraph-anniversary-introduce" target="_blank">聊聊我写CGraph的这一年</a></li><li><a href="http://www.chunel.cn/archives/cgraph-awesome-cpp" target="_blank">从零开始主导一款收录于awesome-cpp的项目，是一种怎样的体验?</a></li><li><a href="http://www.chunel.cn/archives/cgraph-compare-taskflow-v1" target="_blank">炸裂！CGraph性能全面超越taskflow之后，作者却说他更想…</a></li><li><a href="http://www.chunel.cn/archives/cgraph-max-para-size" target="_blank">以图优图：CGraph中计算dag最大并发度思路总结</a></li><li><a href="http://www.chunel.cn/archives/cgraph-kunanniversary-introduce" target="_blank">一文带你了解练习时长两年半的CGraph</a></li><li><a href="http://www.chunel.cn/archives/cgraph-extended-dag" target="_blank">CGraph作者想知道，您是否需要一款eDAG调度框架</a><br /><br><br></li></ul><hr /><h4 id="%E4%B8%AA%E4%BA%BA%E4%BF%A1%E6%81%AF" tabindex="-1">个人信息</h4><ul><li>微信: ChunelFeng</li><li>邮箱: <a href="mailto:chunel@foxmail.com" target="_blank">chunel@foxmail.com</a></li><li>个人网站：<a href="http://www.chunel.cn" target="_blank">www.chunel.cn</a></li><li>github地址: <a href="https://github.com/ChunelFeng" target="_blank">https://github.com/ChunelFeng</a></li></ul><p><img src="http://www.chunel.cn/upload/2021/08/image-a63a5ca7d9104993a51c19d88e25454d.png" alt="image" /></p>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[CGraph作者想知道，您是否需要一款eDAG调度框架]]></title>
                <link rel="alternate" type="text/html" href="http://www.chunel.cn/archives/cgraph-extended-dag" />
                <id>tag:http://www.chunel.cn,2024-02-11:cgraph-extended-dag</id>
                <published>2024-02-11T23:15:32+08:00</published>
                <updated>2024-02-13T17:39:07+08:00</updated>
                <author>
                    <name>Chunel</name>
                    <uri>http://www.chunel.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p>大家好啊，我是不会写代码的纯序员——Chunel，现在是2024年春节期间，小纯在这里，祝大家龙年大吉吧。</p><center><p><img src="http://www.chunel.cn/upload/2024/02/image.png" alt="image" /></p></center><p>自从CGraph项目立项以来，每年春节的时候，我都会在博客中，整理总结去年小破图的进展，对参与开发和投入使用的朋友表示感谢，也分享一些个人成长的心得。虽然不知道还能坚持几年，但今年就不要例外了。</p><p>我记得，之前南哥在nndeploy的群里，说过一句话：针对现在的逻辑，dag已经不够用了。别人只关心你飞的高不高，只有小纯关心你的DAG强不强。正好当时我在看 eBPF 相关的内容，我就想提出来一个 eDAG（extended DAG）的话题，来当做色丶图的title——毕竟，现在的CGraph已经可以用来做太多非传统dag中的逻辑了。</p><p>过去的这一年，是色丶图正式成为一款可适用于工业化场景的基础组件的一年，也是色丶图大规模落地的一年。同时，我们也组织了多次内容丰富的技术分享，在各个社区跟大家做了很好联动。下面，我们就一起重温一下过去的这一年吧。</p><h2 id="%E5%A2%9E%E5%BC%BAdag" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>增强DAG</td></tr></table></h2><p>首先，我们来聊聊新功能吧。这一年中，我们在色丶图中引入了退出、暂停、恢复执行的功能。为一些可视化平台建设的场景，提供了便利。我们底层采用的是线程管控，没有办法做到精细的实时暂停，只能做到node级别的，这个的确是一个小小的遗憾。但我相信，这对于需要人工来处理启动和停止的场景，应该也是足够的了。</p><p>为了方便大家进行图的编排和调试，我们在原先可视化的基础上，提供了perf功能。这可以方便的让大家了解到整个图中，每个元素的耗时和运行的情况，方便大家做后期进一步的调整和优化，更好的并行化自己的逻辑。也进一步帮大家实现了“所得即所见“的问题，哪里运行了，哪里没运行，整个一目了然。</p><p><img src="http://www.chunel.cn/upload/2024/02/image-1707814956569.png" alt="image-1707814956569" /></p><p>我们还提供了超时设置的功能，便于大家做各种三方请求和超时逻辑的处理。值得一提的是，我们在做这个功能的时候，兼顾了node 和 group 的操作逻辑，对外暴露一致的接口信息。个人感觉，设计和完成度都算是是比较高的了。更重要的是，早一年的时间里，有人问过我，有没有这个功能，我当时的说法是：不好做，有可能做不了。</p><p>原以为不可能的事情，还有很多。比如，之前我们在讨论，如何计算dag的最大并发度。又比如，我们讨论能否在dag执行的时候，完成动态构图逻辑。现在，这些都可以很轻易的实现了，感谢智慧的一直没有放弃的批友们。</p><h2 id="%E9%93%BA%E5%A4%A9%E7%9B%96%E5%9C%B0" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>铺天盖地</td></tr></table></h2><p>过去的这一年，是 CGraph 开始大规模落地的一年。范围覆盖了ai研究，推理引擎，自动驾驶，互联网，物联网，金融，游戏，中间件和基础组件研发，平台开发，机器人工控应用，端开发等各种领域。从高端的研究顶会项目，到工作中日常使用的小工具，都有涉及。自信一点的话，也算是做到了 <strong>顶天立地，铺天盖地，惊天动地</strong> 了吧。</p><p>一方面是因为项目功能已经趋于稳定，简单好上手，无任何三方依赖，引入非常简单，而且天然适配任何场景。还有一方面，我想也是因为我们在年初的时候，极大的降低了自身的cpu占用率，免去了大家在做各种技术评估时的后顾之忧。在性能上，我们也有了极大的提升，自测串行和dag场景中，CGraph的耗时仅为taskflow的1/4~1/2，算得上是遥遥领先了。</p><p><img src="http://www.chunel.cn/upload/2024/02/image-1707815614263.png" alt="image-1707815614263" /></p><p><img src="http://www.chunel.cn/upload/2024/02/image-1707815632156.png" alt="image-1707815632156" /></p><p>但在超cpu个数的并发场景中，我们性能还是落后。我再次重申，我一直认为，性能压测只是参考指标，并不能证明项目自身的好坏，taskflow 也一直都是我们学习的榜样。</p><h2 id="%E5%88%86%E4%BA%AB%E4%BA%A4%E6%B5%81" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>分享交流</td></tr></table></h2><p>这一年，我们做了很多触达和宣贯方面的内容。年中的时候，我开始在B站上制作 CGraph相关的视频，刚才去查看了一下，已经有近50个小视频了。嗯，怎么说捏，讲真，我也感觉视频制作的不是很精美，连个最基本的字幕都没有，远不如风神。</p><p><img src="http://www.chunel.cn/upload/2024/02/image-1707815480381.png" alt="image-1707815480381" /></p><p>但是，每一集都是我自己一点一点录的，因为懒得再去学做视频处理，经常是录到一半，觉得不满意，就删了重新录。内容很详实，每个demo都是手敲代码运行，满满的都是诚意，欢迎大家一键三连。</p><p>我们还邀请了其他多个项目的朋友，来小破批做技术分享。内容从软件到服务，从算法到编译，涉及多个领域。我们今后也会持续这项活动，搭建一个舞台，欢迎你来分享。</p><h2 id="%E5%B9%B4%E7%BB%88%E5%B0%8F%E8%8A%82" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>年终小节</td></tr></table></h2><p>很高兴回顾这一年，我们完成了很多去年这个时候，还感觉无法完成的事情。我自己也在其中收获颇多，再次点一下给我们精心准备分享的 鸭哥、风神、鑫哥、南总，也感谢来听分享，平时在批里聊技术的朋友，相信色图和以色图为蓝本，最终把项目落地的朋友。感谢你们的意见，没有你们的帮助，我们很难一路走到今天。</p><p>明年的话，我们将尽可能去实现更多我们今年说的去年想要完成的事情，也期待和大家一起，走的长走的远。</p><p>对项目有兴趣的朋友，欢迎添加我个人微信，随时交流哦。。。</p><center><p><img src="http://www.chunel.cn/upload/2020/10/mmqrcode1602771241876-546dc2b18b5044d2a2808c5d19ad2394.png" alt="mmqrcode1602771241876" /></p></center><pre><code>                                                             [2024.02.13 by Chunel]</code></pre><h4 id="%E6%8E%A8%E8%8D%90%E9%98%85%E8%AF%BB" tabindex="-1"><strong>推荐阅读</strong></h4><ul><li><a href="http://www.chunel.cn/archives/cgraph-run-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——执行逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-loop-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——循环逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-param-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——参数传递</a></li><li><a href="http://www.chunel.cn/archives/cgraph-condition-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——条件判断</a></li><li><a href="http://www.chunel.cn/archives/cgraph-aspect-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——面向切面</a></li><li><a href="http://www.chunel.cn/archives/cgraph-function-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——函数注入</a></li><li><a href="http://www.chunel.cn/archives/cgraph-message-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——消息机制</a></li><li><a href="http://www.chunel.cn/archives/cgraph-event-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——事件触发</a></li><li><a href="http://www.chunel.cn/archives/cgraph-timeout-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——超时机制</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-1-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（一）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-2-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（二）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-3-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（三）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-4-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（四）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-5-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（五）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-6-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（六）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-performance-1" target="_blank">纯序员给你介绍图化框架的简单实现——性能优化（一）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-distance-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——距离计算</a><br /><br><br></li><li><a href="http://www.chunel.cn/archives/listen-to-coder" target="_blank">CGraph 主打歌——《听码农的话》</a></li><li><a href="http://www.chunel.cn/archives/cgraph-anniversary-introduce" target="_blank">聊聊我写CGraph的这一年</a></li><li><a href="http://www.chunel.cn/archives/cgraph-awesome-cpp" target="_blank">从零开始主导一款收录于awesome-cpp的项目，是一种怎样的体验?</a></li><li><a href="http://www.chunel.cn/archives/cgraph-compare-taskflow-v1" target="_blank">炸裂！CGraph性能全面超越taskflow之后，作者却说他更想…</a></li><li><a href="http://www.chunel.cn/archives/cgraph-max-para-size" target="_blank">以图优图：CGraph中计算dag最大并发度思路总结</a></li><li><a href="http://www.chunel.cn/archives/cgraph-kunanniversary-introduce" target="_blank">一文带你了解练习时长两年半的CGraph</a><br /><br><br></li></ul><hr /><h4 id="%E4%B8%AA%E4%BA%BA%E4%BF%A1%E6%81%AF" tabindex="-1">个人信息</h4><ul><li>微信: ChunelFeng</li><li>邮箱: <a href="mailto:chunel@foxmail.com" target="_blank">chunel@foxmail.com</a></li><li>个人网站：<a href="http://www.chunel.cn" target="_blank">www.chunel.cn</a></li><li>github地址: <a href="https://github.com/ChunelFeng" target="_blank">https://github.com/ChunelFeng</a></li></ul><p><img src="http://www.chunel.cn/upload/2021/08/image-a63a5ca7d9104993a51c19d88e25454d.png" alt="image" /></p>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[一文带你了解练习时长两年半的CGraph]]></title>
                <link rel="alternate" type="text/html" href="http://www.chunel.cn/archives/cgraph-kunanniversary-introduce" />
                <id>tag:http://www.chunel.cn,2023-10-06:cgraph-kunanniversary-introduce</id>
                <published>2023-10-06T14:34:06+08:00</published>
                <updated>2023-10-06T19:25:28+08:00</updated>
                <author>
                    <name>Chunel</name>
                    <uri>http://www.chunel.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p>各位朋友大家好，我是不会写代码的纯序员——Chunel Feng。CGraph从立项到现在，已经有两年半的时间了。回顾这一坤年中，几乎每个假期的日日夜夜 ，从设计、开发、答疑、测试、推广、到社区活动，基本都是亲身参与，亲力亲为。整理起来，感慨颇多，又是一篇很长的文章。。。</p><p>我们注意到，很多项目，刚开始加入的人，都是很容易了解思路，并且跟着团队一起迭代升级的。但是，随着功能的不断迭代，后期加入的朋友，很难快速的了解整个项目的全貌和思路。还有，随着人数的增加和功能的不断演进，架构的一致性、可读性和可重用性，又成了一个重大的问题——这可能就是屎山的由来。</p><center><p><img src="http://www.chunel.cn/upload/2023/08/image-1691308160805.png" alt="image-1691308160805" /></p></center><p>为了避免这一问题，在CGraph开发的过程中，我们持续通过架构调整、文章、视频介绍的方式，来缓解这一问题。我们也会在每次大的改动之前，做一些防腐的工作，但总觉得做的还不够多，不够好。无论是代码，还是文字，还是视频，都只会积重难返。</p><p>今天，趁着一坤年的日子，我们再次对之前的所有工作，做一个整理和总结，希望可以帮你做到——<strong>了解CGraph，看着一篇就够了</strong> 的目标。</p><p>按照惯例，首先上源码和视频：</p><ul><li><p><a href="https://github.com/ChunelFeng/CGraph" target="_blank">【github】CGraph 项目链接</a></p></li><li><p><a href="https://www.bilibili.com/video/BV1mk4y1v7XJ" target="_blank">【B站视频】CGraph 入门篇</a> <br></p></li><li><p><a href="https://www.bilibili.com/video/BV1CN411w7XB" target="_blank">【B站视频】CGraph 功能篇</a> <br></p></li><li><p><a href="https://www.bilibili.com/video/BV1B84y1D7Hs" target="_blank">【B站视频】CGraph 应用篇</a> <br></p></li></ul><h2 id="%E5%8A%9F%E8%83%BD%E5%9B%9E%E9%A1%BE" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>功能回顾</td></tr></table></h2><p>我们来回顾一下功能吧。刚开始的时候，<strong>我们实现了基于图的调度逻辑，支持了包含条件、子图等功能的group逻辑</strong>。然后，我们设计了一套<strong>全pipeline内部共享的参数传递机制</strong>，一直沿用至今。这两块内容，其实就奠定了一个 parallal-flow 框架的基础功能。后期的所有升级和重构，基本上也都是围绕着这两块内容进行的。</p><p>接下来，我们集中精力，针对线程池的调度能力，做了很多优化和适配，并且一直持续至今。通过测试，整体调度性能大幅度提高，我们也因此，有机会再各个维度上思考，并且接受了一些大佬的指导，收获颇多。</p><p><img src="http://www.chunel.cn/upload/2023/10/image.png" alt="image" /></p><p>为了更好的投入使用，我们还<strong>引入了类似切面、定时器、事件等功能</strong>。帮助大家更好的设计和实现自己的pipeline逻辑。之后，我们就进入了v2.x的时代。。。</p><p>之所以做了大版本的升级，<strong>主要是因为CGraph从官方层面开始正式支持项目了</strong>。支持的第一个项目就是人工智能及大数据领域的研究课题，可以说是起点很高了。也随着这个契机，我们加入了一些适配逻辑，完善了一些功能，但现在回头看，惊叹那个时候还有这么多的不完善，甚至糟糕的设计和瓶颈，也要向第一波吃螃蟹的朋友表示感激之情和歉意。</p><p>现在，CGraph支持了很多领域的很多项目，收到的大都是好评和感谢。但我相信，明年这个时候再回看，也会发现当前很多俗手 被一点点改正和优化。为此，我们也会不断的听取意见，持续努力，期待您多多指教。</p><p><img src="http://www.chunel.cn/upload/2023/01/image.png" alt="image" /></p><p>再后来，我们<strong>实现了message机制，在pipeline内部或者跨pipeline，完成了更多维度、更大场景的dag调度逻辑</strong>，我愿称之为绝杀。。。我们还优化了执行逻辑，提供了可视化的链路结构图和性能分析图。有个小tips，作为项目发起者的我，却不是CGraph当前真正执行逻辑的作者。在这里，我们感谢来自批里的家人和社区小伙伴的贡献。</p><p>近期吧，我们根据用户的反馈的需求，<strong>完成了对执行逻辑的异步兼容，实现了超时逻辑和部分其他相关逻辑</strong>。简便程度超出我的预期，补充了并行流图中的重要一环。</p><p>总结下来，其中有很多功能，都是刚开始的时候觉得不可实现，或者当时认为已经很不错的。但继续往下看看，总会发现还有很多可能性，很多不足。我们小团队整体的意识和视野，也就是在这些过程中，一步一步的开阔和提升。</p><h2 id="%E6%80%BB%E7%BB%93%E5%B1%95%E6%9C%9B" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>总结展望</td></tr></table></h2><p>一坤年之后，回到刚开始建立CGraph项目的星巴克，回想这些日子发生的事情，遇到的和帮助过我们的人，收到的鼓励、支持、反馈和意见，每次支持用户的答疑和心得。让我觉得这些日子没有白费，在身体力行的完成为社会做点事情的目标。</p><p>我们的项目入选了 <a href="https://github.com/fffaraz/awesome-cpp" target="_blank">《awesome-cpp》</a><a href="https://github.com/521xueweihan/HelloGitHub/blob/master/content/HelloGitHub70.md" target="_blank">《HelloGithub 第70期》</a> 等知名榜单，也被多个社区和技术交流群推荐和讨论，支持的项目覆盖研究领域、开源社区、大大小小的IT公司。这些都是对我们付出的肯定。</p><p>我们也曾闹出过比较严重的乌龙。我们曾测得，CGraph的性能在各个场景都可以超越taskflow。但回头却发现，无法复现当时的测试结论——至今也不知当时为什么会得出那些数据。也是后来，我们又持续进行了多轮竭尽所能的优化，才在部分指标上，实现反超。虽然我一直认为，压测性能并不能代表太多，但我们一定会在性能优化的方向不断前进。</p><p><img src="http://www.chunel.cn/upload/2023/03/image-1679218564855.png" alt="image-1679218564855" /></p><p>我们也有我们的问题和局限性，我们并不了解异构计算，并不能很清楚的梳理不同硬件设备、计算资源、操作系统带来的异同。项目虽然是跨平台的，但却不支持gpu等算力资源，也缺少系统级别的针对性的优化。taskflow很多的调度优化策略，我们至今也没能完全搞懂和借鉴。当然，还有很多类似或者相关的项目可以调研，这些都是我们最好的老师。</p><p>十一之间假期，有机会和一位超级资深大佬聊了很长时间相关的内容，让我深深感受到，我们对这个领域、这个知识点的了解还是太少，研究还是太浅。深深的感觉到，我们说的很多，但是做的很少。也正是这一幕一幕，时刻提醒我们，<strong>要多听多看，不要让思维长期陷入死局，要寻求更多更广的指导和合作。不能自我满足，更不要盲目自嗨</strong>。不要为过去一点点小小的成就沾沾自喜，也不要迷失于当前太多看不懂、想不通、猜不透的现状。朝着更高更快更强的目标努力，我们总能一点点的进步，把未知变成已知，把思维编程实现。</p><p>讲到这里，我们按照时间顺序 介绍了CGraph大体功能的迭代过程和用途，也回顾了我们在这一过程中，我们取得的成果和现有的不足。接下来，还有很多坤年，我们还可以做更多、更深的事情。也期待您的加入和支持。</p><blockquote><p>最后一句话，是一个不好的消息：明天就要上班了。。。。。。</p></blockquote><center><p><img src="http://www.chunel.cn/upload/2020/10/mmqrcode1602771241876-546dc2b18b5044d2a2808c5d19ad2394.png" alt="mmqrcode1602771241876" /></p></center><pre><code>                                                             [2023.10.06 by Chunel]</code></pre><h4 id="%E6%8E%A8%E8%8D%90%E9%98%85%E8%AF%BB" tabindex="-1"><strong>推荐阅读</strong></h4><ul><li><a href="http://www.chunel.cn/archives/cgraph-run-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——执行逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-loop-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——循环逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-param-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——参数传递</a></li><li><a href="http://www.chunel.cn/archives/cgraph-condition-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——条件判断</a></li><li><a href="http://www.chunel.cn/archives/cgraph-aspect-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——面向切面</a></li><li><a href="http://www.chunel.cn/archives/cgraph-function-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——函数注入</a></li><li><a href="http://www.chunel.cn/archives/cgraph-message-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——消息机制</a></li><li><a href="http://www.chunel.cn/archives/cgraph-event-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——事件触发</a></li><li><a href="http://www.chunel.cn/archives/cgraph-timeout-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——超时机制</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-1-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（一）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-2-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（二）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-3-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（三）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-4-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（四）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-5-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（五）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-6-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（六）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-performance-1" target="_blank">纯序员给你介绍图化框架的简单实现——性能优化（一）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-distance-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——距离计算</a><br /><br><br></li><li><a href="http://www.chunel.cn/archives/listen-to-coder" target="_blank">CGraph 主打歌——《听码农的话》</a></li><li><a href="http://www.chunel.cn/archives/cgraph-anniversary-introduce" target="_blank">聊聊我写CGraph的这一年</a></li><li><a href="http://www.chunel.cn/archives/cgraph-awesome-cpp" target="_blank">从零开始主导一款收录于awesome-cpp的项目，是一种怎样的体验?</a></li><li><a href="http://www.chunel.cn/archives/cgraph-compare-taskflow-v1" target="_blank">炸裂！CGraph性能全面超越taskflow之后，作者却说他更想…</a></li><li><a href="http://www.chunel.cn/archives/cgraph-max-para-size" target="_blank">以图优图：CGraph中计算dag最大并发度思路总结</a><br /><br><br></li><li><a href="https://www.bilibili.com/video/BV1mk4y1v7XJ" target="_blank">【B站视频】CGraph 入门篇</a> <br></li><li><a href="https://www.bilibili.com/video/BV1CN411w7XB" target="_blank">【B站视频】CGraph 功能篇</a> <br></li><li><a href="https://www.bilibili.com/video/BV1B84y1D7Hs" target="_blank">【B站视频】CGraph 应用篇</a> <br></li></ul><hr /><h4 id="%E4%B8%AA%E4%BA%BA%E4%BF%A1%E6%81%AF" tabindex="-1">个人信息</h4><ul><li>微信: ChunelFeng</li><li>邮箱: <a href="mailto:chunel@foxmail.com" target="_blank">chunel@foxmail.com</a></li><li>个人网站：<a href="http://www.chunel.cn" target="_blank">www.chunel.cn</a></li><li>github地址: <a href="https://github.com/ChunelFeng" target="_blank">https://github.com/ChunelFeng</a></li></ul><p><img src="http://www.chunel.cn/upload/2021/08/image-a63a5ca7d9104993a51c19d88e25454d.png" alt="image" /></p>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[纯序员给你介绍图化框架的简单实现——超时机制]]></title>
                <link rel="alternate" type="text/html" href="http://www.chunel.cn/archives/cgraph-timeout-introduce" />
                <id>tag:http://www.chunel.cn,2023-09-09:cgraph-timeout-introduce</id>
                <published>2023-09-09T15:06:57+08:00</published>
                <updated>2023-10-04T16:21:53+08:00</updated>
                <author>
                    <name>Chunel</name>
                    <uri>http://www.chunel.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p>各位朋友大家好，我是不会写代码的纯序员——Chunel，很高兴又和大家见面了。往前一算，有两个月没有更新博客了。这算不算是一种超时呢？——这一句话，引出了后面的全文，嘿嘿。</p><p>在开发的过程中，我们经常会遇到一些奇奇怪怪的问题，导致异常一步一步的堆积。我举个例子，比如：在一个链路中，先请求远端的 server，然后拿到结果后，简单处理一下，最后在控制台上打印结果。</p><center> <p><img src="http://www.chunel.cn/upload/2023/01/image-1674885197839.png" alt="image-1674885197839" /></p></center><p>就这么简单的一个流程，也可能会出现很多意想不到的坑。就比如，如果server中，不小心走到了一个sleep 1000s的逻辑，那pipeline的第一个node，就很长时间都走不完了对吧。后面的逻辑，就在那里干等着了。或者说server内部实际上已经执行出错了，我们还不知道捏。</p><p>为了解决类似的这种问题，我们花了一些时间，最终在<strong>色丶图</strong>的执行逻辑中，加入了超时机制。接下来，就跟大家介绍一下相关的内容。</p><p>按照惯例，首先上源码：</p><blockquote><p><a href="https://github.com/ChunelFeng/CGraph" target="_blank">https://github.com/ChunelFeng/CGraph</a></p></blockquote><h2 id="%E8%B6%85%E6%97%B6%E8%AE%BE%E7%BD%AE" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>超时设置</td></tr></table></h2><p>pipeline流程中，有些逻辑涉及到数据库、三方服务等，不可避免的会遇到执行时间不确定的情况。在这个情况下，我们可以通过 setTimeout 接口，来设置当前 node的最大执行时间。</p><pre><code class="language-cpp">enum class GElementTimeoutStrategy {    AS_ERROR = 1,   // 当做异常处理（默认）    HOLD_BY_PIPELINE = 2,  // pipeline run执行完成之前，等待结束    NO_HOLD = 3,    // 不等待结束。非特殊场景，强烈不推荐使用，不排除个别平台会出现崩溃的情况};GElement* setTimeout(CMSec timeout, GElementTimeoutStrategy strategy)</code></pre><p>我们来看一下这个接口哈。<strong>第一个参数超时时长</strong>，单位是ms，代表当前node执行超过这个时间之后，就会结束执行。</p><p><strong>第二个参数是超时的处理策略</strong>。默认情况下，执行超时就直接返回错误，pipeline也就随之结束了。这个是默认的策略。</p><p>第二种呢，也是比较推荐的处理策略，就是超时了也不退出，而是继续走接下来的流程。不过呢，会在pipeline整体执行结束之前，统一等待这些超时逻辑执行完毕——这样做是因为 node中可能会对全局的GParam参数进行写入或读取，pipeline开始和结束的瞬间，也会对GParam进行处理。框架层面必须避免这个之间带来的影响。</p><p>第三种捏，也就是 <code>NO_HOLD</code>，就是pipeline结束之前不做回收，强烈不推荐。原因就是可能出现上面提到的影响——除非你确定这样做，对你的整体流程，没有任何影响。</p><p>具体使用代码，可以参考这个demo：<a href="https://github.com/ChunelFeng/CGraph/blob/main/tutorial/T22-Timeout.cpp" target="_blank">CGraph timeout tutorial</a></p><h2 id="%E8%B6%85%E6%97%B6%E5%88%A4%E6%96%AD" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>超时判断</td></tr></table></h2><p>前面我们提到过一个问题，就是当前的element执行超时了之后，如果不当做错误返回，后面的逻辑就会继续执行。</p><p>考虑这样一种情况：一个node，执行的真实时长是5s，但是超时时间设置为1s。那么，在如果它在第4s的时候，将pipeline中的一个 GParam的参数的值做了修改，那后面（第4s后）执行的node中，获取的 GParam的值是修改后的值么？</p><p>答案是：是的。因为在CGraph中，全局共享一处GParamManager管理类，在其中的任何改动，都是全局可见的。而且这种改动，有时候的确是必要的，或者说是符合开发者预期的。</p><p>但是，如果你想确保，当前node在超时后，就无法再修改GParam，那就需要在修改前，做一个判断：<code>isTimeout()</code>。如果返回值为true的话，那表明当前已经处于超时状态了。</p><center><p><img src="http://www.chunel.cn/upload/2021/07/image-efc81a3291634d35a3b94ea0f6e938dd.png" alt="image" /></p></center><p>还有一点需要说明的是，如果当前执行的group超时，或者这group中的所有正在执行的node，和未执行的node，都被认定为超时。</p><h2 id="%E7%9B%B8%E5%85%B3%E9%80%BB%E8%BE%91" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>相关逻辑</td></tr></table></h2><p>围绕着超时机制展开的，还有一些其他的逻辑。比如，您可以在某个特定阶段，通过 GFence 等待你指定的前面的某几个超时节点执行完成。也可以通过GSome 来实现类似弱依赖的功能。相关的视频，我们已经放到B站上，欢迎观看：</p><ul><li><a href="https://www.bilibili.com/video/BV1bw411D78J/?spm_id_from=333.788&amp;vd_source=2c7baed805c6cb33d630d5d4546cf0be" target="_blank">B站视频 GSome</a></li><li><a href="https://www.bilibili.com/video/BV1eh4y1P7mK/?spm_id_from=333.788&amp;vd_source=2c7baed805c6cb33d630d5d4546cf0be" target="_blank">B站视频 GFence</a></li></ul><h2 id="%E6%9C%AC%E7%AB%A0%E5%B0%8F%E7%BB%93" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>本章小结</td></tr></table></h2><p>CGraph从开始到现在，已经经历了近一坤年。最近，在v2.5.0之后，我们在有超时逻辑的异步执行上，做了很多事情，弥补了在并行执行逻辑中的重要一环。相信这些改变，也会帮助大家更好的去完成自己的dag逻辑。</p><p>我们博客也维护很长时间了，这些日子，项目的结构和设计发生了很多变化，我们也没有来及去讲原先文章里的介绍做调整。有些新朋友，看到最早的博客，对照当前的代码，跟我们反馈，说是已经面目全非了——当然，一段时间后，这一篇文章也会面目全非，哈哈。在这里，我们表示抱歉。</p><p>其实，CGraph的设计思路挺简单的，代码行文也是高度统一的，相信你看完其中一个部分，就可以触类旁通的了解其他的内容。后期，我们会尽可能通过视频的方式，跟大家讲清楚CGraph的内部设计原理。</p><p>也欢迎对这些内容感兴趣的朋友，添加我的个人微信，随时交流指教。如果能pr一些内容，或是提出一些不足，那可就更好了。</p><center><p><img src="http://www.chunel.cn/upload/2020/10/mmqrcode1602771241876-546dc2b18b5044d2a2808c5d19ad2394.png" alt="mmqrcode1602771241876" /></p></center><pre><code>                                                             [2023.10.04 by Chunel]</code></pre><h4 id="%E6%8E%A8%E8%8D%90%E9%98%85%E8%AF%BB" tabindex="-1"><strong>推荐阅读</strong></h4><ul><li><a href="http://www.chunel.cn/archives/cgraph-run-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——执行逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-loop-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——循环逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-param-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——参数传递</a></li><li><a href="http://www.chunel.cn/archives/cgraph-condition-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——条件判断</a></li><li><a href="http://www.chunel.cn/archives/cgraph-aspect-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——面向切面</a></li><li><a href="http://www.chunel.cn/archives/cgraph-function-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——函数注入</a></li><li><a href="http://www.chunel.cn/archives/cgraph-message-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——消息机制</a></li><li><a href="http://www.chunel.cn/archives/cgraph-event-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——事件触发</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-1-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（一）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-2-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（二）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-3-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（三）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-4-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（四）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-5-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（五）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-6-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（六）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-performance-1" target="_blank">纯序员给你介绍图化框架的简单实现——性能优化（一）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-distance-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——距离计算</a><br /><br><br></li><li><a href="http://www.chunel.cn/archives/listen-to-coder" target="_blank">CGraph 主打歌——《听码农的话》</a></li><li><a href="http://www.chunel.cn/archives/cgraph-anniversary-introduce" target="_blank">聊聊我写CGraph的这一年</a></li><li><a href="http://www.chunel.cn/archives/cgraph-awesome-cpp" target="_blank">从零开始主导一款收录于awesome-cpp的项目，是一种怎样的体验?</a></li><li><a href="http://www.chunel.cn/archives/cgraph-compare-taskflow-v1" target="_blank">炸裂！CGraph性能全面超越taskflow之后，作者却说他更想…</a></li><li><a href="http://www.chunel.cn/archives/cgraph-max-para-size" target="_blank">以图优图：CGraph中计算dag最大并发度思路总结</a><br /><br><br></li><li><a href="https://www.bilibili.com/video/BV1mk4y1v7XJ" target="_blank">【B站视频】CGraph 入门篇</a> <br></li><li><a href="https://www.bilibili.com/video/BV1CN411w7XB" target="_blank">【B站视频】CGraph 功能篇</a> <br></li><li><a href="https://www.bilibili.com/video/BV1B84y1D7Hs" target="_blank">【B站视频】CGraph 应用篇</a> <br></li></ul><hr /><h4 id="%E4%B8%AA%E4%BA%BA%E4%BF%A1%E6%81%AF" tabindex="-1">个人信息</h4><p>微信: ChunelFeng<br />邮箱: <a href="mailto:chunel@foxmail.com" target="_blank">chunel@foxmail.com</a><br />个人网站：<a href="http://www.chunel.cn" target="_blank">www.chunel.cn</a><br />github地址: <a href="https://github.com/ChunelFeng" target="_blank">https://github.com/ChunelFeng</a></p><p><img src="http://www.chunel.cn/upload/2021/08/image-a63a5ca7d9104993a51c19d88e25454d.png" alt="image" /></p>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[纯序员给你介绍图化框架的简单实现——性能优化（一）]]></title>
                <link rel="alternate" type="text/html" href="http://www.chunel.cn/archives/cgraph-performance-1" />
                <id>tag:http://www.chunel.cn,2023-08-05:cgraph-performance-1</id>
                <published>2023-08-05T15:10:27+08:00</published>
                <updated>2023-12-23T13:30:40+08:00</updated>
                <author>
                    <name>Chunel</name>
                    <uri>http://www.chunel.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p>大家好，我是不会写代码的纯序员——Chunel，有一阵子没写文章了，主要是因为前阵子我把更多的精力，放到了B站的视频录制上面。也欢迎大家去B站搜索CGraph，了解相关的内容。</p><p>与此同时，我们并没有停止迭代和优化的脚步，并且在大家的帮助和建议之下，完成了不少性能点的优化。接下来，我打算分几个章节，来给大家介绍一下其中优化的思路和使用的方法。</p><p>因为内容已经涉及到一些优化选项，本篇文章默认，读者已经对CGraph有了一些了解，或者是已经使用入门了。如果还不了解相关内容的朋友，我们也推荐您阅读之前的文章，或者去B站上搜素一下相关视频集合。</p><p>按照惯例，首先上源码：</p><blockquote><p><a href="https://github.com/ChunelFeng/CGraph" target="_blank">https://github.com/ChunelFeng/CGraph</a></p></blockquote><center><p><img src="http://www.chunel.cn/upload/2023/03/image-1678011326067.png" alt="image-1678011326067" /></p></center><h2 id="cpu%E5%8D%A0%E7%94%A8%E7%8E%87" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>cpu占用率</td></tr></table></h2><p>之前有人跟我们反馈过，在落地的过程中，哪怕是只写几个 sleep(1s)的算子，系统的cpu占用也会很高——这一定程度上阻碍的后续的落地流程。我们也注意到这个问题了，主要的原因，是我们在设计线程池的时候，为了提升并发场景性能，默认做了一些busy loop+yield的操作。</p><p><img src="http://www.chunel.cn/upload/2023/08/image-1691246765185.png" alt="image-1691246765185" /></p><p>针对一些非计算密集型的pipeline，可以通过配置的方式，极大的降低系统的cpu占用率。具体的操作方式，可以参考 <a href="https://github.com/ChunelFeng/CGraph/blob/main/tutorial/T01-Simple.cpp" target="_blank">T01-Simple</a> 这里的注释，只需要将主线程个数设置为0，并且设置开辟n个辅助线程执行就好了。具体执行效果，见下图：</p><pre><code class="language-cpp">UThreadPoolConfig config;config.default_thread_size_ = 0;config.secondary_thread_size_ = 8;    // 需要执行的线程数pipeline-&gt;setUniqueThreadPoolConfig(config);</code></pre><p><img src="http://www.chunel.cn/upload/2023/08/image-1691225503730.png" alt="image-1691225503730" /></p><p>原理啊，主要就是修改了辅助线程的执行逻辑。当辅助线程从 pool的queue中无法获取任务的时候，会进入 wait_for 的状态，等待再有新的任务，被push进来。当然了，我们也在这个等待上，加了一份期限（目前是 100ms）。我愿称之为：<strong>常温超导态色丶图</strong>。</p><p>整体压测下来，性能跟全部用主线程（busy loop) 的情况基本持平。在这里要解释一下，我们压测 2000w次，整体差了5s左右。这种性能的差距，在单次调度上，基本可以忽略。建议大家根据自己使用的实际情况，选择性使用更care性能的模式，还是更care cpu占用的模式——反正都是一个配置选项的问题。</p><p>特别强调一点，针对需要经常使用 yield（暂停） 和 resume（恢复）pipeline的朋友，我们强烈推荐采用全部辅助线程来执行所有的流程。可以在暂停的时候，将cpu占用率降到很低的水平。</p><p>【改动 2023.12.23】<br />CGraph v2.5.3 以及后期版本，降低了主线程在空闲时候的cpu占用率。用户采用默认设置（主线程）即可。</p><h2 id="%E4%B8%B2%E8%A1%8C%E6%89%A7%E8%A1%8C" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>串行执行</td></tr></table></h2><p>在推广的过程中，我们也会遇到一些用户，其实本身逻辑中，就没有并发部分——比如说，一段很长的流程的算法逻辑。但是呢，又想通过算子化（或者图化），来解决原有逻辑代码逻辑混乱、难以维护的问题。</p><p>这个时候，如果选择dag执行框架，就会将原有单个线程（主线程）就可以执行完毕的逻辑，强行放到了多个线程中去执行。我们知道，多线程内部也是有损耗的。代码的结构是清晰了，但是性能却降低了。这反而违背了算法更高、更快、更强的初衷。</p><center><p><img src="http://www.chunel.cn/upload/2023/08/image-1691308160805.png" alt="image-1691308160805" /></p></center><p>针对这种情况，我们提供了pipeline的串行执行功能。确保针对 <code>a-&gt;b-&gt;c-&gt;d</code> 的这种pipeline，运行的时候，不启动后台的线程池，而是直接通过主线程执行。在实现了算子化的同时，省掉了线程切换开销，从而兼顾了原有的性能。</p><p>又有一个问题：并不是所有的pipeline都是 <code>a-&gt;b-&gt;c-&gt;d</code> 这么简单的，其中还可能包含各种类型的group（特别是Condition/MultiCondition）。什么情况下，pipeline可以串行执行呢？为此，我们提供了 <code>pipeline-&gt;makeSerial();</code> 方法，在pipeline构建完成之后，直接调用这个方法，如果返回值是ok的话，那就说明当前的pipeline是可以串行的。并且这个时候，色图已经为你做好了串行执行的设置。</p><p><img src="http://www.chunel.cn/upload/2023/08/image-1691229461271.png" alt="image-1691229461271" /></p><p>接下来，直接运行就好了。返回值如果不是 ok的话，这个时候系统就不会做任何操作，按照原先的执行参数正常执行好了。</p><p>实验证明，在空跑大量节点的情况下，单线程串行执行的性能，比多线程好很多。推荐有类似需求的朋友，在执行之前，先 makeSerial 一下。反正返回不是ok了，也没有任何影响，对吧。</p><h2 id="perf%E5%8A%9F%E8%83%BD" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>perf功能</td></tr></table></h2><p>最新版本的色丶图中，我们加入了链路的perf功能，可以方便的查看整体链路上，具体哪里耗时。当然了，整体pipeline的耗时，基本上肯定都在节点内部，我们就没有标注出来调度的性能损耗。这个值通过node和node之间的时间差，也能计算出来。反正调度的损耗肯定是微秒级别的，可以忽略不计。</p><p>使用的方法，也非常简单，在pipeline构建完成之后（切记，是在pipeline构建完成，不需要init），直接调用 <code>pipeline-&gt;perf();</code> 方法，就可以了。这个时候，程序会在内部自动进行埋点，并且记录和分析运行耗时。最终，以 GraphViz的格式，打印出来。在perf函数执行完毕之后，我们也会自动清除自动加入pipeline内部的埋点信息，不会有任何性能方面的影响。</p><p>您只需要将屏幕上打印出来的信息，复制到 <a href="https://dreampuf.github.io/GraphvizOnline/" target="_blank">GraphViz Online</a> 网站，或者其他 graphviz 格式解析软件，就可以查看具体的性能分析了。如下图：</p><p><img src="http://www.chunel.cn/upload/2023/08/image-1691227058025.png" alt="image-1691227058025" /></p><p>我们稍微解释一下：</p><ul><li>start： 开始的运行的时间（距离pipeline init 开始，单位均为 ms）</li><li>finish： 最后一次运行的时间</li><li>per_cost： 平均每次执行的耗时</li><li>total_cost： 总耗时（仅单次执行的元素中，没有这一项）</li></ul><p>因为一个node（或者group），可能会运行多次，但可能并不是连续运行，所以 finish-start的时间，并不一定等于 total_cost ，这一点需要稍微强调一下。相信有了这个工具，您可以更好的编排和优化自己的流图了，思路可以有：</p><ul><li>耗时较长算子的专项优化</li><li>拆分较长时间的 element，设计并发逻辑</li><li>重新规划 element 的依赖关系或者执行顺序</li></ul><p>等等等，我们之前就是这样干的。做的时候，记得28定律，先把耗时的大头（或者是程序员）给优化掉。</p><h2 id="%E6%9C%AC%E7%AB%A0%E5%B0%8F%E7%BB%93" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>本章小结</td></tr></table></h2><p>本章小结中介绍的优化项，都是CGraph在演进过程中，各位用户反馈的真实使用意见。也是我们在开始的时候，设计不周全的点，有些内容，的确就是我自己的知识盲区。但好在，在大家的共同努力下，终于也都一一解决了。项目也是在这样的环境中，健康成长。</p><p><img src="http://www.chunel.cn/upload/2023/08/image-1691247566323.png" alt="image-1691247566323" /></p><p>by the way，前阵子，我们收到了来自上海国泰君安的朋友，发来的感谢信。在此感谢贵团队的信任和帮助。支持的用户多了，但收到感谢信这个事情吧，还是第一次，哈哈</p><p>这些年，我们支持了包含AI研究、推理引擎、互联网、游戏开发、应用开发、各种行业不同公司、组织、学校的各种需求，积累了丰富的落地经验。也希望这些经验，今后可以落地在自己手头和身边的工作中去。</p><p>色丶图也会不断的迭代，往更简单、更好用、更高效的路上，不断迭代。下面是我的个人微信，也欢迎大家随时交流，多多指教。一起为项目贡献自己的智慧。</p><center><p><img src="http://www.chunel.cn/upload/2020/10/mmqrcode1602771241876-546dc2b18b5044d2a2808c5d19ad2394.png" alt="mmqrcode1602771241876" /></p></center><pre><code>                                                             [2023.08.05 by Chunel]</code></pre><h4 id="%E6%8E%A8%E8%8D%90%E9%98%85%E8%AF%BB" tabindex="-1"><strong>推荐阅读</strong></h4><ul><li><a href="http://www.chunel.cn/archives/cgraph-run-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——执行逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-loop-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——循环逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-param-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——参数传递</a></li><li><a href="http://www.chunel.cn/archives/cgraph-condition-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——条件判断</a></li><li><a href="http://www.chunel.cn/archives/cgraph-aspect-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——面向切面</a></li><li><a href="http://www.chunel.cn/archives/cgraph-function-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——函数注入</a></li><li><a href="http://www.chunel.cn/archives/cgraph-message-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——消息机制</a></li><li><a href="http://www.chunel.cn/archives/cgraph-event-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——事件触发</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-1-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（一）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-2-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（二）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-3-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（三）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-4-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（四）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-5-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（五）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-6-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（六）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-distance-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——距离计算</a><br /><br><br></li><li><a href="http://www.chunel.cn/archives/listen-to-coder" target="_blank">CGraph 主打歌——《听码农的话》</a></li><li><a href="http://www.chunel.cn/archives/cgraph-anniversary-introduce" target="_blank">聊聊我写CGraph的这一年</a></li><li><a href="http://www.chunel.cn/archives/cgraph-awesome-cpp" target="_blank">从零开始主导一款收录于awesome-cpp的项目，是一种怎样的体验?</a></li><li><a href="http://www.chunel.cn/archives/cgraph-compare-taskflow-v1" target="_blank">炸裂！CGraph性能全面超越taskflow之后，作者却说他更想…</a></li><li><a href="http://www.chunel.cn/archives/cgraph-max-para-size" target="_blank">以图优图：CGraph中计算dag最大并发度思路总结</a><br /><br><br></li><li><a href="https://www.bilibili.com/video/BV1mk4y1v7XJ" target="_blank">【B站视频】CGraph 入门篇</a> <br></li><li><a href="https://www.bilibili.com/video/BV1CN411w7XB" target="_blank">【B站视频】CGraph 功能篇</a> <br></li></ul><hr /><h4 id="%E4%B8%AA%E4%BA%BA%E4%BF%A1%E6%81%AF" tabindex="-1">个人信息</h4><p>微信: ChunelFeng<br />邮箱: <a href="mailto:chunel@foxmail.com" target="_blank">chunel@foxmail.com</a><br />个人网站：<a href="http://www.chunel.cn" target="_blank">www.chunel.cn</a><br />github地址: <a href="https://github.com/ChunelFeng" target="_blank">https://github.com/ChunelFeng</a></p><p><img src="http://www.chunel.cn/upload/2021/08/image-a63a5ca7d9104993a51c19d88e25454d.png" alt="image" /></p>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[以图优图：CGraph中计算dag最大并发度思路总结]]></title>
                <link rel="alternate" type="text/html" href="http://www.chunel.cn/archives/cgraph-max-para-size" />
                <id>tag:http://www.chunel.cn,2023-05-14:cgraph-max-para-size</id>
                <published>2023-05-14T15:32:00+08:00</published>
                <updated>2023-07-23T23:41:37+08:00</updated>
                <author>
                    <name>Chunel</name>
                    <uri>http://www.chunel.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p>大家好，我是不会写代码的纯序员——Chunel，很高兴又在这里跟大家见面了，今天我们来聊聊CGraph 中最新引入的 <strong>并发度计算</strong> 功能，在介绍之前，我愿称之为<strong>色丶图</strong>至今为止，实现思路最复杂且被用到的概率最低的功能，哈哈。</p><p>在开始之前，我们照例，提供源码链接，跪求大家来个star。</p><blockquote><p><a href="https://github.com/ChunelFeng/CGraph" target="_blank">https://github.com/ChunelFeng/CGraph</a></p></blockquote><h2 id="%E9%97%AE%E9%A2%98%E6%8F%8F%E8%BF%B0" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>问题描述</td></tr></table></h2><p>色丶图项目从开始组件团队的时候，就一直很乐意做一些共建和赋能的事情。前阵子，有幸被选中成为开源推理引擎 <a href="https://github.com/zpye/SimpleInfer" target="_blank">SimpleInfer</a> 项目的底层调度组件，跟作者也有一些沟通。</p><p>聊到功能的时候，基本上所有的需求都是可以支持的。但是，有一个问题，当 SimpleInfer 组建好一个 pipeline之后，如何分配一个合理的执行线程数。</p><p>顿时，我就敏感起来了。前阵子做性能分析的时候，我们发现不同的线程数设置，对执行的效率影响是非常巨大的。CGraph虽然可以设置独立线程池，也支持不同的pipeline设置共享线程池，但其实解决的是<strong>是否能设置</strong>的问题，下一步要解决的，是<strong>怎么设置好</strong>的问题了。</p><blockquote><p>我们先来回顾一下这个问题哈：</p></blockquote><p><img src="http://www.chunel.cn/upload/2023/05/image.png" alt="image" /></p><p>我们看上面的这三个dag图哈，左上方的dag最大的情况下，需要2thread来执行，因为有依赖关系，即便在最极限的情况下，只有 B和C 两个算子（蓝色）可以并行。</p><p>右上方的那个呢，理论上C/D/E/F 四个算子有可能能并行，也就是说，最大的并发度是4。同理啊，下方的那个全连接网络，也是理论上最多只有 D/E/F/G 这4个算子，可以同时进行。</p><p><img src="http://www.chunel.cn/upload/2023/05/image-1684053063197.png" alt="image-1684053063197" /></p><p>如果设定的线程数超过了这个最大值，不仅不可能带来任何性能提升，而且会造成系统资源浪费，这个就叫做 <strong>dag 的最大并发度</strong>了。既然有这个理论背景了，下一步就是通过计算，把这个数字求出来了。</p><h2 id="%E9%97%AE%E9%A2%98%E5%88%86%E6%9E%90" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>问题分析</td></tr></table></h2><p>刚开始看到这个问题的时候，我感觉是比较简单的，直接做个bfs就好。但后来看看，并没有这么简单。比如上方右上角的那个图吧，如果做 bfs的话，应该是 【A,BCD,EF,G】，max值是3（即为 BCD），这个明显和实际不符。</p><p>再想想，能否给 dag转成 对应的二维矩阵，然后通过矩阵论的方式来解决。看了一下，貌似也不行。因为dag中，连接的信息是不全的。再拿右上角的图来举例子，我们是无法一次性看出来 A和E，C和E之间是否有联系的——A和E中间隔着一个B，C和E是真的没有关系。</p><p>我们再想回溯（或者着色），遇到稍微深一点的图，都会导致复杂度的极度上升。貌似实现起来也很难哦，没啥好的思路。</p><p><img src="http://www.chunel.cn/upload/2023/05/image-1684054624401.png" alt="image-1684054624401" /></p><p>无奈中，我们来请教 chatgpt吧。我们发现，原以为无敌的gpt的回答结果，其实就是最基本的层序遍历，这个只能在最简单的场景中，正确结果。稍微复杂一点，计算的结果值就是错的了。国内厂商提供的大模型，更是一言难尽，刚开始提供的代码，根本无法通过编译。<em>整个问答的过程，像极了面试写题的时候，不会却非要写点啥的纯序员本员了，哈哈。</em></p><p>最后，我们去谷歌和 leetcode 上搜索，都找不到对应的解决方案——连靠谱一点的分析也没有。<strong>所以，可以说，接下来的内容，应该是大家在网上能看到的第一篇靠谱的方法了，啊哈哈哈哈。</strong></p><h2 id="%E9%97%AE%E9%A2%98%E8%A7%A3%E6%B3%95" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>问题解法</td></tr></table></h2><p>本着搞不定就放弃的原则，这篇文章，就已经结束了。</p><p>哦，不不不，意识到这是一个比较有意义的基础问题，还是要尝试去解决一下的。于是，我们请教了身边的很多朋友。经过长时间的讨论、尝试和分析，终于来自杭电计算机院的 <a href="https://github.com/Hanano-Yuuki" target="_blank">倪博士</a>，提出了理论可行，且可以实现（复杂度较低）的算法方案。</p><p>下面，我们就以下图（左上角）中的DAG图，来拆解一下计算过程。</p><p><img src="http://www.chunel.cn/upload/2023/05/image-1684056362546.png" alt="image-1684056362546" /></p><blockquote><ol><li>求出全路径</li></ol></blockquote><p>这一步比较简单，做个dfs就可以了。可以看出，上图中一共有三条完整路径，分别是 A-B-D-F，A-B-E-F，A-C-F ，如图中右上方所示</p><blockquote><ol start="2"><li>根据全路径，得出所有可达点对</li></ol></blockquote><p>看一下上图的蓝色字体部分，表示的是每一条路径中的点对（point pair）的集合。需要说明的是，这个点集是无向并去重的。比如，path1 和 path2中，都包含 AB 的点集，直接去重即可。</p><p>这一步的作用，得出所有的前后依赖关系，特别是可以一次性看出 D和A之间，是有依赖关系的——前面聊过，这在原先的dag中，是不容易直接看出这个关系的，因为中间有B。</p><blockquote><ol start="3"><li>根据上面的点集信息，得到对应的二维矩阵</li></ol></blockquote><p>看上面的点集信息，如果是 有AB，则 AB和BA的位置，均设置为1（这个是无权图）；又如没有BC，则对应的位置都写0。还有就是，对角线上都写0。</p><center><p><img src="http://www.chunel.cn/upload/2023/05/image-1684057551636.png" alt="image-1684057551636" /></p></center><blockquote><ol start="4"><li>求出上图（二维矩阵）的最大独立集</li></ol></blockquote><p>然后，求出上图中的最大独立集合。集合中元素的个数，就是我们需要求的最大并发度。且集合中的元素，就是无法并发的节点。</p><h2 id="%E6%B1%82%E5%9B%BE%E7%9A%84%E6%9C%80%E5%A4%A7%E7%8B%AC%E7%AB%8B%E9%9B%86" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>求图的最大独立集</td></tr></table></h2><p>原先的问题解决了，但又引入了一个新的问题——如何求 图的最大独立集。我搜索了一下，并没有什么很常用的算法，来完成这个功能。但是，我们可以将这个问题，转移成另外一个等价的问题：</p><blockquote><p>原图的最大独立集 = 原图补图的最大团</p></blockquote><p>补图的意思，就是将原图中的0和1值，进行倒转。最大团是图（数据结构）中的一个基本概念，是结点数最多的极大团。如果不了解这一块的内容，可以看一下这篇文章：<a href="https://www.jianshu.com/p/dabbc78471d7" target="_blank">团、极大团、最大团</a> ，或者 <a href="https://www.bilibili.com/video/BV1Ks4y1V7Cc/?spm_id_from=333.337.search-card.all.click&amp;vd_source=2c7baed805c6cb33d630d5d4546cf0be" target="_blank">最大团介绍视频(B站)</a></p><p>而解最大团的方法，通常有最大团回溯算法，和Bron-Kerbosch算法，有兴趣的朋友可以自己去搜索一下。By the way，chatgpt 是可以直接写出来以上两种算法的，亲测可用。</p><h2 id="%E6%9C%AC%E7%AB%A0%E5%B0%8F%E7%BB%93" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>本章小结</td></tr></table></h2><p>就这样，这个困扰了我们近半个月的难题，就被解决了。具体的实现代码，在CGraph 的 <a href="https://github.com/ChunelFeng/CGraph/blob/main/src/GraphCtrl/GraphElement/_GOptimizer/GMaxParaOptimizer.h" target="_blank">GMaxParaOptimizer.h</a> 中，有兴趣的朋友可以去看看源码，里面包含了非常详细的注释。</p><p>通过这个问题的解决过程，我也有了一些自己的感悟。大家经常会讨论<code>刷leetcode有啥用</code> 这样的问题。我想，如果只是处理一些常规的流程性质的coding，是基本没啥用的。</p><p>但是，如果遇到一些需要分析思路的问题的话，刷没刷过的差距就显而易见了——这也在一定程度上，区分出了高手和普通程序员。上面的几个流程，每个小流程都是leetcode中的题目，没刷过题（或打过竞赛）的朋友，其实是很难有这些思路的。当然了，我相信，如果只是纯刷也不行，我们还应该有很好的意识，将其中学到的知识活学活用，合理的用到对应的领域——不只面试哈，哈哈哈哈。</p><p>这里要大赞倪童鞋，倪童鞋在学校从事AI基础算法研究工作，水平、经验和阅历明显超过身边其他人一大截。我们一群工作了很多年的人（比如坤叔、乐哥）在群里讨论了两个星期也没解决的问题，倪童鞋仅用一晚上，就提出了可行的思路和实现方案。不得不承认，人跟人之间，差距还是很大的。<strong>当然了，我这里不是说坤叔和乐哥不行啊，我没有这个意思哦，请大家不要误会哦</strong>。</p><center><p><img src="http://www.chunel.cn/upload/2023/05/image-1684059578440.png" alt="image-1684059578440" /></p></center><p>还有一点遗憾，就是当前算法，在面对包含子图，甚至是子图套子图的dag的时候，是无法完成计算的。我想了一下，也并不是简单的加入分治的思路就可以解决的。这个问题，我们会继续关注，也欢迎有兴趣的朋友加入我们，一起讨论交流，多多指教。</p><p>最近工作中，极大的依赖chatgpt的功能，这次终于找到了AI暂时无法解决的问题。同时，这也应该是全网第一篇，完整介绍dag并发度计算方法的博客——至少在中文领域是这样的。很高兴有机会跟大家分享这些内容。</p><p>今天还是母亲节，祝福天下母亲节日快乐。虽然不在身边，但我还是决定出去吃点好吃的，为老妈庆祝一下节日，哈哈。</p><center><p><img src="http://www.chunel.cn/upload/2020/10/mmqrcode1602771241876-546dc2b18b5044d2a2808c5d19ad2394.png" alt="mmqrcode1602771241876" /></p></center><pre><code>                                                          [2023.05.14 by Chunel]</code></pre><h4 id="%E6%8E%A8%E8%8D%90%E9%98%85%E8%AF%BB" tabindex="-1"><strong>推荐阅读</strong></h4><ul><li><a href="http://www.chunel.cn/archives/cgraph-run-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——执行逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-loop-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——循环逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-param-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——参数传递</a></li><li><a href="http://www.chunel.cn/archives/cgraph-condition-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——条件判断</a></li><li><a href="http://www.chunel.cn/archives/cgraph-aspect-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——面向切面</a></li><li><a href="http://www.chunel.cn/archives/cgraph-function-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——函数注入</a></li><li><a href="http://www.chunel.cn/archives/cgraph-message-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——消息机制</a></li><li><a href="http://www.chunel.cn/archives/cgraph-event-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——事件触发</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-1-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（一）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-2-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（二）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-3-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（三）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-4-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（四）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-5-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（五）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-6-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（六）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-distance-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——距离计算</a><br /><br><br></li><li><a href="http://www.chunel.cn/archives/listen-to-coder" target="_blank">CGraph 主打歌——《听码农的话》</a></li><li><a href="http://www.chunel.cn/archives/cgraph-anniversary-introduce" target="_blank">聊聊我写CGraph的这一年</a></li><li><a href="http://www.chunel.cn/archives/cgraph-awesome-cpp" target="_blank">从零开始主导一款收录于awesome-cpp的项目，是一种怎样的体验?</a></li><li><a href="http://www.chunel.cn/archives/cgraph-compare-taskflow-v1" target="_blank">炸裂！CGraph性能全面超越taskflow之后，作者却说他更想…</a><br /><br><br></li><li><a href="https://www.bilibili.com/video/BV1mk4y1v7XJ" target="_blank">【B站视频】CGraph 入门篇</a> <br></li><li><a href="https://www.bilibili.com/video/BV1CN411w7XB" target="_blank">【B站视频】CGraph 功能篇</a> <br></li></ul><hr /><h4 id="%E4%B8%AA%E4%BA%BA%E4%BF%A1%E6%81%AF" tabindex="-1">个人信息</h4><p>微信: ChunelFeng<br />邮箱: <a href="mailto:chunel@foxmail.com" target="_blank">chunel@foxmail.com</a><br />个人网站：<a href="http://www.chunel.cn" target="_blank">www.chunel.cn</a><br />github地址: <a href="https://github.com/ChunelFeng" target="_blank">https://github.com/ChunelFeng</a></p><p><img src="http://www.chunel.cn/upload/2021/08/image-a63a5ca7d9104993a51c19d88e25454d.png" alt="image" /></p>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[关于 CGraph和 taskflow 性能对比测试的相关说明]]></title>
                <link rel="alternate" type="text/html" href="http://www.chunel.cn/archives/cgraph-compare-taskflow-v2" />
                <id>tag:http://www.chunel.cn,2023-04-27:cgraph-compare-taskflow-v2</id>
                <published>2023-04-27T22:19:45+08:00</published>
                <updated>2023-11-27T23:52:33+08:00</updated>
                <author>
                    <name>Chunel</name>
                    <uri>http://www.chunel.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p>之前写过一篇文章 <a href="http://www.chunel.cn/archives/cgraph-compare-taskflow-v1" target="_blank">炸裂！CGraph性能全面超越taskflow之后，作者却说他更想…</a> ，介绍 色丶图和 taskflow 之间串行、并行和 dag的性能对比，并且记录了相关数据，并保存截图。但并非适用于所有的平台。</p><p>希望大家可以根据自己的场景和环境，做出自己的性能对比。也欢迎大家给我们提供性能优化的意见和建议，随时期待您的指教。</p><p>再次申明一下，压测情况和实际使用的情况，有较大的区别。实际使用的情况下，更多性能损耗在算子内部，压测环境下损耗主要在调度上。请大家根据实际情况，对比分析。</p><hr /><p>以下代码，均推荐用当前最新版本测试：</p><blockquote><p>CGraph</p></blockquote><pre><code class="language-cpp">#include &quot;MyGAspect/MyTimerAspect.h&quot;using namespace CGraph;class MyEmptyNode : public GNode {public:    CStatus run() override {        return CStatus();    }};void tutorial_concurrent_32() {    // 并行的执行32次，对应第1个例子，8thread，32并发，50w次    GPipelinePtr pipeline = GPipelineFactory::create();    CStatus status;    GElementPtr arr[32];    UThreadPoolConfig config;    config.default_thread_size_ = 8;    // 我的笔记本，是8核心的 macbook pro m1    config.secondary_thread_size_ = 0;    config.max_task_steal_range_ = 7;    config.max_thread_size_ = 8;    config.primary_thread_policy_ = CGRAPH_THREAD_SCHED_RR;    config.primary_thread_priority_ = 10;    config.primary_thread_empty_interval_ = 1;    config.primary_thread_busy_epoch_ = 3000;    config.monitor_enable_ = false;    // 关闭扩缩容机制    pipeline-&gt;setUniqueThreadPoolConfig(config);    for (auto &amp; i : arr) {        pipeline-&gt;registerGElement&lt;MyEmptyNode&gt;(&amp;i);    }    pipeline-&gt;setAutoCheck(false);    status += pipeline-&gt;init();    /** 其中流程进行计时 **/    MyTimerAspect asp;    asp.beginRun();    for (int t = 0; t &lt; 200000; t++) {        pipeline-&gt;run();    }    asp.finishRun(status);    // 这里会输出时间信息    /*******************/    status += pipeline-&gt;destroy();    GPipelineFactory::remove(pipeline);}void tutorial_serial_32() {    // 串行执行32次，对应第二个例子，1thread，32串行，1000w次    GPipelinePtr pipeline = GPipelineFactory::create();    CStatus status;    GElementPtr arr[32];    pipeline-&gt;registerGElement&lt;MyEmptyNode&gt;(&amp;arr[0]);    for (int i = 1; i &lt; 32; i++) {        pipeline-&gt;registerGElement&lt;MyEmptyNode&gt;(&amp;arr[i], {arr[i-1]});    }    pipeline-&gt;makeSerial();    pipeline-&gt;setAutoCheck(false);    status += pipeline-&gt;init();    /** 其中流程进行计时 **/    MyTimerAspect asp;    asp.beginRun();    for (int t = 0; t &lt; 1000000; t++) {        pipeline-&gt;run();    }    asp.finishRun(status);    // 这里会输出时间信息    /*******************/    status += pipeline-&gt;destroy();    GPipelineFactory::remove(pipeline);}void tutorial_dag() {    // 简单dag场景，对应第三个例子，2thread，dag，100w次    GPipelinePtr pipeline = GPipelineFactory::create();    CStatus status;    GElementPtr a,b1,b2,c1,c2,d;    UThreadPoolConfig config;    config.default_thread_size_ = 2;    // 我的笔记本，是8核心的 macbook pro m1    config.secondary_thread_size_ = 0;    config.max_task_steal_range_ = 1;    config.max_thread_size_ = 2;    config.primary_thread_empty_interval_ = 1;    config.primary_thread_busy_epoch_ = 3000;    config.monitor_enable_ = false;    // 关闭扩缩容机制    config.primary_thread_policy_ = CGRAPH_THREAD_SCHED_RR;    config.primary_thread_priority_ = 10;    pipeline-&gt;setUniqueThreadPoolConfig(config);    pipeline-&gt;setAutoCheck(false);    pipeline-&gt;registerGElement&lt;MyEmptyNode&gt;(&amp;a);    pipeline-&gt;registerGElement&lt;MyEmptyNode&gt;(&amp;b1, {a});    pipeline-&gt;registerGElement&lt;MyEmptyNode&gt;(&amp;b2, {b1});    pipeline-&gt;registerGElement&lt;MyEmptyNode&gt;(&amp;c1, {a});    pipeline-&gt;registerGElement&lt;MyEmptyNode&gt;(&amp;c2, {c1});    pipeline-&gt;registerGElement&lt;MyEmptyNode&gt;(&amp;d, {b2, c2});    pipeline-&gt;setGEngineType(GEngineType::DYNAMIC);    status += pipeline-&gt;init();    /** 其中流程进行计时 **/    MyTimerAspect asp;    asp.beginRun();    for (int t = 0; t &lt; 1000000; t++) {        pipeline-&gt;run();    }    asp.finishRun(status);    // 这里会输出时间信息    /*******************/    status += pipeline-&gt;destroy();    GPipelineFactory::remove(pipeline);}int main() {    for (int i = 0; i &lt; 5; i++) {        tutorial_concurrent_32();        // tutorial_serial_32();        // tutorial_dag();    }    return 0;}</code></pre><blockquote><p>taskflow</p></blockquote><pre><code class="language-cpp">#include &lt;taskflow/taskflow.hpp&gt;  // the only include you needclass CStatus {    int code = 0;    std::string info;    std::string path;};void demo1() {    tf::Executor executor(8);    tf::Taskflow taskflow(&quot;simple&quot;);    // 并行的32路    for (int i = 0; i &lt; 32; i++) {       auto x = taskflow.emplace([] {           return CStatus();        });    }    auto start_ts_ = std::chrono::high_resolution_clock::now();    for(int i = 0; i &lt; 200000; i++) {        executor.run(taskflow).wait();    }    std::chrono::duration&lt;double, std::milli&gt; span = std::chrono::high_resolution_clock::now() - start_ts_;    printf(&quot;----&gt; [taskflow] time cost is : [%0.2lf] ms \n&quot;, span.count());}void demo2() {    // 串行32个    tf::Executor executor(1);    tf::Taskflow taskflow;    auto task1 = taskflow.emplace([]() { return CStatus(); });    auto task2 = taskflow.emplace([]() { return CStatus(); });    auto task3 = taskflow.emplace([]() { return CStatus();  });    auto task4 = taskflow.emplace([]() { return CStatus();  });    auto task5 = taskflow.emplace([]() { return CStatus();  });    auto task6 = taskflow.emplace([]() { return CStatus();  });    auto task7 = taskflow.emplace([]() { return CStatus();  });    auto task8 = taskflow.emplace([]() { return CStatus();  });    auto task9 = taskflow.emplace([]() { return CStatus();  });    auto task10 = taskflow.emplace([]() { return CStatus();  });    auto task11 = taskflow.emplace([]() { return CStatus();  });    auto task12 = taskflow.emplace([]() { return CStatus();  });    auto task13 = taskflow.emplace([]() { return CStatus();  });    auto task14 = taskflow.emplace([]() { return CStatus();  });    auto task15 = taskflow.emplace([]() { return CStatus();  });    auto task16 = taskflow.emplace([]() { return CStatus();  });    auto task17 = taskflow.emplace([]() { return CStatus();  });    auto task18 = taskflow.emplace([]() { return CStatus();  });    auto task19 = taskflow.emplace([]() { return CStatus();  });    auto task20 = taskflow.emplace([]() { return CStatus();  });    auto task21 = taskflow.emplace([]() { return CStatus();  });    auto task22 = taskflow.emplace([]() { return CStatus();  });    auto task23 = taskflow.emplace([]() { return CStatus();  });    auto task24 = taskflow.emplace([]() { return CStatus();  });    auto task25 = taskflow.emplace([]() { return CStatus();  });    auto task26 = taskflow.emplace([]() { return CStatus();  });    auto task27 = taskflow.emplace([]() { return CStatus(); });    auto task28 = taskflow.emplace([]() { return CStatus();  });    auto task29 = taskflow.emplace([]() { return CStatus();  });    auto task30 = taskflow.emplace([]() { return CStatus();  });    auto task31 = taskflow.emplace([]() { return CStatus();  });    auto task32 = taskflow.emplace([]() { return CStatus();  });    task1.precede(task2);    task2.precede(task3);    task3.precede(task4);    task4.precede(task5);    task5.precede(task6);    task6.precede(task7);    task7.precede(task8);    task8.precede(task9);    task9.precede(task10);    task10.precede(task11);    task11.precede(task12);    task12.precede(task13);    task13.precede(task14);    task14.precede(task15);    task15.precede(task16);    task16.precede(task17);    task17.precede(task18);    task18.precede(task19);    task19.precede(task20);    task20.precede(task21);    task21.precede(task22);    task22.precede(task23);    task23.precede(task24);    task24.precede(task25);    task25.precede(task26);    task26.precede(task27);    task27.precede(task28);    task28.precede(task29);    task29.precede(task30);    task30.precede(task31);    task31.precede(task32);    auto start_ts_ = std::chrono::high_resolution_clock::now();    for(int i = 0; i &lt; 1000000; i++) {        executor.run(taskflow).wait();    }    std::chrono::duration&lt;double, std::milli&gt; span = std::chrono::high_resolution_clock::now() - start_ts_;    printf(&quot;----&gt; [taskflow] time cost is : [%0.2lf] ms \n&quot;,           span.count());}void demo3() {    // 简单dag图    tf::Taskflow taskflow;    auto [A, B1, B2, C1, C2, D] = taskflow.emplace(            // []() { return std::this_thread::sleep_for(std::chrono::milliseconds(1)); },            []() { return CStatus(); },            []() { return CStatus(); },            []() { return CStatus(); },            []() { return CStatus(); },            []() { return CStatus(); },            []() { return CStatus(); }    );    A.precede(B1, C1);    B1.precede(B2);    C1.precede(C2);    D.succeed(B2, C2);    // execute the workflow    tf::Executor executor(2);    auto start_ts_ = std::chrono::high_resolution_clock::now();    for (int i = 0; i &lt; 1000000; i++) {        executor.run(taskflow).wait();    }    std::chrono::duration&lt;double, std::milli&gt; span = std::chrono::high_resolution_clock::now() - start_ts_;    printf(&quot;----&gt; [taskflow] time cost is : [%0.2lf] ms \n&quot;,           span.count());}int main(){    for (int i = 0; i &lt; 5; i++) {        demo1();        // demo2();        // demo3();    }    return 0;}</code></pre>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[身边是否有985毕业却混得很惨的例子？]]></title>
                <link rel="alternate" type="text/html" href="http://www.chunel.cn/archives/ad-tea-leaf-v2" />
                <id>tag:http://www.chunel.cn,2023-04-05:ad-tea-leaf-v2</id>
                <published>2023-04-05T19:36:10+08:00</published>
                <updated>2023-04-05T19:57:38+08:00</updated>
                <author>
                    <name>Chunel</name>
                    <uri>http://www.chunel.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p><img src="http://www.chunel.cn/upload/2023/04/image.png" alt="image" /></p><h2 id="%E6%A2%81%E6%80%BB%E7%89%8C%E8%8C%B6%E5%8F%B6%EF%BC%8C%E6%98%8E%E5%89%8D%E8%8C%B6%E5%8D%96%E6%98%8E%E5%90%8E%E4%BB%B7%EF%BC%88%E9%A2%84%E8%AE%A1%E6%B8%85%E6%98%8E%E5%BD%93%E5%89%8D%E5%8F%91%EF%BC%89" tabindex="-1">梁总牌茶叶，明前茶卖明后价（预计清明当前发）</h2><ul><li>同事喝了绿油油，长辈喝了笑哈哈</li><li>晚辈喝了考上985 。。。其实也不是很重要了，毕业之后都是要出来卖茶叶的（我后面会分析原因的）</li></ul><h2 id="%E8%9B%A4%E8%9B%A4%EF%BC%8C%E6%83%B3%E4%B8%8D%E5%88%B0%E5%90%A7%EF%BC%8C%E8%BF%99%E6%98%AF%E4%B8%80%E6%9D%A1%E5%B9%BF%E5%91%8A" tabindex="-1">蛤蛤，想不到吧，这是一条广告</h2><ul><li>别催了别催了，已经在写了。。。梁总牌茶叶正式开启o2o销售模式。。。</li><li>一面之猿网开始接广告盈利了。。。不然服务器都买不起了。。。</li><li>你敢花钱投广告，我们就敢发。。。以下内容没有经过任何审核。。。</li></ul><p><img src="http://www.chunel.cn/upload/2023/04/image-1680693637197.png" alt="image-1680693637197" /></p><h2 id="%E6%9B%B4%E6%83%B3%E4%B8%8D%E5%88%B0%E7%9A%84%E6%98%AF%EF%BC%8C%E8%BF%99%E8%BF%98%E6%98%AF%E4%B8%80%E4%B8%AA%E5%8A%B1%E5%BF%97%E7%9A%84%E6%95%85%E4%BA%8B" tabindex="-1">更想不到的是，这还是一个励志的故事</h2><ul><li>朋友卖茶叶的事情，告诉我们，985学历不值钱，大厂履历也不值钱，工资都是零花钱。</li><li>朋友一掷千金在奥体中心旁边买8w一平的大别野，用的都是卖茶叶赚来的。。。</li></ul><p><img src="http://www.chunel.cn/upload/2023/04/image-1680694096635.png" alt="image-1680694096635" /></p><h2 id="%E4%BB%A5%E4%B8%8B%E6%89%8D%E6%98%AF%E5%B9%BF%E5%91%8A%E7%9A%84%E5%85%A8%E9%83%A8%E5%86%85%E5%AE%B9" tabindex="-1">以下才是广告的全部内容</h2><p><img src="http://www.chunel.cn/upload/2023/04/image-1680693679510.png" alt="image-1680693679510" /></p><ul><li>2023年新茶接受预定。礼盒装和罐装都支持，半斤起卖，价位自谈。</li><li>请加微信13296759836（不是本人）详聊～</li></ul><center><p><img src="http://www.chunel.cn/upload/2021/03/image-f91f65ea67a447bba748f067b2468178.png" alt="image.png" /></p></center><h2 id="%E5%8F%8B%E6%83%85%E6%8F%90%E9%86%92" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>友情提醒</td></tr></table></h2><p>加卖家微信的时候，请注明 <font color="red">不是通过【一面之猿网】找过来的</font>，以免被杀熟。</p><h1 id="%E8%BD%AC%E5%8C%96%E6%95%88%E6%9E%9C%E5%A5%BD%E7%9A%84%E8%AF%9D%EF%BC%8C%E4%B8%8B%E6%AC%A1%E6%88%91%E4%BB%AC%E5%8D%96%E6%B5%B7%E9%B2%9C%E5%92%8C%E9%A9%B4%E8%82%89%E3%80%82%E3%80%82%E3%80%82" tabindex="-1">转化效果好的话，下次我们卖海鲜和驴肉。。。</h1>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[炸裂！CGraph性能全面超越taskflow之后，作者却说他更想...]]></title>
                <link rel="alternate" type="text/html" href="http://www.chunel.cn/archives/cgraph-compare-taskflow-v1" />
                <id>tag:http://www.chunel.cn,2023-03-18:cgraph-compare-taskflow-v1</id>
                <published>2023-03-18T21:57:19+08:00</published>
                <updated>2023-07-28T00:02:32+08:00</updated>
                <author>
                    <name>Chunel</name>
                    <uri>http://www.chunel.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p>大家好，我是不会写代码却喜欢和chatgpt聊技术的纯序员——ChunelFeng。最近，被微软的chatgpt刷屏，也知道国内有些厂商跟进追随 ，产品被拿来各种对比。恰巧，前段时间知乎上，有人问我，CGraph（色丶图）和 <a href="https://github.com/taskflow/taskflow" target="_blank">taskflow</a> 之间对比，如何选择。这个问题，让我惊讶于居然已经有人，将小破图和行业的标杆产品拿来做对比了。</p><center><p><img src="http://www.chunel.cn/upload/2023/03/image-1679214815730.png" alt="image-1679214815730" /></p></center><p>taskflow是美国犹他大学开源的一款C++17编写的通用并行任务编程库，被广泛应用于科学计算、机器学习、网络编程的高性能计算任务中，也被广泛应用于科学研究领域和CAD相关产品的开发过程。nvidia, jetbrain,ros 等国际大厂和组织，都是taskflow的用户。</p><p><img src="http://www.chunel.cn/upload/2023/03/image-1679159151968.png" alt="image-1679159151968" /></p><p>CGraph从立项之初，就是想做一款简单好用的<code>类taskflow</code> 作品，也一直将taskflow作为模仿和对标的对象。期间，我们也几次和taskflow做过性能对比和竞品分析，结果都是差强人意的。我还记得，我之前来公司面试的时候，被问到CGraph和 taskflow的对比，性能如何。我只能说，功能和用法方面，巴拉巴拉巴拉。性能方面，目前“基本持平”吧。差在自信，懂的都懂，带带弟弟。</p><center><p><img src="http://www.chunel.cn/upload/2023/03/image-1679218564855.png" alt="image-1679218564855" /></p></center><p>终于，随着项目的不断发展和迭代，经过各位大神的帮助，和AI技术的辅助下，CGraph在实测的各种指标上，都已经完成了对taskflow的超越。从一个方面，回应了 <strong>这个事情，让别人来做，会有什么不一样</strong> 这个世纪难题。</p><blockquote><p>开始之前，先上源码链接：<a href="https://github.com/ChunelFeng/CGraph" target="_blank">https://github.com/ChunelFeng/CGraph</a></p></blockquote><h2 id="%E6%80%A7%E8%83%BD%E5%AF%B9%E6%AF%94" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>性能对比</td></tr></table></h2><p>我们来看一下各个指标上具体的性能对比。主要分为 <strong>多线程并发处理、长链路串行执行、模拟真实dag使用</strong> 这三个部分。</p><blockquote><ol><li>多线程并发处理</li></ol></blockquote><p>在并发处理层面，我们选用的是32节点同时并发执行，循环执行 50w次， 均开8线程处理。</p><pre><code class="language-cpp">void test() {    GPipelinePtr pipeline = GPipelineFactory::create();    CStatus status;    GElementPtr arr[32];    UThreadPoolConfig config;    config.default_thread_size_ = 8;    // 设定8个线程    config.max_thread_size_ = config.default_thread_size_;    config.monitor_enable_ = false;    // 关闭扩缩容机制    pipeline-&gt;setUniqueThreadPoolConfig(config);    for (int i = 0; i &lt; 32; i++) {        pipeline-&gt;registerGElement&lt;MyEmptyNode&gt;(&amp;arr[i]);    }    status += pipeline-&gt;init();    /** 其中流程进行计时 **/    for (int t = 0; t &lt; 500000; t++) {        pipeline-&gt;run();    }    /*******************/    status += pipeline-&gt;destroy();    GPipelineFactory::remove(pipeline);}</code></pre><center><p><img src="http://www.chunel.cn/upload/2023/03/image-1679148293563.png" alt="image-1679148293563" /></p></center><p>看图，这个场景主要考察框架的多并发（8cpu同时跑32线程任务）处理能力，CGraph是明显略胜一筹的。CGraph底层的线程池，采用了多队列的任务模型结构，近期在Ryan哥和风神的帮助下，又定位和优化了pool中的性能瓶颈，有效的减少了抢锁的流程和次数，从而提高了整体调度效率。<br /><br></p><blockquote><ol start="2"><li>长链路串行执行</li></ol></blockquote><p>这里，我们测试的是超长链路的场景下，框架的执行效率和优化程度。</p><center><p><img src="http://www.chunel.cn/upload/2023/03/image-1679149407003.png" alt="image-1679149407003" /></p></center><p>结果是令人振奋的。在串行执行的场景下，<strong>CGraph 的性能居然达到了taskflow的近三倍</strong>，直接秒杀的节奏。这主要得益于CGraph在底层解析dag结构的时候，采用了 linkable 的优化思路，针对这种情况，将dag可以 linkable的部分，提前退化成一个 list结构，从而极大的减少了调度层面的耗时。<br /><br></p><blockquote><ol start="3"><li>模拟真实dag使用</li></ol></blockquote><p>最后，我们来模拟的是一个真实的dag使用的例子。形成一个如上的dag结构，大家比较熟悉吧， 在项目过程中，也会经常遇到，对吧。PS：记得在执行值之前，先切换一下动态执行模式。</p><pre><code class="language-cpp">// 切换动态执行引擎，在dag逻辑较复杂的情况下面，推荐使用动态执行引擎。PS：默认是静态执行引擎pipeline-&gt;setGEngineType(GEngineType::DYNAMIC);</code></pre><center><p><img src="http://www.chunel.cn/upload/2023/03/image-1679154121217.png" alt="image-1679154121217" /></p></center><p>执行100w次，测下来CGraph的性能也是略优于taskflow的。这种场景，其实是最有必要说道说道的。大概一年前吧，我们也进行过类似的测试，taskflow的执行耗时，只有我们的一半左右。那个时候，项目还基本只有我一个人在维护，哈哈，是自己太菜无疑了，而且当时的思路比较僵化哈。</p><center><p><img src="http://www.chunel.cn/upload/2023/03/image-1679153890866.png" alt="image-1679153890866" /></p></center><p>后来，项目热闹起来了，加入的大佬也越来越多。Ryan哥通过多组实验，精准定位了调度的性能瓶颈点和异常逻辑，为接下来的事情指明了前进方向。叶神实现了动态解图的算法，并且提出了基于提升亲和性的优化思路，用于上述dag调度的情况下，提升显著。虽然色图中的线程池，早已比传统的线程池模型在性能方面 强出数倍，风神还是找出了其中执行思路的一些不足之处，并且进一步完善。</p><p>还有，借助最近大火的chargpt，我们也进行针对性的提问，并收获了不少优化并发编程方面经验和思路。一套组合拳下来，终于完成了最后的反杀。更要说明的一点是，CGraph还提供了很多调度相关参数的配置选项，相信根据实际情况进行调优之后，性能一定会更胜一筹的。</p><p>当然，我们的社区里还有像坤叔和 <a href="https://www.zhihu.com/people/jun-jun-42-59/posts" target="_blank">乐哥</a> 这种级别的行业大佬，但在这个过程中，并没有发挥什么积极的作用。我在这里稍微强调一下这个事情，意思是小破图今后还有很多进步的空间，并不是说他们不行哦，真的不是说他们不行，大家不要误会哦，嘿嘿嘿。</p><h2 id="%E5%A6%82%E4%BD%95%E9%80%89%E6%8B%A9" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>如何选择</td></tr></table></h2><p>下面，我就结合自己的一些想法，加入一些主观倾向的来回答一下，也算是稍微梳理一下 <strong>我们的护城河在哪里</strong> 这个问题。我想，选择之前，首先你需要搞想清楚，是否有一些刚需项是无法绕过的。比如：</p><p>仅色丶图支持：</p><ul><li>cpp14或者 cpp11的工程版本（关键）</li><li>做整体链路的参数管控（关键）</li><li>自定义group逻辑</li><li>多dag之间交互</li><li>定时器功能</li></ul><p>仅taskflow支持：</p><ul><li>提供gpu支持（关键）</li><li>提供perf功能</li></ul><center> <p><img src="http://www.chunel.cn/upload/2023/03/image-1679217533834.png" alt="image-1679217533834" /></p></center><p>比如，你的工程版本是cpp11的，你目前就只能选择色丶图，因为不太现实为了引入框架，给taskflow整个工程做降级，对吧。或者，你经常需要实现一些自定义的组合逻辑，甚至是将这些组合的功能开放到你的上游，那你也只能选色图。如果需要在pipeline中，频繁的用到异构计算的话，taskflow就比较合适了，CGraph在这一块并没有做过多的优化。</p><p>其他需要考虑的，就是一些比较主观的因素了。比如，如果你比较喜欢面向对象编程的思路，推荐色图，因为色图中，一切皆对象。如果比较喜欢面向流程，直接写lambda的话，taskflow比较适合。</p><center><p><img src="http://www.chunel.cn/upload/2023/03/image-1679215165432.png" alt="image-1679215165432" /></p></center><p>如果您的逻辑中，主要都是大量的串行逻辑的话，也是推荐选色丶图的。上面的测试结果也表明了，在这种情况下，对比taskflow，色图针对性优化，将调度性能提升了3倍左右。</p><p>还有就是如果你日常reading和coding过程中，对英文资料不是很擅长的话，也推荐你用色丶图。色丶图代码中，包含了很详细的中文注释，所有的介绍文章也是中文的——比如这一篇，属于国货崛起了，哈哈。今后，我们还考虑推出相关的视频介绍，敬请期待。</p><h2 id="%E4%B8%AA%E4%BA%BA%E6%84%9F%E6%83%B3" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>个人感想</td></tr></table></h2><p>最后，也要表明我的一些观点。性能对比只是竞品分析的一个角度，极端的暴力测试，其实和项目的整体完成度，没有太大的关系，更多是可以打个广告，或者做一页ppt罢了。如果总抓着一个性能点不放、太focus在优化层面的话，最终的结果，可能会像在缺少参与度的偏门项目上拿很多金牌一样，榜单上很好看，但没有太大的实际作用。</p><p>作为一个开发者，我认为一款项目成功与否的几个重要标志，还是应该是<strong>项目落地场景和周边生态是否丰富，支持了多少用户，和为这些用户解决了多少实际生产过程中遇到的问题</strong>。总结一下，就是我刚开始在github上写代码的时候，说的一句口号，build together,  power another （共建，赋能）。</p><p>在测试过程中，有一点我还是挺感慨的。当我 chatgpt 给我介绍一下taskflow的时候，它的表现很好。当我让它帮我生成一个测试用例的时候，它也可以很快的完成。</p><p><img src="http://www.chunel.cn/upload/2023/03/image-1679200232127.png" alt="image-1679200232127" /></p><p>而当你问它 CGraph是什么，或让它生成一个例子的时候，它的回答，明显就答非所问了。这也可以从一个侧面，说明了taskflow自身的确算是行业的事实标准，而我们的差距，还有很多很多。当然，这也可能跟chatgpt的训练数据，截止到2021年9月为止有关。</p><p>我们项目是2021年才开始的，近期我们的项目也有了长足的进步。不光是性能方面，在落地场景方面，色丶图在一线互联网大厂和其他几个领域的公司，也都有了一批用户。我们还积极参与到高校的研究课题中，尽可能的给在校学生提供一些力所能及的帮助。值得一提的是，项目第一个加入的童鞋，和截止目前最后一个加入的童鞋，都是国内985高校的在校学生哦。后浪还是很强劲的，也不知道淘汰我们，是chatgpt，还是下一届毕业的应届生，哎。</p><center><p><img src="http://www.chunel.cn/upload/2023/03/image-1679215054178.png" alt="image-1679215054178" /></p></center><p>近期，被chatgpt的各种新闻刷屏，并且已经在实际工作中，体会受益。想着面对这波排山倒海的降维打击，我们这波普通的程序员也就像300年前的纺织工人一样，随时可能面临着行业的洗牌和颠覆。而这又可能才是这个行业正确打开姿势的开端。偶尔用业余时间做的一些小工作，如果能在这无时不在发生的变革中，有一点小小的贡献，而不是随波逐流和听天由命，那自己也会是非常开心的。</p><p>好了，感谢你阅读我的博客，也希望这篇文章，能够帮助您在 CGraph 和 taskflow 之间做选择的时候，提供一些参考。很高兴能在项目开始的两年之内，在性能测试方面超越taskflow。但接下来还有很长的路要走，很多的东西要学，很多功能要去实现和完善，很多的瓶颈要去克服。期待你的交流、指导和加入。</p><center><p><img src="http://www.chunel.cn/upload/2020/10/mmqrcode1602771241876-546dc2b18b5044d2a2808c5d19ad2394.png" alt="mmqrcode1602771241876" /></p></center><pre><code>                                                                       [2023.03.19 by Chunel]</code></pre><hr /><h4 id="%E6%8E%A8%E8%8D%90%E9%98%85%E8%AF%BB" tabindex="-1"><strong>推荐阅读</strong></h4><ul><li><a href="http://www.chunel.cn/archives/cgraph-run-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——执行逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-loop-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——循环逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-param-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——参数传递</a></li><li><a href="http://www.chunel.cn/archives/cgraph-condition-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——条件判断</a></li><li><a href="http://www.chunel.cn/archives/cgraph-aspect-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——面向切面</a></li><li><a href="http://www.chunel.cn/archives/cgraph-function-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——函数注入</a></li><li><a href="http://www.chunel.cn/archives/cgraph-message-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——消息机制</a></li><li><a href="http://www.chunel.cn/archives/cgraph-event-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——事件触发</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-1-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（一）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-2-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（二）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-3-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（三）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-4-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（四）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-5-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（五）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-6-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（六）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-distance-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——距离计算</a><br /><br><br></li><li><a href="http://www.chunel.cn/archives/listen-to-coder" target="_blank">CGraph 主打歌——《听码农的话》</a></li><li><a href="http://www.chunel.cn/archives/cgraph-anniversary-introduce" target="_blank">聊聊我写CGraph的这一年</a></li><li><a href="http://www.chunel.cn/archives/cgraph-awesome-cpp" target="_blank">从零开始主导一款收录于awesome-cpp的项目，是一种怎样的体验?</a><br /><br><br></li><li><a href="https://www.bilibili.com/video/BV1gY4y1x7JT?spm_id_from=333.337" target="_blank">【B站视频】CGraph 快速引入的方法介绍</a></li></ul><hr /><h4 id="%E4%B8%AA%E4%BA%BA%E4%BF%A1%E6%81%AF" tabindex="-1">个人信息</h4><p>微信: ChunelFeng<br />邮箱: <a href="mailto:chunel@foxmail.com" target="_blank">chunel@foxmail.com</a><br />个人网站：<a href="http://www.chunel.cn" target="_blank">www.chunel.cn</a><br />github地址: <a href="https://github.com/ChunelFeng" target="_blank">https://github.com/ChunelFeng</a></p><p><img src="http://www.chunel.cn/upload/2021/08/image-a63a5ca7d9104993a51c19d88e25454d.png" alt="image" /></p>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[纯序员给你介绍图化框架的简单实现——事件触发]]></title>
                <link rel="alternate" type="text/html" href="http://www.chunel.cn/archives/cgraph-event-introduce" />
                <id>tag:http://www.chunel.cn,2023-02-05:cgraph-event-introduce</id>
                <published>2023-02-05T16:38:19+08:00</published>
                <updated>2023-07-09T23:35:17+08:00</updated>
                <author>
                    <name>Chunel</name>
                    <uri>http://www.chunel.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p>大家好， 我是不会写代码却天天想着用户触达的纯序员——Chunel Feng。在之前的文章中，我们说过CGraph 是一个简单好用的DAG调度流图。注入到pipeline中的每个node，在所有依赖的node执行完毕之后，就会开始执行。这也是dag调度中，最常规的应用。</p><p>但是，这些设定，有一些情况是无法满足的。比如，我想每间隔一分钟，上报一下当前系统的状态。又或者，我想在特定的时刻（比如，进入某个判断分支的时候），上报某个具体的参数值。这是当前的dag调度机制，无法做到的。</p><p>今天，我们接着之前的话题，跟大家聊一下CGraph中新加入的触发机制，和一些新的实用功能。</p><h2 id="%E5%AE%9A%E6%97%B6%E8%A7%A6%E5%8F%91" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>定时触发</td></tr></table></h2><p>设想一种这样的情况哈，我在执行pipeline的过程中，随时可能出现意外。我需要添加一个看门狗机制，每隔一分钟，来输出一下系统的负载信息，从而在负载过高的情况下，可以告警出来。</p><p>这个时候，我们需要用到CGraph的 GDaemon 机制了。GDaemon的逻辑，实际上是在pipeline 运行的同时，在后台开启一个可设置时间的 timer，跟pipeline在两个不同的维度执行，跟pipeline不占用线程资源，但可以获取pipeline中所有的GParam信息。</p><p>这样说，可能有点抽象。直接上代码：</p><p><img src="http://www.chunel.cn/upload/2023/02/image.png" alt="image" /></p><p>可见，在这个tutorial中，有一个 daemonTask逻辑 每间隔2s 就执行一次。当然了，在 GDaemon的方法中，还可以向GNode一样 ，通过 <code>CGRAPH_GET_GPARAM</code> 方法获取pipeline中的参数信息。或者是通过实现GTemplateDaemon&lt;&gt;的构造函数中，来实现配置参数的传递。</p><p>GDaemon比较适合用来做一些跟pipeline主流程无关，但是却需要反复执行的逻辑。比如，生成一些系统状态的时序数据，或者持续监控pipeline是否在运行等。<br /></br></p><h2 id="%E4%BA%8B%E4%BB%B6%E8%A7%A6%E5%8F%91" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>事件触发</td></tr></table></h2><p>除了定时触发逻辑之外，还有一种事件触发的逻辑。比如，当多个 node 执行到特定的分支逻辑的时候，需要同步/异步的触发一个特定的功能。这个时候我们如何操作呢？这里推荐用CGraph中提供的GEvent(事件)机制。</p><p>一共有三步：<br />1，实现事件类的具体功能<br />2，在节点中，需要执行的地方，调用 notify 接口，完成事件的同步/异步触发。<br />3，在pipeline 中注册一个具体类型的事件</p><p>废话不多说，直接来看代码：</p><pre><code class="language-cpp">// 实现具体的事件类class MyPrintEvent : public CGraph::GEvent {public:    CVoid trigger(CGraph::GEventParamPtr param) override {        CGRAPH_SLEEP_MILLISECOND(100)    // 留100ms的耗时，便于看出来同步/异步触发机制        auto myParam = CGRAPH_GET_GPARAM_WITH_NO_EMPTY(MyParam, &quot;param1&quot;)        CGraph::CGRAPH_ECHO(&quot;----&gt; trigger [%d] times, iValue = [%d]&quot;, times_++, myParam-&gt;iValue);    }private:    int times_ = 0;};// 在节点中，通过key和type来触发事件class MyEventNode : public CGraph::GNode {public:    CStatus run () override {        CStatus status;        CGraph::CGRAPH_ECHO(&quot;[%s], before event notify&quot;, this-&gt;getName().c_str());        /**         * 模拟在这里，触发一个 event信息，同名的事件被异步执行         * 从打印结果可以看出，after event send 这条信息，提前执行         * 执行的时候，和pipeline公用同一个线程池资源         */        notify(&quot;my-print-event&quot;, GEventType::ASYNC);        CGraph::CGRAPH_ECHO(&quot;[%s], after event notify&quot;, this-&gt;getName().c_str());        return status;    }};// 第三步，在pipeline 中注册对应的事件，在node中的notify就可以触发对应的事件了void pipeline() {    GPipelinePtr pipeline = GPipelineFactory::create();    GElementPtr a,b = nullptr;    CStatus status;    status += pipeline-&gt;registerGElement&lt;MyEventNode&gt;(&amp;a, {}, &quot;nodeA&quot;);    status += pipeline-&gt;registerGElement&lt;MyNode1&gt;(&amp;b, {a}, &quot;nodeB&quot;);    // 在pipeline中添加一个事件信息，通过 notify(&quot;my-print-event&quot;)触发    pipeline-&gt;addGEvent&lt;MyPrintEvent&gt;(&quot;my-print-event&quot;);    pipeline-&gt;process();    GPipelineFactory::clear();}</code></pre><p>大家可以会疑问，在node中触发一个方法，那自己随便封装一个函数，调用一下不就可以了么。为啥还要绕这一阵子，专门封装一个GEvent逻辑。</p><p>这里安利一下GEvent的好处：</p><ul><li>首先，GEvent机制使得逻辑更加清晰，调用方式更加规范，方便后期做进一步的扩展和优化。</li><li>第二，GEvent机制复用了CGraph的pipeline内部的计算资源，用户不需要再关心开辟和释放线程的逻辑。相比自己开线程，有一些性能优势。</li><li>还有，GEvent机制提供了一个位置，可以根据需要 获取/修改 pipeline中的任意多个参数的值，同时也便于内部参数追踪。这是自己写函数所不具备的功能，妙啊。</li></ul><p>这样总结下来，GEvent比较适合处理在pipeline流程中，会多次在特定场景下执行的逻辑，且兼容了同步、异步两种模式。希望可以对您的项目有所帮助。</p><h2 id="%E8%89%B2%E5%9B%BE%E4%BD%A0%E7%9A%84%E8%89%B2%E5%9B%BE" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>色图你的色图</td></tr></table></h2><p>在新版本的CGraph中，我们提供了英文版本的readme，再也不是awesome-cpp榜单中，唯一一个没有英文readme的项目了，哈哈。也正式启用了项目的中文名称：<b>色丶图（Color Graph）</b></p><center><p><img src="http://www.chunel.cn/upload/2023/03/image-1678011326067.png" alt="image-1678011326067" /></p></center><p>来自阿里的Ysi童鞋，实现了CGraph的可视化功能，使得<b>色丶图</b>变得跃然纸上。大家只需要在完成 pipeline 的算子注册逻辑之后，调用 pipeline-&gt;dump()方法，就可以在控制台上，得到一串信息。</p><p><img src="http://www.chunel.cn/upload/2023/03/image.png" alt="image" /></p><p>把图中的信息，复制到 <a href="https://dreampuf.github.io/GraphvizOnline/" target="_blank">Graphviz Online</a> 上，就能看到图的整体结构信息了</p><p><img src="http://www.chunel.cn/upload/2023/03/image-1678006860853.png" alt="image-1678006860853" /></p><p>pipeline中的region，condition 等复杂逻辑，也可以被很好的展示，<b>真正做到了所得及所见</b>，有效避免了架构图来回调整，或者文档长时间未更新导致的前后不一。</p><p>同时，在最新版本中，还提供了参数链路追踪功能。打开之后，可以一键查看每个GParam的调用链路，方便做数据回溯和问题定位。也欢迎大家尝试使用。CGraph进入了v2.3.x 版本之后，主要在链路的可视化，问题追踪方面 做了一些功能优化，可能也是因为在工作中遇到的一些问题的，一些自己的思路和解法吧。</p><h2 id="%E6%9C%AC%E7%AB%A0%E5%B0%8F%E7%BB%93" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>本章小结</td></tr></table></h2><p>很久没有写文章了。这期间，CGraph项目一直有断断续续的更新，其中也有叶神，风神等人的很多贡献。Ryan哥 更是在一个凌晨两点的 15分钟内，定位和解决了我一年都没能解决的问题，水平之高，难以想象。<b>可能这就是大神和普通人之间的差距吧</b>。</p><p>在Mirror哥的帮助下，CGraph成功降级到了cpp11的语法，并且所有功能都适配了windows系统。相比于只支持cpp17的taskflow（我在github找到的绝大部分 类taskflow产品，都只支持cpp17或以上版本），在实际项目落地方面，有了质的飞跃。同时，<b>我们也迎来了来自百度和腾讯等超一线大厂的第一批用户</b>，这都是极好的背书，我们也会在工作之余，尽可能的做好配合和支持工作。希望可以互相学习，一起成长。</p><center><p><img src="http://www.chunel.cn/upload/2023/03/image-1678011160357.png" alt="image-1678011160357" /></p></center><p>当聊到选用CGraph的原因和优势，很多人都会说到一个词：<b>轻量级</b>。是的，我们一直坚持，用原生cpp开发，没有引入任何的三方包。这的确使得我们在功能扩展上，受到了很大的限制。但高度简洁一致的代码风格，便于使用者根据实际需求做自己的扩展，且从根本上避免的各种蛋疼的环境和依赖问题。这也就是我们眼中的轻量级，但又不是那种轻飘飘的感觉。</p><p>接下来的业余时间，我们发力Python版本的开发和适配。希望可以在打通生态方面，有一个里程碑式的突破。届时，各种新玩法和新功能都会接踵而至。同时，为了保障工程自身的稳定迭代，对应的ci/ct也有专人设计和开发中。届时，CGraph工程下，又会多了一个很重要的新文件夹，哈哈。</p><p>目前，项目的官方贡献者已经有6位，后面5个人均大佬。也期待更多朋友的加入，一起共建赋能。下面是我的个人微信号，欢迎随时交流指教。</p><center><p><img src="http://www.chunel.cn/upload/2020/10/mmqrcode1602771241876-546dc2b18b5044d2a2808c5d19ad2394.png" alt="mmqrcode1602771241876" /></p></center><pre><code>                                                          [2023.03.05 by Chunel]</code></pre><hr /><h4 id="%E6%8E%A8%E8%8D%90%E9%98%85%E8%AF%BB" tabindex="-1"><strong>推荐阅读</strong></h4><ul><li><a href="http://www.chunel.cn/archives/cgraph-run-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——执行逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-loop-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——循环逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-param-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——参数传递</a></li><li><a href="http://www.chunel.cn/archives/cgraph-condition-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——条件判断</a></li><li><a href="http://www.chunel.cn/archives/cgraph-aspect-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——面向切面</a></li><li><a href="http://www.chunel.cn/archives/cgraph-function-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——函数注入</a></li><li><a href="http://www.chunel.cn/archives/cgraph-message-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——消息机制</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-1-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（一）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-2-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（二）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-3-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（三）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-4-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（四）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-5-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（五）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-6-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（六）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-distance-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——距离计算</a><br /><br><br></li><li><a href="http://www.chunel.cn/archives/listen-to-coder" target="_blank">CGraph 主打歌——《听码农的话》</a></li><li><a href="http://www.chunel.cn/archives/cgraph-anniversary-introduce" target="_blank">聊聊我写CGraph的这一年</a></li><li><a href="http://www.chunel.cn/archives/cgraph-awesome-cpp" target="_blank">从零开始主导一款收录于awesome-cpp的项目，是一种怎样的体验?</a><br /><br><br></li><li><a href="https://www.bilibili.com/video/BV1gY4y1x7JT?spm_id_from=333.337" target="_blank">【B站视频】CGraph 快速引入的方法介绍</a></li></ul><hr /><h4 id="%E4%B8%AA%E4%BA%BA%E4%BF%A1%E6%81%AF" tabindex="-1">个人信息</h4><p>微信: ChunelFeng<br />邮箱: <a href="mailto:chunel@foxmail.com" target="_blank">chunel@foxmail.com</a><br />个人网站：<a href="http://www.chunel.cn" target="_blank">www.chunel.cn</a><br />github地址: <a href="https://github.com/ChunelFeng" target="_blank">https://github.com/ChunelFeng</a></p><p><img src="http://www.chunel.cn/upload/2021/08/image-a63a5ca7d9104993a51c19d88e25454d.png" alt="image" /></p>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[从零开始主导一款收录于awesome-cpp的项目，是一种怎样的体验?]]></title>
                <link rel="alternate" type="text/html" href="http://www.chunel.cn/archives/cgraph-awesome-cpp" />
                <id>tag:http://www.chunel.cn,2023-01-22:cgraph-awesome-cpp</id>
                <published>2023-01-22T12:45:44+08:00</published>
                <updated>2023-07-09T23:35:35+08:00</updated>
                <author>
                    <name>Chunel</name>
                    <uri>http://www.chunel.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<pre><code class="language-bash">谢邀！其实，根本没有人邀请，好么。 —— 郝博（华为数据库内核高级架构师）</code></pre><p>大家好，我是不会写代码却总想搞个大新闻的纯序员——ChunelFeng，最近正值新年假期，小纯在这里祝福大家新春快乐，兔飞猛进。</p><p>前阵子，有朋友聊到，CGraph仓库代码多起来了，想入门源码，一时之间不知道如何下手了。于是，小纯就抽假期的时间，给大家整理了一份xmind文件，梳理了CGraph的src文件夹下的所有内容简介和之间的关联，希望对大家有所帮助。</p><center><p><img src="http://www.chunel.cn/upload/2023/01/image-1674885656801.png" alt="image-1674885656801" /></p></center><p>再想，前阵子小破图作为唯一一个readme是非英文的项目，被收录于 <a href="https://github.com/fffaraz/awesome-cpp" target="_blank">awesome-cpp</a> 列表，想写一点内容来纪念一下，也正好是当做总结和今后的展望了。接下来，我就结合写CGraph项目的思路和过程，总结一下我的心得和感悟吧。当然，我知道这只是里程碑，但远不是终点。</p><h2 id="%E5%81%9A%E5%8A%A0%E6%B3%95" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>做加法</td></tr></table></h2><p>CGraph项目开始的定位，是一款简单好用的dag调度框架。刚开始，仅用了几天假期，就跑通了demo版本。然后，我们围绕着“简单好用”、“dag”、“调度”、“框架”这几个词，做了很多事情。</p><center><p><img src="http://www.chunel.cn/upload/2023/01/image-1674884967294.png" alt="image-1674884967294" /></p></center><p>我们优化了dag的解图思路，支持了静态、动态解图两种执行模式，可以自行切换，分别适配不同的场景需求。调度方面，我们对线程池进行了大量的调优，并且开放出来相关参数，供用户配置。也进行了一些有针对性的设计。</p><p>为了解决既有老逻辑的迁移到CGraph的问题，我们推出了函数注入的功能。使得原有的功能函数，甚至是代码片段，都可以很快的接入pipeline中，从而成为dag的一个部分。</p><center><p><img src="http://www.chunel.cn/upload/2023/01/image.png" alt="image" /></p></center><p>作为一款框架，我们在完成dag调度本职工作的同时，又加入了切面功能、定时触发和信号触发功能。针对类似视频的流式长链路数据处理，我们又引入了消息机制，来实现多个pipeline之间的交互，从而组成了一个更大的、可以分段执行的dag网络。</p><p>对比taskflow中没有涉及的参数管控，我们实现了一套参数管理机制，从而优化了整个链路的数据传递体验。也在性能几乎一致的情况下，解决了cpp11版本的兼容性。相比于最低支持cpp17的taskflow，极大的扩展了使用范围和适配情况。</p><h2 id="%E5%81%9A%E5%87%8F%E6%B3%95" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>做减法</td></tr></table></h2><p>我之前是玩三国杀的。早期，一共只有30+个武将，学习他们的技能非常简单。但是随着游戏版本的迭代，引入了几百个武将，每个武将又有自己的技能，这极大的增加了新人入门的门槛，也有很多人因此弃坑了。但不断迭代功能（武将），又是项目（游戏公司）继续发展下去的必由之路。</p><center><p><img src="http://www.chunel.cn/upload/2023/01/image-1674885005341.png" alt="image-1674885005341" /></p></center><p>为了尽可能的在演进的过程中保持简单，我们在coding的过程中，将所有的dag执行逻辑，抽象为 <code>object + manager</code> 的思路，形成了一套编程范式。用这种方式表达所有新增的功能，并且对外部提供一致的接口。</p><p>在开发过程中，我也养成一个意识，就是一块类似的代码，如果写了三次或以上，就需要考虑进行一些统一封装了。可以是函数，可以是类，可以是宏，也可以是模板。一阵子下来，项目的代码量降下来了，可维护性和可扩展性也高了。</p><p>我上学的时候几乎没写过代码，毕业后，想完成我做程序员的梦想，刚开始只能去一些很弱的公司。我比很多人更能理解，一个低端coder可能差到什么程度。不懂cmake？没用过submodule依赖？没编译过工程？没用过git？没用过docker？没用过linux？没有关系，不会可以慢慢学。我希望可以做到，无论你现在处于什么样的状态，只要是有需要的话，都可以跑起来CGraph的功能，看看效果，想想跟你的需求是否合拍，然后再决定是否进一步了解下去。</p><p>为此，我们通过宏定义，无缝的兼容了mac/linux/windows三种系统，不加入任何三方引入，没有任何环境依赖，兼容了C++11和以上所有的版本，并详尽的描述了项目编译和使用流程。即便是没有编译环境的机器，也可以通过online环境来查看代码和运行demo。</p><center><p><img src="http://www.chunel.cn/upload/2023/01/image-1674885149871.png" alt="image-1674885149871" /></p></center><p>做减法，还体现在一些功能层面的权衡。之前我们提过想实现一个any的group逻辑，效果是n个算子加入其中，只要其中有1个执行完成，就继续往下执行。但最终也因为可能会引入的一些长尾问题，而放弃了。今后我们会重新评估这个功能的可行性。</p><p>也正是因为我们在开发过程中持续做的这些减法，确保了当前功能的稳定性和可解释性。在今后演进的过程中，我们也会 keep CGraph simple and stupid，持续打磨简单好用的产品。</p><h2 id="%E5%81%9A%E4%B9%98%E6%B3%95" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>做乘法</td></tr></table></h2><p>一个人的力量终归太微小了。好在这一路上，慢慢的加入了很多伙伴。大家一起优化、推广、落地，一步一步实现着成倍、成指数级别的提升。</p><center><p><img src="http://www.chunel.cn/upload/2023/01/image-1674885197839.png" alt="image-1674885197839" /></p></center><p>去年上半年，浙大的王博士主导 <a href="https://github.com/whenever5225/GraphANNS/tree/multimodal_search" target="_blank">GraphANNS</a> 项目开始实施，通过将ANNS算法算子化+逐个调优的方式，来实现算法研究和优化。年中，百度的Yaha童鞋提供了项目对应的 sonarqube服务，基于此消灭了项目的一些冗余代码。来自魔门塔的 logerrors 童鞋贡献了xmake，丰富了项目的编译方式。下半年，阿里的Ysi童鞋，pr了动态解图的执行逻辑，极大的优化了复杂dag逻辑的执行效率。</p><p>我们的线程池优化的相关文章，也发布在doocs社区的公众号上，从而有了更广泛的触达。作为awesome-cpp上唯一一个没有英文readme的项目，我们英语18级的专业人士翻译的英文版本readme，也很快就要完成了。</p><p>CGraph项目本身是没有三方依赖，为了快速的扩展功能，丰富应用场景和领域，我们也开展了碰瓷名项目活动。在 <a href="https://github.com/CDev-io/CGraph-NIO" target="_blank">CGraph-NIO</a> 项目中，我们借用了<a href="https://github.com/sogou/workflow" target="_blank">sougou/workflow</a> 的网络能力，仅几行简单的代码，就实现了CGraph对外提供http并发服务的功能。感谢workflow项目负责人的支持，希望这篇文章可以帮忙做点引流，哈哈。</p><p>因为白嫖的成本太小且收益太高，让我深深的爱上了这个活动。接下来有空的时候，还会考虑一些数据库、消息队列等三方项目的引入，通过共建，极大的扩展CGraph的适用领域。By the way，在这里提（dao）前（bi）预（jin）告（du）一下，百度大佬主导的基于CGraph的rpc项目，接下来的一年里应该也会和大家见面了，期待。</p><h2 id="%E5%86%99%E5%9C%A8%E6%9C%80%E5%90%8E" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>写在最后</td></tr></table></h2><p>回忆到现在，我的嘴角是带着笑意的。开始的那一刻，也没想到会有接下来的这些内容，只是工作中调库调的多了，单纯想写一个类似的东东，让自己练手，让大家能用起来。</p><center><p><img src="http://www.chunel.cn/upload/2023/01/image-1674885944013.png" alt="image-1674885944013" /></p></center><p>聊聊接下来的规划吧。首先最想要做的，就是打通和python交互的生态。可以是cython，可以是ctypes，可能是rpc，也可以是其他的方式。总觉得这种框架，兼容了python生态，会有一个爆破式的跃迁，各种新的应用场景和领域，也会随之明了。</p><p>分布式的能力，也是一个当前的framework中基本的必备条件。后期我们可能会通过引入三方库的形式支持，期待您提供意见，最好是提供您可以合作的产品。我们继续碰瓷名项目之旅，哈哈。</p><p>还有就是调度性能的优化。在之前的对比中，我们已知，在简单图执行层面跟taskflow基本持平。在新的一年里，我们是希望可以通过一些思路，进行直接的超越。包括但不限于引入协程，或者是优化图分解方法等。</p><p>新的一年，希望可以接触到更多优秀的小伙伴，在工作之余一起做一些相关的努力，也希望可以有更多的落地项目可以被看到。希望大家健康平安，心情愉快吧，哈哈哈。</p><center><p><img src="http://www.chunel.cn/upload/2020/10/mmqrcode1602771241876-546dc2b18b5044d2a2808c5d19ad2394.png" alt="mmqrcode1602771241876" /></p></center><pre><code>                                                          [2023.01.28 by Chunel]</code></pre><hr /><h4 id="%E6%8E%A8%E8%8D%90%E9%98%85%E8%AF%BB" tabindex="-1"><strong>推荐阅读</strong></h4><ul><li><a href="http://www.chunel.cn/archives/cgraph-run-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——执行逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-loop-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——循环逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-param-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——参数传递</a></li><li><a href="http://www.chunel.cn/archives/cgraph-condition-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——条件判断</a></li><li><a href="http://www.chunel.cn/archives/cgraph-aspect-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——面向切面</a></li><li><a href="http://www.chunel.cn/archives/cgraph-function-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——函数注入</a></li><li><a href="http://www.chunel.cn/archives/cgraph-message-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——消息机制</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-1-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（一）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-2-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（二）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-3-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（三）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-4-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（四）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-5-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（五）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-6-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（六）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-distance-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——距离计算</a><br /><br><br></li><li><a href="http://www.chunel.cn/archives/listen-to-coder" target="_blank">CGraph 主打歌——《听码农的话》</a></li><li><a href="http://www.chunel.cn/archives/cgraph-anniversary-introduce" target="_blank">聊聊我写CGraph的这一年</a><br /><br><br></li><li><a href="https://www.bilibili.com/video/BV1gY4y1x7JT?spm_id_from=333.337" target="_blank">【B站视频】CGraph 快速引入的方法介绍</a></li></ul><hr /><h4 id="%E4%B8%AA%E4%BA%BA%E4%BF%A1%E6%81%AF" tabindex="-1">个人信息</h4><p>微信: ChunelFeng<br />邮箱: <a href="mailto:chunel@foxmail.com" target="_blank">chunel@foxmail.com</a><br />个人网站：<a href="http://www.chunel.cn" target="_blank">www.chunel.cn</a><br />github地址: <a href="https://github.com/ChunelFeng" target="_blank">https://github.com/ChunelFeng</a></p><p><img src="http://www.chunel.cn/upload/2021/08/image-a63a5ca7d9104993a51c19d88e25454d.png" alt="image" /></p>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[纯序员给你介绍图化框架的简单实现——消息机制]]></title>
                <link rel="alternate" type="text/html" href="http://www.chunel.cn/archives/cgraph-message-introduce" />
                <id>tag:http://www.chunel.cn,2022-11-05:cgraph-message-introduce</id>
                <published>2022-11-05T22:28:39+08:00</published>
                <updated>2023-07-09T23:35:52+08:00</updated>
                <author>
                    <name>Chunel</name>
                    <uri>http://www.chunel.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p>大家好，我是不会写代码的纯序员——Chunel Feng。大概一年之前，有位自动驾驶行业的朋友在github上看到CGraph这个项目，然后联系到我，问我小破图执行的时候，能否执行完节点A之后，后面的节点B开始执行的同时，A节点继续执行。后来，又有一位百度做视频流处理的朋友，找我问到这个问题。</p><p>我当时，跟他们解释了CGraph中的参数传递机制。虽然参数传递极为高效简洁（复杂度为O1），但很遗憾无法支持这种很常见的诉求。接下来的这一年，我曾尝试多种实现思路去解决这个问题，最终都觉得不够巧妙和优雅。于是，也就没有把代码push上去。</p><center><p><img src="http://www.chunel.cn/upload/2022/11/image-1668185448270.png" alt="image-1668185448270" /></p></center><p>巧了，后来我转行，进入了自动驾驶领域的公司，工作的内容也跟视频流处理有一定关系，进而了解了一些其中的招式和套路。基于此，再结合CGraph原有的设计思路，我们推出了 Message（消息机制）这个功能。Review代码的时候，我发现这是我自己设计的最深、最复杂的模板嵌套逻辑，也是我写过的最复杂的代码。但对外暴露的接口，却极其简单。<b>相比于之前功能，我想说：这是一个绝杀</b>。</p><p>今天，就跟大家聊一下CGraph中的消息机制。顺带对比一下，项目中涉及到的三种 param 的区别和应用场景。</p><blockquote><p>我们还是照例，先上代码连接：<a href="https://github.com/ChunelFeng/CGraph" target="_blank">CGraph github源码</a></p></blockquote><h2 id="%E6%B6%88%E6%81%AF%E6%9C%BA%E5%88%B6" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>消息机制</td></tr></table></h2><p>消息机制，对应于源码中的 GraphMessage 文件夹下的内容。设计的目的有三个：<b>一个是为了丰富数据传递的机制；二是为了提高dag整体（可以是由多个pipeline组成）的并发度；三是避免内部资源不可控的过度消耗</b>。</p><p>我们来一个一个解释一下：</p><p><b>1. 丰富数据传输机制</b><br />在之前的文章中，我们提到过CGraph的 <a href="http://www.chunel.cn/archives/cgraph-param-introduce" target="_blank">参数传递</a> 机制。通过在任意位置，写入GParam内容，就可以在另外的任意位置（包括 node、group、aspect、daemon）中获取，并且提供了并发控制机制。看似很无敌，但是也带来了一个问题。这些都是建立在，上述内容被注册到同一个pipeline中的。如果两个节点，被注册到不同的pipeline中，那该如何进行参数互通呢？</p><center><p><img src="http://www.chunel.cn/upload/2022/11/image-1668187025198.png" alt="image-1668187025198" /></p></center><p>为此，我们先实现了一个 <a href="https://github.com/ChunelFeng/CGraph/blob/main/src/UtilsCtrl/ThreadPool/Queue/UAtomicRingBufferQueue.h" target="_blank">线程安全的环形队列</a>，又设计了一个 GMessage 类，其中包含一个环形队列，并且限定 其中仅能传入 GMessageParam 的子类对象。</p><p>然后，又设计了一层 GMessageManager 结构。在其中，包含了一个 map&lt;topic, GMessage&lt; T &gt;&gt;结构。外部只要设定 topic名称和 GMessageParam 子类的类型，就可以创建一个相关的 GMessage了。进而，你就可以不断的往里面去写具体的内容了。</p><p>同时，另一个地方，就可以在从环形队列的头部，不断的去获取最新写入的参数了。从而实现了一个简单的不同类型参数的 send/recv 的逻辑。</p><p>我们来看一下几个相关的接口哈：</p><pre><code class="language-cpp">CGRAPH_CREATE_MESSAGE_TOPIC(MyMessageParam, &quot;test&quot;, 10);    // 创建消息CGRAPH_SEND_MPARAM(MyMessageParam, &quot;test&quot;, mp);    // 写入消息CGRAPH_RECV_MPARAM(MyMessageParam, &quot;test&quot;, mp);    // 获取消息CGRAPH_REMOVE_MESSAGE_TOPIC(&quot;test&quot;)     // 删除一条消息CGRAPH_CLEAR_MESSAGES();    // 清空所有消息</code></pre><p>见名知意，相当简洁是不是。同时，我们还提供了 pub 和 sub 相关接接口，供 一发多收的情况下使用。</p><p><b>2. 提高dag整体的并发度</b><br />参数值pipeline内全局共享，get/set是快了。但也注定了pipeline执行完一圈之后，才能从头继续执行。否则，数据值就对不上了。</p><p>而上面提到的message的这个结构，其实可以很好的解耦这个问题。将原先的pipeline，分成两个部分，pipeline1 处理好一部分数据，直接将结果发送到message中，然后就继续处理自己的下一轮询了。</p><p>而pipeline2 在message中有 MessageParam 的时候，则recv出来数据，然后继续做自己的流程。这样，就算 pipeline2中处理稍慢，导致略有积压，也能一定程度上，减少整体时间的损耗了。</p><center><p><img src="http://www.chunel.cn/upload/2022/11/image-1668185573992.png" alt="image-1668185573992" /></p></center><p>而这，唯一要付出的代价，就是 MessageParam 中，有一次参数的copy。</p><blockquote><p>说到内存拷贝，插入一条广告。近期工作中，遇到的内存管理相关内容比较多，自己对这一块的理解也比较捉鸡。有熟悉这部分内容的大佬，请联系我一下，以便今后随时请教和交流。</p></blockquote><p><b>3. 避免资源不可控的过度消耗</b><br />接着说上面的那种情景。如果，pipeline1处理很快，而pipeline2处理的很慢。那会导致一个情况，messsage的队列中，被疯狂写入，而来不及消费，最终甚至导致OOM问题。或者，pipeline1中的任务一直在长时间占用cpu，导致pipeline2中的内容，少有被执行的机会。</p><p>问了解决这个问题，在创建 message的时候，我们设定了一个 size 的参数，表明 环形队列的尺寸。如果其中的数据达到这个值，那send流程将会被阻塞，避免过度写入。</p><h2 id="%E4%B8%89%E7%A7%8D%E5%8F%82%E6%95%B0%E5%AF%B9%E6%AF%94" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>三种参数对比</td></tr></table></h2><p>CGraph-v2.2.0版本中，一共有三种参数类型，分别是 <code>GParam</code>，<code>GPassedParam</code>和<code>GMessageParam</code>。我们再来一次对比介绍一下:</p><p><img src="http://www.chunel.cn/upload/2022/11/image-1668182947751.png" alt="image-1668182947751" /></p><ul><li>GParam</li></ul><p>GParam 主要用于同一个pipeline中，各种值的传递。可以在 同一个pipeline的任何地方获取，比如 node、group、aspect、daemon 等。这里的获取的时间复杂度是O(1)的，而且是通过 指针传递，效率很高。也提供了控制互斥的语法，不过需要自己调用。</p><ul><li>GPassedParam</li></ul><p>GPassedParam 有好几个重命名，其实目的只有一个，就是点对点的传递参数，解决的是每个类的细节区别问题。</p><pre><code class="language-cpp">using GAspectParam = GPassedParam;using GDaemonParam = GPassedParam;using GElementParam = GPassedParam;</code></pre><p>比如，注册n个节点，功能就是 print 信息，但是又想每次输出的内容均不同。那，这个时候，输出的具体信息，就适合用 GElementParam（也就是 GPassedParam）传入，而不是写三个不同的 print节点。</p><ul><li>GMessageParam</li></ul><p>最后，再说一下配合消息机制而推出的 GMessageParam。这个就是主要用于在不同 pipeline之间传递数据，和做一些负载控制。</p><p><img src="http://www.chunel.cn/upload/2022/11/image-1667745098110.png" alt="image-1667745098110" /></p><p>希望大家在使用的时候，合理做出选择和分配。从以上总结也可以看出，目前CGraph已经支持了各种形式的进程内通信，但是并不支持跨进程、跨机器/机房 通信。如果需要的话，大家可以尝试找一些三方库来完成对应的功能。</p><p>我们接下来也会考虑，是否会推出基线版本层面的支持。需要权衡的因素，主要是一些系统基础库的不同，和暂时不太想引入三方库的缘故。</p><h2 id="%E5%85%A5%E9%80%89-awesome-cpp%E6%A6%9C%E5%8D%95" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>入选 awesome cpp榜单</td></tr></table></h2><p>对于入选 <a href="https://github.com/fffaraz/awesome-cpp" target="_blank">awesome cpp 榜单</a>，我自己也是比较意外的。有一天晚上，也不记得当时咋想的了，就给项目组提了pr，当时真心是不抱着任何希望的哈。</p><p><img src="http://www.chunel.cn/upload/2022/11/image-1667750320156.png" alt="image-1667750320156" /></p><p>一方面，我们自己开发的东东，跟行业大佬和众多大厂名项目，在质量和完整度上有不小的差距。另一方面，<b>也从来没有出现过readme仅用纯中文写的repo</b>，被收录之中——毕竟是一个世界范围的推荐榜单。结果，第二天早上起来的时候，发现pr已经被负责人 approval 了。当时还是激动了好几秒的，不过很快冷静下来。因为马上也还要去工地搬砖。</p><p><img src="http://www.chunel.cn/upload/2022/11/image-1667750252259.png" alt="image-1667750252259" /></p><p>我想：<b>作为一个C++开发者，你不一定调用过 <code>awesome cpp</code> 项目。但是你调用的项目，一定调用过 <code>awesome cpp</code> 里的项目</b>。我之前一直是看这个榜单的，一些常用的基础组件，比如 json库、log库也会从这里推荐的选。真的没想到，有一天自己写的东西，也有幸能够入选。真是自己职业生涯中，浓墨重彩的一笔吧。当然，如果因此能帮助到更多有需要的朋友，我会更加高兴的。</p><h2 id="%E6%9C%AC%E7%AB%A0%E5%B0%8F%E7%BB%93" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>本章小结</td></tr></table></h2><p>我是从去年五一节吧，开始写CGraph这个工程。第一个版本就已经实现了依赖和解依赖的图执行功能，从开始设计到跑通功能，再到完成自测，一共也只花了三天。之后就一直在做各种功能迭代和优化了。其中，有很多过程和功能，并非在开始的时候，我能够预见到的。到现在，无法说小有所成，但的确收获颇丰。</p><p>有一个很重大的收获，就是小破图被收录在了 <code>awesome-cpp</code> 和 <code>hello-github</code> 等业内最知名的推荐榜单上，获得了更高的曝光机会，也算是得到了业内大佬的一些肯定。更重要的，还是我因为写开源代码，有幸和正在看这篇文章的你，建立起了原本不会有的连接，进而发生了影响我职业生涯和生活的种种。</p><center><p><img src="http://www.chunel.cn/upload/2022/11/image-1668176589453.png" alt="image-1668176589453" /></p></center><p>这一路，得益于各位大佬的指点、童鞋的意见，项目自身也在快速的更新和优化。同时有了几位贡献者，并且开始支持个别研究课题。我自己，也在正式工作之余有所成长，同工作形成了一定的互补。</p><p>回顾我的成长，有两件事情对我性格的形成，有很大的影响。第一件事情，就是我小时候学弹琴——那是很多年前的事情了，几乎伴随了我整个童年。还有就是，近几年开始在开源社区写代码。我是大概5年前，就有了类似的想法，会零零散散写点东西发布到 github 上。然后最近2年吧，随着看的东西稍多一些，思考也会多了一些，开始比较频繁的更新。很幸运，能被人看到，有人star和fork，也有幸支持了一些团队。两件事情，都坚持了多年，填充了我大量的业余时间。虽然内容截然不同，但想想<b>都是一个人坐着敲键盘，有种修身养性的感觉</b>。</p><center><p><img src="http://www.chunel.cn/upload/2022/11/image-1668218660672.png" alt="image-1668218660672" /></p></center><p>前面的文章也提到，前阵子换了工作，进入了自己从来没有接触过的自动驾驶领域。换行业的原因很单纯，一方面是这个行业近期火起来，还有一方面是工作内容偏技术导向。初来乍到，有很多内容不了解，需要同事事无巨细的指导。每每走到一些无人区的时候，都能在一些技术交流群里，遇到很懂行的大佬，交流指导后得以解决。现在看来，当年进入开源社区，也是一件幸事，也是一种沉淀。</p><p>今天的内容就聊到这里吧，双十一晚上下班回来，就顾着写文章了。不知不觉已经是第二天了，啥也没买，有点遗憾了。不过，又想到反正我也没啥钱，也就不遗憾了，哈哈。</p><center><p><img src="http://www.chunel.cn/upload/2020/10/mmqrcode1602771241876-546dc2b18b5044d2a2808c5d19ad2394.png" alt="mmqrcode1602771241876" /></p></center><pre><code>                                                   [2022.11.12 by Chunel]</code></pre><hr /><h4 id="%E6%8E%A8%E8%8D%90%E9%98%85%E8%AF%BB" tabindex="-1"><strong>推荐阅读</strong></h4><ul><li><a href="http://www.chunel.cn/archives/cgraph-run-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——执行逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-loop-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——循环逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-param-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——参数传递</a></li><li><a href="http://www.chunel.cn/archives/cgraph-condition-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——条件判断</a></li><li><a href="http://www.chunel.cn/archives/cgraph-aspect-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——面向切面</a></li><li><a href="http://www.chunel.cn/archives/cgraph-function-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——函数注入</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-1-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（一）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-2-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（二）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-3-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（三）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-4-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（四）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-5-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（五）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-6-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（六）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-distance-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——距离计算</a></li></ul><hr /><h4 id="%E4%B8%AA%E4%BA%BA%E4%BF%A1%E6%81%AF" tabindex="-1">个人信息</h4><p>微信: ChunelFeng<br />邮箱: <a href="mailto:chunel@foxmail.com" target="_blank">chunel@foxmail.com</a><br />个人网站：<a href="http://www.chunel.cn" target="_blank">www.chunel.cn</a><br />github地址: <a href="https://github.com/ChunelFeng" target="_blank">https://github.com/ChunelFeng</a></p><p><img src="http://www.chunel.cn/upload/2021/08/image-a63a5ca7d9104993a51c19d88e25454d.png" alt="image" /></p>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[纯序员给你介绍图化框架的简单实现——线程池优化（六）]]></title>
                <link rel="alternate" type="text/html" href="http://www.chunel.cn/archives/cgraph-threadpool-6-introduce" />
                <id>tag:http://www.chunel.cn,2022-09-11:cgraph-threadpool-6-introduce</id>
                <published>2022-09-11T17:44:31+08:00</published>
                <updated>2022-10-09T12:52:34+08:00</updated>
                <author>
                    <name>Chunel</name>
                    <uri>http://www.chunel.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p>大家好，我是不会写代码的纯序员——Chunel Feng，好久没写文章了。前阵子，自己工作有一些异动，也换了方向。一个月适应下来，整体感觉还好。这个，在最后会跟大家聊两句。</p><p>这篇文章，主要跟大家聊一聊近期对线程池功能的优化。在之前的文章中，我们已经从设计、优化和结果几个方面，聊了对UThreadPool类 相关的内容。今天的内容，算是在原有的基础上，做一些简单的追加。</p><p>首先，还是先上源码：<a href="https://github.com/ChunelFeng/CGraph" target="_blank">CGraph<br />github链接 源码</a></p><center><p><img src="http://www.chunel.cn/upload/2022/10/image-1664896422623.png" alt="image-1664896422623" /></p></center><h2 id="%E8%B6%85%E6%97%B6%E6%9C%BA%E5%88%B6" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>超时机制</td></tr></table></h2><p>首先，我们来聊聊线程执行超时的问题。</p><p>我们在实际工作中，遇到跑线程任务的时候，总有些不可预知的情况（比如，数据库慢查询），导致个别任务很慢，而上游一直在等待返回结果。甚至有可能，多个慢的任务，导致上游功能层的流程完全阻塞了。</p><p>为了避免这种情况，我们需要对单个线程的执行时间，做一个时间限定，比如：当前任务不能超过 3000ms，如果超过，要结束阻塞，并且返回错误信息。别小看这个功能，在有些情况下，这是可以帮上大忙的。</p><center><p><img src="http://www.chunel.cn/upload/2022/10/image-1664896686368.png" alt="image-1664896686368" /></p></center><p>用起来也很简单，看下面一段代码。在调用的时候，需要在后面加入 超时时间信息，单位是ms。</p><pre><code class="language-cpp">auto tp = new UThreadPool();CStatus status = tp-&gt;submit([] { sleep(2500); }, 2000);status += tp-&gt;submit([] { sleep(2500); }, 3000);    // 会超时，结果提现在status的结果中</code></pre><p>其实，思路也挺简单的，用的是 std::future&lt;&gt; <code>wait_for（单个）</code> 和 <code>wait_until（多个）</code>的方法进行等待。如果超过约定时长，则停止等待，并且返回错误信息。</p><p>需要强调的一点是，上游的线程执行函数返回，并且继续进行接下来的操作了。但是，线程函数仍然会继续执行，并且占用相关资源，直到该线程函数执行完毕，或者本进程结束。</p><p>也就是说，切记不要再 while(true) 的线程中，设定等待时长，然后就不管了。这样的话，程序虽然还是正常运行，但是线程资源是释放不了的。</p><h2 id="%E4%BB%BB%E5%8A%A1%E7%BB%84" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>任务组</td></tr></table></h2><p>接着，我们来聊聊任务组（UTaskGroup）的事情。我们知道，线程池一般都用来处理批量任务。在前面的内容中，我们也都是通过for循环的方式，将一堆任务放到线程池中执行。考虑下面几个问题：</p><ul><li>我想等这一批任务执行结束，再执行其他的任务，怎么办？</li><li>我想给这一批任务，设定一个统一的等待时长，怎么办？</li><li>我想在多批任务执行结束的时候，固定执行某个回调逻辑，怎么办？</li></ul><p>给大家推荐任务组。任务组的设计，主要就是为了方便对多任务的管理。使得多个任务，表现出一些统一的特性（比如，这一批任务最多执行 5秒），也方便后期的复用和移植。一组任务，最重要的就是整整齐齐。</p><center><p><img src="http://www.chunel.cn/upload/2022/10/image.png" alt="image" /></p></center><pre><code class="language-cpp">UTaskGroup taskGroup;auto tp = new UThreadPool();for (int i = 0; i &lt; 100; i++) {    taskGroup.addTask([i] { std::cout &lt;&lt; i &lt;&lt; &quot; &quot;; });    // 将任务放到一个taskGroup中，并发执行。执行的结果是无序的}tp-&gt;submit(taskGroup, 2000);    // 最多执行 2000ms，就结束</code></pre><p>任务组还可以设定结束时候的回调，多个UTaskGroup复用一个结束逻辑（如：记录超时次数）。关于使用方法，大家看一个源码中的例子，肯定也就明白了，不多说了。</p><h2 id="%E4%BC%98%E5%85%88%E7%BA%A7%E5%92%8C%E9%98%BB%E5%A1%9E%E6%89%A7%E8%A1%8C" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>优先级和阻塞执行</td></tr></table></h2><p>最后，我们来聊聊任务优先级的问题。在实际开发中，我们经常会遇到这样的问题，多任务之间，基本是没有什么依赖和关联关系的，但是多少会有优先级会有些区别。比如，m个键盘输入任务和n个打印任务，明显前者应该更优先于后者。</p><p>为此，我们专门设定了根据优先级执行的逻辑。上游可以将任务绑定优先级，并且写入线程池中。线程池会在内部进行统一排序，并且最终执行。</p><pre><code class="language-cpp">void priority(UThreadPoolPtr tp) {    tp-&gt;commitWithPriority([] { print_func(); }, -3);    // 优先级设定为-3    tp-&gt;commitWithPriority([] { keyboard_func_1(); }, 10);    // 优先级设定为10    tp-&gt;commitWithPriority([] { keyboard_func_2(); }, 10);    tp-&gt;commitWithPriority([] { keyboard_func_3(); }, 10);    tp-&gt;commitWithPriority([] { commen_func(); }, 0);}</code></pre><p>需要说明几点：</p><p><b>首先，这种优先级任务的执行，是不依赖主线程中的消息队列，而仅依赖辅助线程执行的</b>。如果“仅需要”执行这种优先级任务，建议在配置中将主线程个数设置为0。</p><pre><code class="language-cpp">static const int CGRAPH_DEFAULT_THREAD_SIZE = 0;static const int CGRAPH_MAX_THREAD_SIZE = 2;</code></pre><p><b>还有就是，这种机制有一个好处，可以优化线程资源配置</b>。假设这种场景：有很多长时间执行的任务，一股脑的塞入线程池中，将所有的主线程和辅助线程全部占满了。那继续push进来的任务，就都无法正常执行了。久而久之，甚至可能导致oom和进程崩溃。</p><p>其实，有个假设，就是一般情况下，需要较长时间运行的线程，都不是很care是否能够立即开始：比如，你敲下键盘（短时间任务），就希望屏幕上立刻给出反馈；而你想打印一份很长的文档（长时间任务），点下按钮后，打印机过个5秒钟再开始工作，你就不会有感觉。</p><p>针对这种情况，我们将部分长时间执行的任务设定一个较低的优先级，放到pool内部的优先队列中，仅让辅助线程来执行它们。这样，既能保证这些任务在合适的时间被执行，又能保证不会占用主线程资源，更耗尽所有线程资源。</p><h2 id="%E6%9C%AC%E7%AB%A0%E5%B0%8F%E7%BB%93" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>本章小结</td></tr></table></h2><p>这一章的内容，主要是针对最近一些朋友提出来的意见和建议，做的一些改进。说的都是一些很小很实用的点，也伴随着大量的改动点、优化点和测试点。</p><p>在这里，我们感谢来自 <a href="http://www.qtcn.org/bbs/i.php" target="_blank">qtcn社区</a> 的大佬，给我们提出的意见和解决方案。如果有遇到在Windows上将线程池封装成dll后，无法正常运行的问题，请参考：<a href="https://github.com/ChunelFeng/CGraph/issues/17" target="_blank">线程池封装问题和解决方法</a>，也欢迎大家加入qtcn社区。我刚开始工作的时候，是有机会接触一下qt的。当时没学好，现在整个就很遗憾。</p><p>还要感谢杭州凤起路旁的金主爸爸，给我们一面之猿网提供的了新的服务器。我开一面之猿网两年多，赚的钱还不够买服务器的，悲剧啊。小破站持续接收广告中，包括且不限于 <em>茶叶、西柚、奶茶</em>，我们底线很低的。希望大家多投广告，也增加打赏的力度，让我有点资本，再去狗币圈里疯狂收割一波。</p><center><p><img src="http://www.chunel.cn/upload/2022/05/image-2b511d90ad4744fd9e621e091904364e.png" alt="image" /></p></center><p>最后，如果你看到这里，说明也是对这一块内容比较感兴趣的朋友了。所在团队近期（2022.10.04）持续招人，组内技术氛围好，工程力强，在我混进来之前，人均大佬。如果你对 索引、存储、调度、异构计算 等一个或多个技术领域有自己的理解和研究，并且熟悉数据结构和操作系统的话，希望可以跟您取得联系，期待与您一起共事的机会。</p><center><p><img src="http://www.chunel.cn/upload/2020/10/mmqrcode1602771241876-546dc2b18b5044d2a2808c5d19ad2394.png" alt="mmqrcode1602771241876" /></p></center><pre><code>                                                   [2022.10.04 by Chunel]</code></pre><hr /><h4 id="%E6%8E%A8%E8%8D%90%E9%98%85%E8%AF%BB" tabindex="-1"><strong>推荐阅读</strong></h4><ul><li><a href="http://www.chunel.cn/archives/cgraph-run-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——执行逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-loop-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——循环逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-param-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——参数传递</a></li><li><a href="http://www.chunel.cn/archives/cgraph-condition-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——条件判断</a></li><li><a href="http://www.chunel.cn/archives/cgraph-aspect-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——面向切面</a></li><li><a href="http://www.chunel.cn/archives/cgraph-function-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——函数注入</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-1-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（一）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-2-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（二）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-3-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（三）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-4-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（四）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-5-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（五）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-distance-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——距离计算</a></li></ul><hr /><h4 id="%E4%B8%AA%E4%BA%BA%E4%BF%A1%E6%81%AF" tabindex="-1">个人信息</h4><p>微信: ChunelFeng<br />邮箱: <a href="mailto:chunel@foxmail.com" target="_blank">chunel@foxmail.com</a><br />个人网站：<a href="http://www.chunel.cn" target="_blank">www.chunel.cn</a><br />github地址: <a href="https://github.com/ChunelFeng" target="_blank">https://github.com/ChunelFeng</a></p><p><img src="http://www.chunel.cn/upload/2021/08/image-a63a5ca7d9104993a51c19d88e25454d.png" alt="image" /></p>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[从月入3k5的实习生，到年薪百万的程序员。毕业这6年，我每天都坚持做这一件事情...]]></title>
                <link rel="alternate" type="text/html" href="http://www.chunel.cn/archives/coder-dream" />
                <id>tag:http://www.chunel.cn,2022-08-07:coder-dream</id>
                <published>2022-08-07T21:59:51+08:00</published>
                <updated>2023-03-05T23:11:43+08:00</updated>
                <author>
                    <name>Chunel</name>
                    <uri>http://www.chunel.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p>做梦！！！</p><p><img src="http://www.chunel.cn/upload/2022/08/image.png" alt="image" /></p><p>快别tm做梦了，明天早上还要搬砖呢！！！</p><hr /><h4 id="%E6%8E%A8%E8%8D%90%E9%98%85%E8%AF%BB" tabindex="-1"><strong>推荐阅读</strong></h4><ul><li><a href="http://www.chunel.cn/archives/cgraph-run-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——执行逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-loop-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——循环逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-param-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——参数传递</a></li><li><a href="http://www.chunel.cn/archives/cgraph-condition-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——条件判断</a></li><li><a href="http://www.chunel.cn/archives/cgraph-aspect-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——面向切面</a></li><li><a href="http://www.chunel.cn/archives/cgraph-function-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——函数注入</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-1-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（一）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-2-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（二）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-3-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（三）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-4-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（四）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-5-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——线程池优化（五）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-distance-introduce" target="_blank">纯序员给你介绍图化框架的简单实现——距离计算</a><br /><br><br></li><li><a href="http://www.chunel.cn/archives/listen-to-coder" target="_blank">CGraph 主打歌——《听码农的话》</a></li></ul><hr /><h4 id="%E4%B8%AA%E4%BA%BA%E4%BF%A1%E6%81%AF" tabindex="-1">个人信息</h4><p>微信: ChunelFeng<br />邮箱: <a href="mailto:chunel@foxmail.com" target="_blank">chunel@foxmail.com</a><br />个人网站：<a href="http://www.chunel.cn" target="_blank">www.chunel.cn</a><br />github地址: <a href="https://github.com/ChunelFeng" target="_blank">https://github.com/ChunelFeng</a></p><p><img src="http://www.chunel.cn/upload/2021/08/image-a63a5ca7d9104993a51c19d88e25454d.png" alt="image" /></p>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[向量检索算法（ANNS）优化思路之我见]]></title>
                <link rel="alternate" type="text/html" href="http://www.chunel.cn/archives/anns-optimize-analyse-1" />
                <id>tag:http://www.chunel.cn,2022-06-11:anns-optimize-analyse-1</id>
                <published>2022-06-11T12:09:31+08:00</published>
                <updated>2022-06-17T20:26:01+08:00</updated>
                <author>
                    <name>Chunel</name>
                    <uri>http://www.chunel.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p>大家好，我是不会写代码却忽然想转行写算法的纯序员——Chunel Feng。最近，互联网人的日子着实不好过。大家每天都喊着节能减排，<strong>总有种【程序和程序员之间，必须有一个被优化】的感觉</strong>。</p><p>为了避免今后无砖可搬的困境，今天我想站在一个业余爱好者的角度，跟大家来聊一些关于ANNS（Approximate Nearest Neighbor Search）算法，也就是通常意义上说的向量检索算法的一些优化思路。</p><p>首先申明，我并非专业领域的人员，<code>我指的不是 我并非ANNS领域的专业人员，我指的是，我不是什么专业的程序员</code>。在这里，描述的更多的是我参考一些文献，和请教行业大佬而获取的知识，并且加以思考（mark）和转述（ctrl+cv）。内容和思路相对比较通用，以ANNS算法作为切入点，其他领域的童鞋也可参考相关思路。</p><p>阅读这篇文章的前置条件，是您对向量检索概念和hnsw算法过程有一些了解。推荐一篇博文入门：<a href="https://blog.csdn.net/u011233351/article/details/85116719" target="_blank">一文看懂HNSW算法理论的来龙去脉</a> 。如果您不了解相关背景，也不愿看入门博客，那也推荐您继续看下去，<strong>因为文中，还有一些段子</strong>。</p><h2 id="%E8%83%8C%E6%99%AF" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>背景</td></tr></table></h2><p>不知是否众所周知，现在ANNS算法通常分为四个派别：图（多图）思路，量化/降维思路，哈希思路，树（森林）思路。近些年，图思路由于较高的召回率、较好的性能和较多的后期优化思路，脱颖而出，hnsw就算是其中的代表作。</p><p><img src="http://www.chunel.cn/upload/2022/06/image-1654927800892.png" alt="image-1654927800892" /></p><p>hnsw有两个优势：一个是通过平层图+跳表的思路，加速了开始阶段远距离导航的过程；一个是通过启发式选边的思路，降低了候选集局部密集的干扰。现有的很多算法研究思路，都是基于这个方案，做进一步针对性的优化。</p><p>接下来，我就针对我了解到的一些优化方案，一一讨论和分享。主要包括：<strong>分而治之，主次分明，前世今生，合纵连横</strong>。</p><h2 id="%E5%88%86%E8%80%8C%E6%B2%BB%E4%B9%8B" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>分而治之</td></tr></table></h2><p>当前的图模型算法有很多，比如 nsg，vamana，hnsw等。其中，选入口点、导航、裁边等过程，思路各有不同。但是，如果加以总结、分析和抽象，会发现基本所有的图算法，都有以下思路：</p><p><img src="http://www.chunel.cn/upload/2022/06/image.png" alt="image" /></p><blockquote><p>构图的时候</p></blockquote><ul><li>初始化连通图（初始化）</li><li>为连通图选择邻居（连边）</li><li>对邻居进行优选（选边+裁边）</li><li>选定一个图（或 多层图）的入口点</li><li>确保图的联通性（避免出现孤岛）</li></ul><blockquote><p>查询的时候</p></blockquote><ul><li>从入口点进入图</li><li>图中导航，搜索到topK个最近邻居</li></ul><p>以上是现有的大佬的总结方法。你也可以有自己的分法，或者自行扩展。但更需要应对的问题是，分好 <code>元素（component）</code>了之后，怎么办。</p><p>首先，可以将各种算法不同component阶段的优质思路 拼凑起来，形成一套完成的 构图+查询 的流程。那我们可以认为，这个组合算法（缝合怪）其实就集合了当前所有图算法优势。</p><p>然后，我们在针对特定的数据集，将这个缝合怪的特定component，进行针对性的优化。那就可以认为，得到了一套适配当前数据集、并且集合了众多图算法优点 的算法。</p><p>同样的思路，也被用于图像算法性能调优上，并且快速取得了较好的成果：<a href="https://oldpan.me/archives/learn-a-little-halide" target="_blank">图像、神经网络优化利器:了解Halide</a> ，有兴趣的朋友可以参考一下。</p><h2 id="%E4%B8%BB%E6%AC%A1%E5%88%86%E6%98%8E" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>主次分明</td></tr></table></h2><p><strong>在做性能优化的时候，我们理应考虑轻重缓急，抓大放小</strong>。打个比方，你从杭州想坐火车去上海找童鞋玩，那肯定是先坐火车，从杭州火车站到上海火车站；然后再做公交车，到你朋友家的小区门口；最后从小区门口挨家挨户的走到他家门口。如果你朋友在上海买不起房子的话，约在他公司附近的星巴克，也是同样的道理，嘿嘿。</p><p>在这个过程中，坐火车的那一段，相当于是粗导航（Navigable），只要确保大体方向对，几站路就能到上海火车站。</p><p>坐公车那一段，相当于是细粒度查询。这里就要开始小心了，如果做错公车，或者是坐过了车站，那会导致你下车后要走很长的路，甚至最后没法找到朋友家。</p><p>到了小区门口之后，挨家挨户找的那个过程，其实就像是在少量候选集中，进行暴力搜索。一定要找到对的门牌号为止。</p><pre><code class="language-">最后，带着笑脸，低头寒暄，说一句，好久不见。又或者，装作若无其事的样子，说一句，好巧，原来你也在这里。找到之后做点什么，那就不是我们研究的流程了，在这里不加以赘述。</code></pre><p>同样的，ANNS算法在查询过程中，刚开始查询得到的候选点，和快结束时查询得到的候选点，虽然邻居数量大体相同，但对最终的召回率（或准确率）的影响，是不同的。</p><p>在刚开始查询的阶段，只要保证大致是沿着正确的方向导航即可——这样可以，减少比对（计算）次数，从而缩短查询耗时。在候选点和查询点非常接近的时候，再使用近乎暴力的方式查询——这样可以提高准确率。</p><p><img src="http://www.chunel.cn/upload/2022/06/image-1654930477990.png" alt="image-1654930477990" /></p><p>图看不懂就算了，不要在意这些细节，我也是直接从论文里截图过来的。大概意思就是我举的那个例子：红色五角星表示你朋友家（query点），绿色点表示杭州火车站（入口点）。阴影部分可以想成是坐火车+公车的过程，红圈里面可以想成是挨家挨户的找的过程。</p><h2 id="%E5%89%8D%E4%B8%96%E4%BB%8A%E7%94%9F" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>前世今生</td></tr></table></h2><p>以上内容，我们关注的是算法构图和查询的流程，都focus在ANNS算法本身。这个时候，如果我们把格局打开，考虑一下算法建模之前呢？考虑一下，模型训完之后呢？</p><blockquote><p>前世</p></blockquote><p>ANNS算法构图的dataset，是海量的高维向量。而dataset是通过其他embedding算法计算出来的，分布会随着原材料的不同，呈现一定的随机性和区域性的。这将引发一些不确定性和局部热点问题。</p><p>设想，如果我们在构图之前，对dataset做一个预处理，或者进行一些定向分析，那是不是也可能提升最终构图的质量。</p><p>预处理也有一些思路，比如pca、pq等方式，可以降低向量的维度，从而降低计算工作量。或者，对dataset进行空间转换，使得数据分布尽可能均匀，降低理论平均查询耗时。再或者，做二次embedding，尽可能的缩短真的相近向量的距离。</p><blockquote><p>今生</p></blockquote><p>模型创建完之后，当然就可以直接用于query点的快速查询了。不过，这就万事大吉了么？<strong>那除了程序员之外，还能有啥可以优化的空间么？</strong></p><p><img src="http://www.chunel.cn/upload/2022/06/image-1654940968835.png" alt="image-1654940968835" /></p><p>当然可以。比如我们可以尝试结合一批真实的query请求，分析一下，query点的分布，从而调整图模型的入口点，使得模型本身和实际使用情况更加match。</p><p>再或者，我们可以根据这些query的导航路径，来查看<strong>哪些边在实际查询中，基本没用到；哪些边更经常被访问；哪些变对最终结果影响更大</strong>，从而对现有的图模型做进一步的加权和裁边操作——让模型和程序员一起，变秃变强。</p><h2 id="%E5%90%88%E7%BA%B5%E8%BF%9E%E6%A8%AA" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>合纵连横</td></tr></table></h2><p>我们常说，一个人的成长，要考虑自身努力，也要结合时代背景。针对ANNS算法模型的优化，也是如此。<strong>我们不仅要把焦点放在图算法本身，还需要考虑周边因素的一些影响</strong>。</p><blockquote><p>合纵</p></blockquote><p>我们说了很多图算法相关的内容和优化，也在最开始的时候，提到了ANNS算法，大体有四种思路。很多时候，这些思路是可以进行进一步的组合，并且发挥出更大威力的。</p><p>比如，选择入口点的时候，就可以考虑通过KD-Tree，快速找到局部入口点。或者通过哈希的方法，更好的做子图的拆分。我上面提到的，将图算法进行component拆分，很可能其中的某个component，就可以照搬其他类型算法的思路。</p><blockquote><p>连横</p></blockquote><p>以上内容，我们貌似都是在一个特定设备上，针对模型和数据集进行优化。那我们再讨论一些与<code>设备和运行环境</code>相关的问题。比如，<strong>数据量太大，内存无法读取，单次查询过程需要从磁盘上反复io数据；再或者，冷数据相对较多，缓存命中率低；又或者，有单批大量计算，cpu已经冒烟</strong>。</p><p><img src="http://www.chunel.cn/upload/2022/06/image-1654941115966.png" alt="image-1654941115966" /></p><p>针对这些问题，我们在设计模型的过程中，可能更需要考虑<strong>降低io开销（参考B树 vs B+树）、缓存友好、计算相对集中的结构和构图方式</strong>。从而针对特定的硬件设备、更专业的领域、和更极端的情况下，发挥出最更的威力。</p><h2 id="%E4%B8%AA%E4%BA%BA%E6%80%BB%E7%BB%93" tabindex="-1"><table><tr><td bgcolor=#D1EEEE>个人总结</td></tr></table></h2><p>本篇文章，主要介绍了一些<code>ANNS Graph Based</code>算法的优化思路，旨在以现有hnsw算法为标准，进行进一步的剖析和优化，不断提升自己的认知。有一些内容，是通用思路级别的，相信对其他领域算法的研究，和其他方向的优化，也有一定的参考和借鉴意义。</p><p>很多内容，都是业内专业人士给我的指导和分享，并且被证明的确行之有效。我自己并没有做太多的实验和论证，但也参考了一些相关的博客和文档，并加入了理解和表述。希望本文对大家今后进一步的工作和研究，有参考和借鉴意义。</p><center><p><img src="http://www.chunel.cn/upload/2021/08/image-aa8df7f06386405288514b7ae6df850b.png" alt="image" /></p></center><p>如果您对以上话题有兴趣，也欢迎添加我的个人微信，方便今后随时交流讨论。对了，下面还有打赏码，也期待您的打赏。讲真话，<strong>如果不是前阵子狗币跌的太多，我真的不会很在乎这一毛两毛的打赏</strong>，哈哈。</p><hr /><h4 id="%E4%B8%AA%E4%BA%BA%E4%BF%A1%E6%81%AF" tabindex="-1">个人信息</h4><p>微信: ChunelFeng<br />邮箱: <a href="mailto:chunel@foxmail.com" target="_blank">chunel@foxmail.com</a><br />个人网站：<a href="http://www.chunel.cn" target="_blank">www.chunel.cn</a><br />github地址: <a href="https://github.com/ChunelFeng" target="_blank">https://github.com/ChunelFeng</a></p><p><img src="http://www.chunel.cn/upload/2021/08/image-a63a5ca7d9104993a51c19d88e25454d.png" alt="image" /></p><hr /><blockquote><p>参考论文：</p></blockquote><ul><li>A Comprehensive Survey and Experimental Comparison of Graph-Based Approximate Nearest Neighbor Search</li><li>Two-stage routing with optimized guided search and greedy algorithm on proximity graph</li><li>Efficient and robust approximate nearest neighbor search using Hierarchical Navigable Small World graphs</li><li>DiskANN: Fast Accurate Billion-point Nearest Neighbor Search on a Single Node</li></ul><blockquote><p>参考博客：</p></blockquote><ul><li><a href="https://www.zhihu.com/column/ai-search" target="_blank">Zilliz(知乎)——Milvus 开源向量数据库</a></li><li><a href="https://blog.csdn.net/whenever5225/category_9734174.html?spm=1001.2014.3001.5482" target="_blank">程序员王同学(CSDN) —— 近似最近邻搜索</a></li><li><a href="https://zhuanlan.zhihu.com/p/415320221" target="_blank">13 种高维向量检索算法全解析！数据库顶会 VLDB 2021 论文作者干货分享</a></li></ul><blockquote><p>参考视频资料：</p></blockquote><ul><li><a href="https://space.bilibili.com/478166626/channel/seriesdetail?sid=865744" target="_blank">Zilliz-Planet(B站) —— Paper reading 学术直播间</a></li></ul><blockquote><p>对应代码实现（持续进行中）：</p></blockquote><ul><li><a href="https://github.com/whenever5225/GraphANNS" target="_blank">GraphANNS(github)</a></li><li><a href="https://github.com/ChunelFeng/CGraph" target="_blank">CGraph(github)</a></li></ul>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[聊聊我写CGraph的这一年]]></title>
                <link rel="alternate" type="text/html" href="http://www.chunel.cn/archives/cgraph-anniversary-introduce" />
                <id>tag:http://www.chunel.cn,2022-05-02:cgraph-anniversary-introduce</id>
                <published>2022-05-02T17:12:50+08:00</published>
                <updated>2022-05-03T22:52:26+08:00</updated>
                <author>
                    <name>Chunel</name>
                    <uri>http://www.chunel.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p>大家好，我是不会写代码却准备all in云原生的纯序员——Chunel Feng，至于为什么要选择云原生，主要是因为我根本不懂什么叫做AI啊。</p><p>最近，趁着五一假期的期间，我参与了一个相似搜索相关的研究项目，同时也算是一个CGraph的实际落地项目。正好是去年这个时间吧，也是我现在在的这家咖啡厅，我开始创建CGraph这个项目，并且落了第一行代码，到现在也正好算是一周年了。想从起因、立项、现状和规划几个方面，来review一下这一年里这个项目的情况，和我的一些感悟和成长。</p><p>首先，还是照例先上链接：<a href="https://github.com/ChunelFeng/CGraph">CGraph源码地址</a><br /><br></p><h2 id="起因"><table><tr><td bgcolor=#D1EEEE>起因</td></tr></table></h2><p>去年这时，我一个朋友参加面试，面试官让他写一个节点之间解依赖执行的题目。后来他找到我聊这个问题，问如果是我，会怎么写。我当时也没很明确的思路，只是原来写过一点图查询算法，知道可以用二维矩阵或者邻接表来实现。</p><p><img src="http://www.chunel.cn/upload/2022/05/image-d60f2ab291054ba0a297f5abb4ff54d3.png" alt="image.png" /></p><p>那个时候，我在往caiss的框架里添加新的算法的时候，也遇到了一些问题，觉得随便加入一些东西，改动量就很大，还要改原先的一些逻辑，接口也不够通用。再联想到平日上（mo）班（yu）写逻辑的时候，都是把一些逻辑给算子化，注册到一个dag中按序执行。我就在想，为啥不自己写个dag框架玩捏。</p><p>这样的话，不仅是我，今后别人想做算法实验或添加逻辑的时候，也会方便许多。再进一步，如果做的好的话，兴许能 <b>沉淀出一套方法论，打出一套组合拳捏，形成一条护城河呢</b>！！！<br /><br></p><h2 id="立项"><table><tr><td bgcolor=#D1EEEE>立项</td></tr></table></h2><p>之前也聊过，我做事很少做规划的，而且比较喜欢和欣赏那种一招吃遍天、且极具美感的方法。感觉dag调度可以一劳永逸的解决我现阶段遇到的问题，那就直接开始了。</p><p><img src="http://www.chunel.cn/upload/2022/05/image-56fa5913e7d54a57b0214956c5c68955.png" alt="image.png" /></p><p>这就是当时的截图了，整个项目只有一句说明，一行代码也没有。要做成啥样，有哪些方法或者可能，我心里真的一点概念都没有——像极了现在融资之前先画个饼的互联网人。</p><p>后来摸排资料，总结几种办法。但考虑到要坚持纯手写、不调用三方包，基本也就确定了现在的 <b>统一实例+邻接表+拓扑排序+分支合并+分治+静态解图</b> 的大体思路。而就是这一套方法，一直沿用到现在，帮助我实现了很多功能，解决了很多问题。<br /><br></p><h2 id="迭代"><table><tr><td bgcolor=#D1EEEE>迭代</td></tr></table></h2><p>迭代的过程，也就是这一年的commit的记录，大家也可以查一下ChangeLog.md文件。具体的实现思路，就不在这里说了。我在做的过程中，基本每一处功能点都落文章了，有兴趣的话，大家可以去项目中查看。</p><p>就说说做的时候，自己其实很多思路都来自于自己的日常工作。我经常会想，我搬砖的时候用的这些框架，如果让我重写，我会如何实现；手头组织的这些逻辑，怎样才能更好的被支持。然后就这样一路的做下来，就有了现在的样子。</p><p>针对单进程调度层面，我们提供了<b>并发编程、切面编程、统一调度、外部注入</b>的思路，并且用了很大的精力，来实现调度优化。当前的现状，基本是我个人的能力极限，也很期待大家的意见和指教。</p><p>至于架构层面，比如：分布式调度、全局一致性、算力调度、灾备快照等逻辑，我自己的确是没这个能力了。如果今后要做的话，会考虑创建关联项目，然后引入三方库吧，也可能今后忽然掌握了某块知识，回头来自己实现一遍。</p><p><img src="http://www.chunel.cn/upload/2022/05/image-6367d091b38d4d168066f74d265d0935.png" alt="image.png" /></p><p>无论如何，我一直坚持主项目中仅用原生cpp实现，这样做有个非常明显的好处，就是不用考虑跨平台兼容，使用起来也基本不会有任何环境问题，非常方便使用者入手基本功能，跑起来<code>HelloCGraph</code>工程。<br /><br></p><h2 id="出圈"><table><tr><td bgcolor=#D1EEEE>出圈</td></tr></table></h2><p>为了扩展一些影响力，让更多的人看到和用起来这个项目，我一边写代码，一边写文章。穿插着还在我们自己的技术交流群里，开了几次线上直播，介绍实现的原理和现状，搜集意见、方向和规划。</p><p>同时，还投递并命中了几个业界比较知名的期刊和榜单，增加了曝光度和影响力。在这期间，还有几个朋友一起加入了共建的过程。感谢的话我们就不再说了，都在图里了，哈哈。</p><p><img src="http://www.chunel.cn/upload/2022/05/image-dff21b3b94e744768368b09a95a9f3dc.png" alt="image.png" /></p><p>最牛逼的是，我还发挥了自己沉寂了多年的文学天赋，给CGraph写了主题曲——<a href="http://www.chunel.cn/archives/listen-to-coder">《听码农的话》</a>，简直是码坛方文山了。<b>在没能做到技术上天和产品落地的条件下，实现了项目出圈和文体两开花，也算是在当下极度内卷的环境里，开辟了一条新的赛道吧</b>。</p><p>在这里，我们要特别说一下最近在做的事情——<a href="https://github.com/whenever5225/GraphANNS">GraphANNS</a>。我们打算通过【拆分打散+定向组合】原有图查询算法的思路（卧槽，map-reduce？哈哈），实现一些ANNS算法的创新研究和工程沉淀。</p><p>项目刚刚开始，今后也还有大量的适配、编码、实验和优化工作。也提前预祝今后可以在这个领域取得一些小小的成绩。也希望CGraph要<b>配得上、帮得到、撑得住、玩得转</b>高大上的AI领域研究。</p><p>看到这里，对这个项目还一脸懵逼的朋友，我推荐看看这个博客：<a href="https://blog.csdn.net/whenever5225">程序员王同学 CSDN</a>，相信看完之后，你就更懵逼了——毕竟是ANNS领域的资深专业人士写的，哈哈哈哈。<br /><br></p><h2 id="规划"><table><tr><td bgcolor=#D1EEEE>规划</td></tr></table></h2><p>如果你问我，现在市面上，有很多类似调度的框架，CGraph跟它们比有什么优势？我想说的是，我们功能少，bug多，没提供啥通用的算法，不支持异构计算，也没有网络或者文件功能。但就是因为这样，所有东西都要你自己做，你才能有所成长啊啊哈哈哈哈。</p><p>当然了，作为框架侧，我们也会坚持持续演进和支持。说几个暂时能想到的方向吧：<b>设计和完善插件系统，优化dag调度逻辑，提供流式处理的方法，加入协程调度逻辑</b> ，还有就是我们会坚持【<b>碰瓷名项目</b>】活动，在 <a href="https://github.com/CDev-io"><br />纯序员和他的开发者小伙伴们</a> 这个github group中，给大家提供一些开箱即用的CGraph结合其他项目的功能算子，也欢迎大家加入，提提意见和贡献力量。更欢迎大家可以把自己的项目或算法包提供出来，一起共（bai）建（piao）和赋（guang）能（gao）。</p><p><img src="http://www.chunel.cn/upload/2022/05/image-965f46a43a144bbfb1f31f1a8d4c4371.png" alt="image.png" /></p><p>看，我们小组已经有四个人了，而且分工明确，各有所长。特别说明一下，本来还请了一位百度的nlp研究员啊，没加进来啊，没进来的原因居然是github账号弄丢了，呜呜呜，重大损失啊。还有一位菜鸟的工程师啊，约好下班回家就加入的，可能现在还没有下班，哈哈哈哈。<br /><br></p><h2 id="总结"><table><tr><td bgcolor=#D1EEEE>总结</td></tr></table></h2><p>短短一年的时间吧，自己大部分的业余时间又都花在了开源项目（喝咖啡）上。相比两年前刚写开源的时候，感觉到自己的设计思路、问题排查和编程能力方面，都有了很大的提升。结识了更多的朋友，得到了更多大佬的指导和帮助，在这里向大家问好，给大家点赞。</p><center><p><img src="http://www.chunel.cn/upload/2022/05/image-2b511d90ad4744fd9e621e091904364e.png" alt="image.png" /></p></center><p>我自己也有意识的去尝试往一些相关或者交叉领域看一看，寻求出圈和合作的机会，为某一天的可能发生的脱变，积蓄一点能量。我们自己创建的技术交流群，在保持活跃的同时，技术话题的密度也肉眼可见增加。从诗词歌赋和人生哲学，聊到人工智能和云原生。</p><p>嗯，自己在日常工作中，也要加把油了，要争取做到“聊以致用”啊。对了，最后顺便提一句，我可是有正儿八经工作的程序员哦，哈哈哈！！！</p><pre><code>                              [2022.05.03 by Chunel]</code></pre><hr /><h4 id="推荐阅读"><strong>推荐阅读</strong></h4><ul><li><a href="http://www.chunel.cn/archives/cgraph-run-introduce">纯序员给你介绍图化框架的简单实现——执行逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-loop-introduce">纯序员给你介绍图化框架的简单实现——循环逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-param-introduce">纯序员给你介绍图化框架的简单实现——参数传递</a></li><li><a href="http://www.chunel.cn/archives/cgraph-condition-introduce">纯序员给你介绍图化框架的简单实现——条件判断</a></li><li><a href="http://www.chunel.cn/archives/cgraph-aspect-introduce">纯序员给你介绍图化框架的简单实现——面向切面</a></li><li><a href="http://www.chunel.cn/archives/cgraph-function-introduce">纯序员给你介绍图化框架的简单实现——函数注入</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-1-introduce">纯序员给你介绍图化框架的简单实现——线程池优化（一）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-2-introduce">纯序员给你介绍图化框架的简单实现——线程池优化（二）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-3-introduce">纯序员给你介绍图化框架的简单实现——线程池优化（三）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-4-introduce">纯序员给你介绍图化框架的简单实现——线程池优化（四）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-5-introduce">纯序员给你介绍图化框架的简单实现——线程池优化（五）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-distance-introduce">纯序员给你介绍图化框架的简单实现——距离计算</a><br /><br><br></li><li><a href="http://www.chunel.cn/archives/listen-to-coder">CGraph 主打歌——《听码农的话》</a></li></ul><hr /><h4 id="个人信息">个人信息</h4><p>微信: ChunelFeng<br />邮箱: <a href="mailto:chunel@foxmail.com">chunel@foxmail.com</a><br />个人网站：<a href="http://www.chunel.cn">www.chunel.cn</a><br />github地址: <a href="https://github.com/ChunelFeng">https://github.com/ChunelFeng</a></p><p><img src="http://www.chunel.cn/upload/2021/08/image-a63a5ca7d9104993a51c19d88e25454d.png" alt="image" /></p>]]>
                </content>
            </entry>
            <entry>
                <title><![CDATA[纯序员给你介绍图化框架的简单实现——距离计算]]></title>
                <link rel="alternate" type="text/html" href="http://www.chunel.cn/archives/cgraph-distance-introduce" />
                <id>tag:http://www.chunel.cn,2022-04-05:cgraph-distance-introduce</id>
                <published>2022-04-05T20:25:28+08:00</published>
                <updated>2023-07-09T23:36:31+08:00</updated>
                <author>
                    <name>Chunel</name>
                    <uri>http://www.chunel.cn</uri>
                </author>
                <content type="html">
                        <![CDATA[<p>大家好，我是不会写代码的纯序员——Chunel Feng，有一阵子没写我的技术博客了，大家是不是很急着看段子了！！！今天的段子特别多，希望大家慢慢看。。。</p><p>之前跟大家提过，我们之所以写CGraph这个项目，主要是为了对再早些我们写的一款向量检索引擎——caiss，做技术架构升级，全面支持图化和可插拔开发。写着写着，不断衍生出有很多实用的功能——比如，分块治理，元素切面，调度优化等——都是刚开始的时候，没曾想过的。</p><p>就这样，一直断断续续的写了近一年——毕竟平日还是要正常上班的嘛，只有周末休息的时间才有空写。这一年因为做开源项目，认识了很多新的朋友，也有了一些自己的成长和收获，这些都不一一说了。不过无论怎样，我们还是不忘初心，在这个时间点，开启了针对Ann向量检索领域的定制化支持，同时也开启了<code>CGraph-v2.0.0</code>版本的新阶段。</p><p><img src="http://www.chunel.cn/upload/2021/07/image-efc81a3291634d35a3b94ea0f6e938dd.png" alt="image" /></p><p>在Ann领域中，距离计算是必不可少的话题。这篇文章中，我会跟大家聊聊CGraph中距离计算组件的实现和扩展方法，并且说说一下我们对Ann领域，做的一些定制化开发和贡献。</p><p>我们还是照例，先上代码连接：<a href="https://github.com/ChunelFeng/CGraph/tree/main/src/UtilsCtrl/Distance">CGraph 中距离计算组件 源码</a></p><h2 id="整体思路"><table><tr><td bgcolor=#D1EEEE>整体思路</td></tr></table></h2><p>在做CGraph的距离计算库之前，我粗略的扫了一下几个主流的向量算法的实现方法。有些是默认支持float方法的；更多的一些呢，其实是支持template 传入具体类型，然后通过添加工厂注册的方法来判断需要计算的距离类型。</p><pre><code class="language-cpp">template&lt;typename T&gt;void* createDistFactory&lt;T&gt;(int distType) {    // 工厂方法，创建新的距离，需要实现新的Dist类，并修改工厂创建的代码    void* dist = nullptr;    switch (distType) {        case 1: dist = new DistCos&lt;T&gt;();        case 2: dist = new DistEuc&lt;T&gt;();        case 3: dist = new DistMht&lt;T&gt;();        // 添加新的距离计算方式，需要在原有主流程代码中做改动        case 4: dist = new DistMyFunc&lt;T&gt;();        default: dist = nullptr;    }    return dist;}</code></pre><p>我必须严（为）肃（锤）声（而）明（锤）一下，添加工厂元素，是要入侵式的修改主流程代码好么？作为在恋爱咨询领域有丰富经验的资深专家，我一直认为：<b>代码入侵这种东西吧，跟出轨一样，不要关注多少次，只要关注有或者没有</b></p><p><img src="http://www.chunel.cn/upload/2021/08/image-26fc4b229fe341ed9d73b0de0a5477d3.png" alt="image" /></p><p>还有，除了完成正常的计算工作之外，还能做一点什么方面的提升和优化（让我文章有内容可以写）呢？想了一下，我们主要在以下几点做改进：</p><ul><li>支持任意类型距离的计算，支持快速无入侵的添加自己的距离计算库</li><li>支持任何数据类型的计算（int，float，double啥的自由切换，别跟我说string了）</li><li>支持各种校验逻辑、归一化逻辑的扩展</li><li>代码简洁易懂，可维护性强，使用简单，可扩展性强</li><li>支持各种simd、cuda等加速计算功能（todo）</li></ul><h2 id="距离计算库"><table><tr><td bgcolor=#D1EEEE>距离计算库</td></tr></table></h2><p>具体实现的思路和代码逻辑结构，我就不说了哈——这也不是我们这篇文章的主旋律。大家有兴趣的话，直接去看一下 <a href="https://github.com/ChunelFeng/CGraph/blob/main/tutorial/TU05-Distance.cpp">CGraph距离计算库 源码</a> 吧，欢迎review &amp;&amp; commit。</p><p><img src="http://www.chunel.cn/upload/2022/04/image-8aa177021f054a3e86467d8e80f0bca8.png" alt="image.png" /></p><p>总之，结果是，实现了一个以下类型的<code>UDistance</code>子类之后，就可以通过 <code>UDistanceCalculator</code>类，实现向量计算逻辑了。使用的过程，一共就是上图中划红框的两句话。</p><p><img src="http://www.chunel.cn/upload/2022/04/code-cff2ec36c9a542df9d11cb0eb83f3476.png" alt="code.png" /></p><h2 id="插件引入"><table><tr><td bgcolor=#D1EEEE>插件引入</td></tr></table></h2><p>讲到这里，简单的提两句，因为是完全无入侵的方式实现的逻辑，也推荐大家来贡献一下AI领域比较常见的距离计算库的实现类哈，特别是各种加速计算的版本哈。简单的实现demo参考链接：<a href="https://github.com/ChunelFeng/CGraph/blob/main/tutorial/MyUtils/MyDistance.h">自定义距离计算 源码</a></p><p><img src="http://www.chunel.cn/upload/2022/04/image-c405bc01159e41e9be6e247bef4b2416.png" alt="image.png" /></p><p>大家有兴趣的话，可以自行去实现自己设计的距离，然后复用CGraph提供的计算功能。目前除了以上所说的逻辑之外，还支持了自校验、标准化、batch计算等功能，并且以上功能都支持无入侵的三方扩展的。</p><p>期待有机会跟大家一起完成一些共建工作。<br /><br></p><h2 id="本章小节"><table><tr><td bgcolor=#D1EEEE>本章小节</td></tr></table></h2><p>好吧好吧，上面说了这么多，主要是为了引出来这一小节的话题——那就是我们的CGraph成功出圈了，已经有了第一个开源的兄弟项目：<b>GraphANNS</b>  。我们之所以要升级到CGraph-v2.0.0版本，抽象DomainAnn结构，并且实现基础版本的距离组件，就是为了更好的支持这次出圈的合作共建。</p><p>最近，杭电&amp;之江实验室的同学，通过复写CGraph的算子和参数类，实现了对Ann传统算法的细粒度拆解，拼装，逐层调优和调度逻辑，简直就是mini版的blink + faiss的组合。今后也会基于此方法实现更多的算子，并完成相关实验，得到更优的结果。</p><p>希望在这个不断优化过程中，CGraph调度和组合功能，对实验人员有所帮助。也希望整个工程，今后在行业内有自己的影响力。具体工程的链接：<a href="https://github.com/whenever5225/GraphANNS">GraphANNS 源码地址</a>。<b>当然了，我们也会不断升级和优化CGraph的功能，并且提供尽可能多的定向支持，使得框架 配得上、帮得到、撑得住、玩得转 高大上的AI领域研究</b>。</p><p><img src="http://www.chunel.cn/upload/2021/08/image-caa147949fd24f4fb9759356268c89dc.png" alt="image" /></p><p>工程的作者是一位在Anns领域有丰富经验的研究人员，即将去浙大读博。也时刻关注最新的Anns算法，并且长期更新自己的博客专栏 <a href="https://blog.csdn.net/whenever5225">程序员王同学 in CSDN</a>，git主页 <a href="https://github.com/whenever5225">whenever5225 in Github</a> 里，还有一些其他论文和对应的工程实现，欢迎大家来关注和支持一波，一起交流和分享相关技术。</p><blockquote><p>PS：比某些天天在技术交流群里卖茶叶的浙大混混，不知道强到哪里去了。</p></blockquote><p>在此，我们也一并感谢技术交流群里给项目提供xmake编译脚本、搭建code review服务、帮忙贡献代码和提意见的童鞋。最后的最后哈，欢迎大家添加我的个人微信，大家经常交流，互相学习，共同进步哈。</p><p><img src="http://www.chunel.cn/upload/2021/12/image-7d7d581c97c241228cba67dbad4a9588.png" alt="image" /></p><pre><code>                              [2022.04.10 by Chunel]</code></pre><hr /><h4 id="推荐阅读"><strong>推荐阅读</strong></h4><ul><li><a href="http://www.chunel.cn/archives/cgraph-run-introduce">纯序员给你介绍图化框架的简单实现——执行逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-loop-introduce">纯序员给你介绍图化框架的简单实现——循环逻辑</a></li><li><a href="http://www.chunel.cn/archives/cgraph-param-introduce">纯序员给你介绍图化框架的简单实现——参数传递</a></li><li><a href="http://www.chunel.cn/archives/cgraph-condition-introduce">纯序员给你介绍图化框架的简单实现——条件判断</a></li><li><a href="http://www.chunel.cn/archives/cgraph-aspect-introduce">纯序员给你介绍图化框架的简单实现——面向切面</a></li><li><a href="http://www.chunel.cn/archives/cgraph-function-introduce">纯序员给你介绍图化框架的简单实现——函数注入</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-1-introduce">纯序员给你介绍图化框架的简单实现——线程池优化（一）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-2-introduce">纯序员给你介绍图化框架的简单实现——线程池优化（二）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-3-introduce">纯序员给你介绍图化框架的简单实现——线程池优化（三）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-4-introduce">纯序员给你介绍图化框架的简单实现——线程池优化（四）</a></li><li><a href="http://www.chunel.cn/archives/cgraph-threadpool-5-introduce">纯序员给你介绍图化框架的简单实现——线程池优化（五）</a></li></ul><hr /><h4 id="个人信息">个人信息</h4><p>微信: ChunelFeng<br />邮箱: <a href="mailto:chunel@foxmail.com">chunel@foxmail.com</a><br />个人网站：<a href="http://www.chunel.cn">www.chunel.cn</a><br />github地址: <a href="https://github.com/ChunelFeng">https://github.com/ChunelFeng</a></p><p><img src="http://www.chunel.cn/upload/2021/08/image-a63a5ca7d9104993a51c19d88e25454d.png" alt="image" /></p>]]>
                </content>
            </entry>
</feed>
