写底层写久了,才慢慢想明白的几件小事

写在又一次深夜调试之后。没有新算法,没有完整可运行的工程Demo,只是敲了很多年代码之后,沉淀下来的一点个人感悟。

最近很长一段时间,一直在和内存、系统调用、各种诡异边界case打交道。看多了开源项目源码,踩过不少线上难以复现的坑,慢慢意识到:大部分棘手问题,并不是我们缺少精妙的算法,而是底层认知存在盲区。书本、教程大多教我们如何写出可以跑通的代码,但很少提醒我们,哪些思维习惯,决定了代码会不会埋下长期隐患。

新手很容易陷入一个误区:代码编译通过,测试环境输出结果符合预期,就代表代码是正确的。

越靠近系统底层,越能体会到:能跑,只是最低门槛,不等于没有问题。

一段程序,在测试机平稳运行几小时,一旦放到高并发、内存紧张、网络随机抖动的真实生产环境,各种奇怪bug就会集中爆发。

关于抽象:不要迷信抽象,也不要敌视抽象

刚学编程的时候,我非常迷恋高度抽象的写法。多层封装,寥寥几行接口就完成复杂逻辑,感觉优雅又高级。踩坑踩多之后,心态发生了很大变化。

抽象本身自带成本。每增加一层封装,就多一层黑盒。你不知道这层封装内部隐含多少次内存分配,不知道背后隐藏多少次系统调用,也不清楚异常场景下,它会默默吞掉哪些错误。

一旦出问题,bug往往不在你写的业务代码中,藏在多层封装深处。堆栈层层跳转,定位问题的成本成倍增加。

但反过来,如果完全拒绝抽象,凡事直接裸调用系统接口,到处复制粘贴重复逻辑,又是另一种灾难。代码大量重复,后续修改一个逻辑就要同步十几处,维护直接崩盘。

现在我的看法变得很朴素:

抽象不是越高级越好,而是要看得见代价。

当你选用一个封装库或者框架,最好清楚它底层大致做了什么、开销多大、限制是什么、失败时会表现出什么行为。如果你完全不清楚底层发生了什么,再优雅的封装对你而言,都是一颗定时炸弹。

性能这件事:过早优化是恶,但完全不思考性能更是恶

“过早优化是万恶之源”这句话几乎所有程序员都看过。很多人把它理解成:先随便写,保证跑通,性能问题等上线之后再优化。

现实往往很残酷。大量性能瓶颈,在架构设计阶段就已经定型,后期很难修补。

循环内部频繁申请小块内存;热点路径持续锁竞争;明明可以批量处理,却反复发起系统调用。这些代码写的时候轻松简单,等到流量上涨,CPU飙升、内存碎片、延迟抖动接踵而至。等到上线发现性能不足,业务逻辑已经堆积很多,重构改动风险巨大,基本不敢大刀阔斧修改。

“不要过早优化”,不是让我们无视性能,而是不要做无依据的微观抠细节。不必一开始就纠结循环如何少消耗几个CPU周期。但是在设计接口、选定数据结构的时候,就要预判这个路径会不会成为热点,会不会产生不必要的分配、拷贝、系统调用。

设计阶段评估代价;实现阶段保持代码朴素;遇到性能瓶颈依靠观测工具定位,再针对性优化。这是我现在觉得最务实的思路。

错误处理:最容易被敷衍,却最考验工程素养

写Demo的时候,我们习惯性忽略返回值。
malloc不判断空指针,系统调用不检查返回码,函数出错直接忽略继续往下执行。Demo跑起来很爽。

放到真实程序中,错误不会按照你预想的顺序发生。内存耗尽、文件打开失败、中断返回EINTR,各种各样的异常悄无声息到来。

不处理错误,程序不会立刻崩溃。它会把损坏的内部状态隐藏起来,继续运行,很久之后,在完全无关的地方宕机。这类bug,是调试成本最高的一类。

当然,也不是所有错误都要写冗长的if判断。关键是想清楚:调用失败之后,程序后续还能不能正常工作?哪些异常可以容忍,哪些必须直接终止,哪些场景需要重试。

很多人讨厌写错误判断,觉得它割裂主逻辑的阅读感。可工程代码,本来就不只有“一切顺利”这一条执行路径。

读源码和写代码

读源码不等于复制源码。

以前我有个误区:多看几个开源项目,把里面酷炫技巧抄过来,能力就会提升。后来才明白,阅读代码最重要的不是记住它怎么写,而是思考:作者当时面临什么样的约束?他做了哪些取舍?这个设计优点是什么,代价是什么?

同一个问题有很多解法,每一种选择都在权衡。不存在完美方案,只有适配当前场景的方案。很多开源库精巧的设计,是为了解决它自身业务场景,直接照搬过来,放到你的项目里反而画蛇添足。

写代码是向外输出,读源码是向内吸收。不要只学表面写法,要读懂背后权衡。

关于调试:不要靠猜,要靠证据

遇到诡异bug,人脑天生喜欢脑补根因。现象一出,脑子里立刻生成一套自洽猜想,之后所有观察都会不自觉往猜想上靠拢。顺着猜想改几行代码,碰巧现象消失,就认定找到了问题根源。

很多时候,只是把问题掩盖了,隐患依旧埋在代码里,换一组运行条件就会再次复现。

现在我会反复提醒自己:猜想只是猜想,必须要有证据证实。

日志、core dump、perf、gdb、内存统计,工具给出的客观现象优先级高于直觉。直觉可以用来生成假设,但绝对不能当做结论。

最后一点感悟

写底层写多了,会慢慢褪去对花活的迷恋。

好的工程代码,未必写法多么惊艳。清晰的边界,可控的代价,完备的异常思考,容易排查问题,比花哨技巧更加珍贵。

很多技术道理,看书看文档只能看懂字面意思。只有自己实实在在踩过坑,调试过那些匪夷所思的问题之后,那些文字才真正内化变成自己的东西。

技术成长不是记住更多API,更多是思维方式一点点迭代。

© 版权声明
THE END
喜欢就支持一下吧
点赞13 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容