【华为人】——做横扫问题的“扫地僧”
读博的时候,我和华为曾经有过一段亲密接触。当时实验室和华为有一个关于无线网络环境下视频QoE度量的合作项目。我作为项目负责人,从视频编码、信道建模、传输协议等各个环节进行建模、算法、仿真、数据分析,得到了一个非常复杂的模型,自我感觉非常完善,却被华为研发人员无情地指出模型“太理想化”。在随后数次的交流中,华为提出了很多实际工程化中应用的约束和要求,都是我完全没想到的。
这次技术合作影响了我此后的博士学习生涯。我开始把大部分精力都花在算法在实际工程应用中如何解决问题上。算下来,至少写了超过15万行的代码。我越来越觉得,解决工程问题并不是只靠几个公式、模型和算法能完成的,从技术到实际解决工程问题的“最后一公里”更为关键,需要大量的工程经验积累。
从不绕过稀奇古怪的难题
2010年,我加入了华为,从事无线基站的软件平台的工程开发。这个平台支撑了全球超过500万基站的发货,支持十几种不同的CPU、上百种单板硬件、多版本编译器、多样的操作系统,庞大的代码量和复杂的业务逻辑,是复杂工程环境下的大规模应用项目,也是积累经验的绝佳机会。
我踌躇满志地准备大干一场,却没料到,遇上的全是稀奇古怪的难题。
2013年,为了让代码更具有灵活性和扩展性,我用了“反应式框架”重构了代码,也用了一些C++编程中的小技巧。这种技术方案我很早在学校就用过,而且也是很多开源软件中的常用做法。
我信心满满,认为代码实现得很漂亮,自然不会有什么问题。刚开始验证也很顺利,但是测试人员在一款比较老的芯片上进行验证时,却发生了严重的错误——程序崩溃了。我一下懵了,把重构的那部分代码看了又看,也对比一些开源代码中类似的写法,甚至还把C++标准文档的对应部分翻阅了一下,仍然非常确信这是正确的写法,实在百思不得其解。交付时间很紧迫,我只能用另外的实现方法绕过了这个问题,完成了交付。但这个诡异的问题却始终萦绕在我的脑海中:如果找不到根因,以后还会遇到。
我下决心把 “虫”捉到。我利用中午休息的时间,一点点去啃优化后的汇编代码,但有几行始终看不明白。请教了在这方面很有经验的老同事,也没有看到什么问题。连续两个中午,我翻来覆去看了好几遍还是没有进展,连吃饭都没胃口了。晚上回到家,我又仔细翻阅C++标准,一行行读下来,发现备注中有些线索指向C++ ABI规范。总算有点眉目了,第二天我找出相关规范,对照生成的汇编代码仔细对比,确实发现了配套编译器在此处的Bug,并通过咨询CPU提供商的工程师,证实了这个问题。
“虫”终于捉到了!我觉得入口的盒饭都异常美味。此后,我们也把这种写法加入到我们开发的静态代码检查工具中,用于提前发现问题。
在之后的几年开发过程中,我遇到了十多次和OS、运行时库、链接器、编译器相关的问题。我从来没有绕过这些问题,而是坚持把问题代码的根本原因找到,这让我积累了多样化的经验、更透彻的掌握计算机底层基础知识,同时让我对编码可靠性、移植性有了更深入的思考。程序员就是在不停解决问题的过程中成长,再通过沉淀和思考,成为技术专家。
欠下的“债”都要还回去
写代码是不是就是if-else的业务逻辑代码堆砌?我不这么认为。写出高效率、可扩展性强、高质量、简洁的代码不是一件容易的事。首先,作为程序员要有追求,不能仅仅以实现基本可用的功能为目标,“能打补丁用就行”等只顾眼前的做法会带来越来越重的技术“债务”,以致整个业务领域在面向未来的时候寸步难行。其次,要多学习和借鉴优秀的代码架构,从技术上提高自己,并把优秀的思想用在平时的开发中。
最难忘的一次“还债”经历是关于软件平台中的CPRI共享(CPRI-Sharing)特性的,这是基站软件平台支撑无线SingleRAN多制式资源共享的重要特性,复杂的算法支撑多达几百种场景组合。
当时代码已经复杂到一个函数3000多行,总共2万多行核心代码,每增加场景都可能导致以前的功能受损,几乎没人敢改,也不愿意从根本上解决这些“债务”。
刚开始我对代码细节不太了解,也是一样提心吊胆地往里面塞代码。每加一部分代码,就要把业务逻辑反反复复推演一遍,看看对于老的功能、其他的场景是否有影响。都说代码是开发人员的脸面,我想我的代码夹在这样的“烂代码”中,以后拿出去也不好意思见人啊!我决定把当前混乱的实现作一个高层次的抽象,重构这部分代码,实现“算法和场景隔离”。
经过一个版本的痛苦开发后,我对细节已经比较了解了,脑海里也有了完整的功能地图。在这个基础上,我想到曾经读过的一个跨平台多媒体播放器VLC(VideoLAN Client)的源代码。它完全基于插件化的思想,用一个精巧的插件管理和插件的“能力评分”机制,把视频、音频编解码器等作为插件灵活拼装。这就有点像搭乐高,可以根据使用需求进行组合,也可以随时删减模块,具备高度的灵活性。
我借鉴了这个思路重构了代码。每个算法对象都可以作为独立的插件,在运行时根据硬件能力和组网场景进行选择。重构后的代码非常容易理解,也具备很强的扩展性,最重要的是扩展新功能支持新场景时,对老功能几乎没有影响。之后的几年里,这个框架支撑了几次大的新需求,很少出问题。我想这应该算是好的代码实现带来的收益吧!
其实,开发人员在经过几年的编码后,常常会陷入到一定的瓶颈。这个时候需要打开边界,多看一些优秀的开源代码,尤其要精读一份高质量代码,用心体会其中的架构设计思想,这会帮助我们提升编码能力,形成自己的思想。
当“扁鹊二哥”需要勇气
2011~2012年,无线大规模切换到Linux操作系统,以前的代码编写错误导致的多线程死锁、数据竞争的问题在实验室测试时频繁出现。一旦上网出现问题,对基站业务来说都是致命的。
当时,无线不断强调员工要成为“扁鹊二哥”,提前发现问题隐患。既然问题都已经有苗头了,当然不能坐视不管了。我所在的软件设计团队决定通过对代码的静态分析来检测这类问题。
经过一段时间调研,我们找到一些简单的方案,但检查的准确度很低,能用的场景的非常有限。我直观地觉得,要能够解决大部分场景的检查,必须要基于C/C++的编译器的深层次信息。
我拿出读博期间的方式,查阅IEEE/ACM论文、看Clang的社区论坛等所有可能获得信息的地方。再加上团队的共同努力,这些问题逐步获得了解决。最终,开发完成的工具(“Soter”)在软件平台累积发现超过50多处多线程死锁、数据竞争的问题。
随后,我们继续增强该工具从TOP低级编码、安全编码、编程规范等多方面功能,在部门的编码质量看护上发挥重要的作用。在内源平台上开源后,也被很多部门和其他产品线部门应用。
回想整个过程,为了从根本上提升软件工程能力,彻底解决问题,我们才选择了一条艰难的道路。但这也让我们认识到,投资基础工程能力建设对整个开发团队的重要性。
理解技术的本质,才能跨界创新
在软件云化的趋势下,我们看到了云化软件在资源共享、弹性伸缩方面的优势,但在传统的基站硬件上,这些软件技术、框架都没法直接应用。只有理解这些技术的本质,使用“云化软件的思维”重构部分架构和实现才有可能。
去年,我和清华大学的老师一起开展合作项目,用的是老师提出的ASPCA算法进行日志降维分析。这个算法在常用的标准数据集上测试效果非常好,但是真正用到当前软件的日志上,效果却不好。一方面是我们的日志内容不规范,另外这个算法真正发挥作用需要大量数据前处理。直到我们通过日志模板提取、日志分区、分模块、按时间轴纬度分析等数据处理方法后,算法才真正发挥作用。
我最大的体会是,想到这些Idea也许没那么困难,甚至做出技术原型的复杂度也没那么大,但是真正能用在一个商用化的、大规模的系统中,困难程度远超想象。这个时候,我们会发现,这些技术和方法,离真正能解决工程实际问题这“最后一公里”还有很大距离,而这正是我们需要着力的方向。
我想我会继续坚持对软件的热爱,打开边界、脚踏实地,在看似枯燥的工程开发中不断解决问题,发掘技术的金矿。
【问答时间】
问:你成长为“软件王”,有啥秘籍?
答:阿里巴巴在的技术合作人“多隆”,是加入公司16年来一直在编码的“扫地僧”。他曾经说过,技术成长的秘诀就在不停地解决各种工程问题,从业务代码看到框架代码、库、libc代码、操作系统内和代码等。我非常认同,程序员就是在不停解决问题的过程中成长。
问:开发工作真的有技术含量吗?
答:如果不打开边界,不去了解业界发展,只局限在当前的工作领域涉及的技术,就会觉得工作没有技术含量。如果只是从外界了解一些技术概念、想法,而不真正理解技术本质,并解决技术落地的“最后一公里”问题,那也不可能真正提升能力。
问:你通过什么方式解压?
答:循环听一首非常振奋人心的音乐,或者去看电影来解压。最喜欢的一首歌:It’s My Life,最喜欢的电影:《星际穿越》。
问:你的爱好是什么?
答:我喜欢玩一些卡牌类的游戏。大学的时候,整整一年时间,整个寝室同学把所有的钱都拿来到国外买“万智牌”,没钱就吃泡面和饼干,哈哈。
本文为《华为人》版权所有,未经允许不得转载。如需转载请联系编辑部hwrb@huawei.com
华为人期刊
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。