2024-09-18 10:33:59
最近,一款名为 Cursor 的 AI 编程工具火遍了编程圈。自从 ChatGPT 发布以来,AI 正以肉眼可见的速度进化,从最初的翻译和总结,到现在的语音、视觉和思考能力,极大地方便了我们的工作和生活。许多人担心,未来 AI 会不会取代人类?
作为一个程序员,我的大部分工作都与代码打交道。在编程领域,AI 也展现了强大的能力。例如,Github Copilot 极大地提高了编程效率。Copilot 就像一个“副驾驶”,可以帮助你完成代码片段,但它无法自主完成整个项目,仍然需要你懂一些代码。
但是 Cursor 不一样。你只需描述自己的想法,它就能自动生成代码,重复这个过程就能完成整个项目。它有多强大呢?有一个 8 岁的小女孩借助 Cursor AI 在45分钟内写出了一个聊天机器人,着实令人震惊。
一些程序员担忧自己会失业,而产品经理们开始弹冠相庆,表示以后再也不用依赖程序员,靠 AI 就能实现自己的想法。我最近也在思考这个问题。我从事编程已有十多年,自认为还有一些积累,难道这么快就要被 AI 取代吗?
我曾写过很多代码,也在 Github 上开源过许多流行的项目。平心而论,那些代码写得有多好吗?并没有,只能说是凑合能用。除了我自己其他人就写不出来吗?绝对不是,能写出比它更好的代码大有人在。所以,就写代码而言,我本来就能被别人取代,AI 的出现,只是换了个取代者而已。
那我过去做的那些项目,为什么拥有那么多用户呢?显然不是因为代码写得好,而是因为它给用户创造了价值。很多开发者的代码水平并不顶尖,但他们能精准发现用户需求,并用代码满足这种需求,创造了价值,从而获得丰厚的回报。如果单独看那个 App 或网站,会觉得“不过如此,我上我也行”,但事实是,你上你不行。你可能的确能写出那个代码,还能写得更好,但那并不是重点,重点是他借助代码创造了价值。
写好代码容易,创造价值很难,把价值变现更难。
我曾在某大厂做性能优化,当时我想,每让程序运行快一秒,就相当于给用户节省了一秒的时间,一天有几亿人用,我相当于给用户节省了几千天的时间,时间就是金钱,我给用户节省的时间就是为用户创造了金钱。然而事实却是,我的工资与给用户创造的价值完全不匹配。
我曾写过很多代码,有的是开源的,有的是免费的。开源项目 star 过万,免费产品月活过百万,它们一定给用户创造了巨大的价值。一些开发者用我的开源项目也创造了价值。我有个前员工说,他用我的开源项目接私活赚了钱才来面试的,但事实是,我并没有通过这个项目获取到直接的收入。
所以说,创造价值很难,把价值变现更难。
在 AI 的浪潮下,担心自己是否会被取代并没有意义,因为你本就不是无可替代的。多想想如何创造价值,如何把自己创造的价值变现才是更重要的事情。
AI 可以成为强大的助手,但真正理解用户需求、创造独特价值并将其变现的能力,仍然是人类的优势。通过不断学习和适应新技术,我们可以在这个快速变化的时代中找到自己的位置,继续为社会创造价值。
2024-01-18 10:32:11
自 2018 年我发布 VirtualXposed 以来,这 6 年里我做了不少开源或者免费的产品,用过我 App 的用户,没有千万也有百万;然而,所有这些东西都是完全免费的,我没有任何直接的经济收益,「用爱发电」说的大概就是这了。
几天前我把小狐狸钱包的助记词搞丢了,当时我很着急,因为多年以前我就把这个钱包的地址放在我的很多捐赠页面,心想里面没准有一笔巨款呢;结果我去 etherscan 上去一查,没有任何交易记录,资产是零,总算有惊无险。
这听起来似乎很滑稽,但却是绝大多数“用爱发电”项目的现状,靠用户自发捐赠,可能连一顿饭钱都挣不回来,更别提养家糊口了。
不挣钱倒是没啥,毕竟我们都是手艺人,那两把刷子养活自己还是没啥问题的。更让人难受的是,我们还经常被各种网络喷子问候,那些无端的谩骂、诋毁甚至毁谤,会让人产生自我怀疑:“我到底是在图个啥?”。
如果你喝了一杯奶茶,那种甜味只能维持几分钟;而如果你喝一杯咖啡或者吃一片柠檬,那种苦涩会在脑海里回荡很久。人就是这样一种奇怪的生物,那些友善用户支持和赞美的话语,可能转眼就被抛诸脑后,但那些恶意的言论却会让人久久无法释怀。
这些年里,我经历过若干次停更,也有很多人因为立场或者意见不合诋毁甚至毁谤我,而由于软件 Bug 或者功能缺陷被用户喷到自闭的次数更是数不清。这期间,我养成了很多自我保护的习惯,比如说不加自己不是管理员的群,不看任何陌生人私信,苗头不对不废话直接拉黑… 现在我已经炼就了一项颅内过滤器的技能:能把那些不适的言论当作压根不存在。
我知道肯定有人会说,都是成年人了不要这么玻璃心,你就这点承压能力?但当你一个人面对数百万用户的时候,你就会深刻地体会到,生物的多样性真不是盖的,有些物种,它的脑子可能还是三叶虫,但那嘴皮子绝对是次世代的外星科技。
不过,虽然林子大了什么鸟都有,但鸟大了也是什么林子都有。既然改变不了别人,那就改变自己;咱也不扯什么鲲鹏之志,但天天跟麻雀蝙蝠缠斗,注定成不了什么大鸟。
很多“用爱发电“的开发者把自己搞得很累,其实你把它当作是钓鱼,摩托,音响,单反那样的兴趣爱好就好多了,既然是爱好,就不要搞得像打第二份工一样,开心了就玩,玩腻了就扔一边。我分享一下我现在对于“用爱发电”这件事的心态,不一定对,给同类中人一个参考:
最后,愿所有用爱发电的小可爱们都能被温柔以待,晚安!
2023-11-10 18:05:54
前几天我发了一篇文章:小米的存储扩容是拆承重墙吗,文中站在非技术角度解读,其核心论点是“小米公司不可能背着巨大的风险去做收效甚微的事”,有粉丝表示赞同:“当对技术不了解的时候,还可以从基本逻辑来理解”;然而有一些童鞋嗤之以鼻,表示没有技术分析,纯属 YY。
我观察了这些粉丝过往的留言,发现他们大部分都是懂技术的,其专业程度远超普通用户;我对此表示理解,他们就是因为我的技术关注我的,现在我在这说别的,当然属于是在扯淡。的确,我做技术已经十年了,在某些领域做得还不错,这自然会给人一种固有的刻板印象;五年前,我也因为技术而从大厂出来选择自主创业;不过,我对技术的理解并非你们想象中的那样。
技术,只是解决问题的一种方式。它是你日常工具箱里面的一种工具,就像水管松了用扳手拧,灯泡不亮用万用表测,衣服破了用缝纫机补一样。
然而,很多技术人员总喜欢尝试用技术解释一切,误认为技术能够解决所有问题。我上面举的例子可能更偏工程技术一点,但即使是科学技术也只能解决特定领域的问题,甚至科学本身也是有范畴的。打个比方,你能用技术解决婆媳矛盾吗?你能用技术解决巴以冲突?你能用技术让 Google 进入中国?很显然不能,甚至你邻居家小孩天天在门口拉屎都用技术解决不了。这种“拿着锤子看什么都像是钉子”的问题,并非只有技术人员才有,但越优秀的技术人员,往往越容易陷入这个怪圈。
技术人员还有一个特点,因为他们懂技术,动手能力强,遇到问题往往喜欢自己折腾,除非实在搞不定,绝对不会交给别人。我在网上见到太多的人,它们自己亲自注册公司,自己去给自己软件申请著作权,自己去开通 ChatGPT 账号… 事实上呢,这些事情花钱请别人都能搞定,而且不需要花很多额外的钱。这种“花钱买别人的时间”就是典型的非技术思维,你花钱请别人做给你自己省下来的时间,如果你能创造更多的价值,那干嘛要自己做呢?有人说,我的时间不值钱,让别人做纯属亏本;那你能不能想想怎么把自己时间变得更值钱?
我知道我今天的这两个观点在技术人眼中大逆不道,但我还是要讲出来;我粉丝的绝大部分都是年轻人,很多都在读书还有的刚步入社会;我们在学校的时候,老师只会教我们“好好读书,学好知识(技术)”,但是技术只是一种解决问题的工具,当我们步入社会,会遇到很多技术无法解决的问题;所以越早意识到这一点,主动培养自己技术之外的能力,你的生活会变得越轻松。很遗憾我当年毕业的时候没有人告诉我这一点,因此我作为一个愣头青愣了很多年;我没有想要让你们认同我的观点,把它当作维叔的一个友情提醒就行了。
十年前我花了半个月的工资买了一个自定义域名 weishu.me
,并用它打造了自己的技术博客,博客中有一句座右铭:“不为繁华易匠心”,今天我想把它改成:“君子不器”。
2022-06-12 19:57:10
若干年前,我还在做 Android 客户端性能优化的时候,读到了 OpenResty 作者章亦春老师的文章:动态追踪技术漫谈,当时被深深地震撼到了,原来通过使用各种高级的调试技术,解决问题竟然可以做到如此精准而优雅。
然而当我真正要解决 Android 系统上应用程序的性能问题时,才发现理想很丰满现实很骨感——手头趁手的工具几乎没有。文章中提到的内核态追踪技术 SystemTap / DTrace 在 Android 系统上压根不存在,用户态的追踪技术开销大到可怕:TraceView 开启后程序性能直接下降十倍不止,Systrace 当时功能半残废,使用起来还需要自己插桩;simpleperf 能使,但就是有点 simple……到最后,真正要解决问题的时候,还是靠经验、二分法和 inline hook;为了定位 Android 虚拟机的性能问题,我甚至还自己造了个 ART HOOK 的轮子。
然而,时间来到 2022 年,世界已焕然一新:eBPF 这种革命性的技术改变了一切。
eBPF is a revolutionary technology with origins in the Linux kernel that can run sandboxed programs in a privileged context such as the operating system kernel. It is used to safely and efficiently extend the capabilities of the kernel without requiring to change kernel source code or load kernel modules.
简单来说,eBPF 是一个运行在 Linux 内核里面的虚拟机组件,它可以在无需改变内核代码或者加载内核模块的情况下,安全而又高效地拓展内核的功能。
eBPF 的前身是 BPF(Berkeley Packet Filter) 技术,它原始的含义是 extended BPF。BPF 是一种古老的技术,最早可以追溯到 1992 年,它是为捕捉和过滤网络数据包而设计的,鼎鼎大名的抓包软件 Wireshark 就是基于它实现的。然而,经过若干年的发展,eBPF 早已脱胎换骨,成为 Linux 内核可观测技术事实上的标准。
在 eBPF 之前,给内核拓展功能非常麻烦。由于内核是硬件资源的管理者,其对安全性和稳定性的要求非常高,所以它的功能迭代周期相比应用程序慢得多。如果想要在内核中添加新功能,要么就直接修改内核代码,要么通过内核模块(LKM)实现。修改源码的话,自己维护成本高,进主干发布周期长,改完替换内核还有风险;用内核模块的话,由于 Linux 内核不提供稳定的 API,其维护成本会很高,内核模块在面临不同的内核版本时会面临巨大的困难。
eBPF 通过在内核中实现一个轻量级虚拟机,可以动态地加载和运行自定义的程序,通过这个自定义的程序可以轻松地拓展内核的功能。虚拟机在运行程序之前会进行校检,可以确保其不会导致 panic(回想一下内核模块,一不小心系统分分钟重启给你看..)另外,在 BTF 的加持下,eBPF 程序可以在跨内核版本上做兼容(内核模块再次泪目…
扯了这么多,有人会说,这玩意不就是一个运行在内核里面的解释器嘛。。就像运行在浏览器里面的 Javascript 引擎一样,有什么好稀罕。这个类比好像没毛病,但由于它运行在内核里,Linux 上独此一家,因此得万千宠爱于一身。因为,Linux 内核它真的太需要一个虚拟机了…
eBPF 技术最早在 Linux 3.15 被引进,从时间上来看已经不算是一种新技术了,而且它在服务端的应用已经有很长的历史,为何到现在咱才想起它呢?
这玩意它首先得益于云原生技术的蓬勃发展,不过跟咱要讲的 Android 没啥关系,暂且不表。对 Android 系统来说,GKI(General Kernel Image) 的出现,让 eBPF 登上了历史舞台。
GKI——通用内核镜像,是 Google 为了解决 Android 碎片化而提出的一种技术。
在 GKI 之前,Android 内核的碎片化非常严重,不同的设备制造商、不同的设备型号,甚至不同的设备版本,其运行的内核代码都不一样;这就导致在内核里面添加功能维护成本巨高,进而使得内核升级几无可能(回想一下以前的 Android 设备,其内核版本出厂之后除了安全补丁,基本上被锁死了)
而 eBPF 作为内核的一个功能,它的启用需要依赖特定的内核编译配置,如果某个内核没有开启某编译选项,eBPF 可能就用不了;在 Android 内核碎片化的年代,想要搞到能完整支持 eBPF 的设备,那是挺难的;如果想用 eBPF 这个功能,只有自己去改内核代码然后自己编译,然而你会遇到各种设备驱动问题,比如触屏失灵,Wifi 不工作等等等等。。当你好不容易在自己设备上折腾好,想要在别的设备上运行时,哦豁,它不支持 BTF,你的 eBPF 程序跑不了。。
GKI 通过统一核心内核,把其他功能(如 SoC,ODM 等提供的)从内核剥离并提供稳定接口(KMI),一举解决了碎片化问题。并且,Google 强制要求,Android 12 以上版本的设备,出厂必须使用 GKI 内核。
更重要的是,GKI 内核的编译选项,完整支持 eBPF 的几乎所有功能!!也就是说,你拿到一个 GKI 的设备,无需自己编译内核代码,它必然支持 eBPF;你在一个 GKI 设备上编写 的 eBPF 程序,可以轻松地拓展到其他的 GKI 设备!
所以现在这个时间点,在 Android 12 已经发布,Android 13 已经 beta 的情况下,是时候开始学习和使用 eBPF 了。
长篇大论了这么多,把 eBPF 吹的那么神,这种所谓的革命性的技术能拿来干啥?官方文档这么说:
eBPF 在现代数据中心和云原生环境中提供高性能的网络和负载均衡,以低开销提取细粒度的安全可观察性数据,帮助应用程序开发人员追踪应用程序,为性能故障排除提供洞察力,预防应用程序和容器运行时的安全执行等等。eBPF 的可能性是无穷的,它所释放的创新才刚刚开始。
那么,在 Android 系统上,它能干什么?按照官方的说法,我们可以拿它来动态追踪应用程序、解决性能问题;可能还不够具体,我举几个例子。
我们知道,应用程序与内核打交道的方式就是系统调用,我们在分析应用程序的时候,监控系统调用是一种非常有效的方式。在 eBPF 之前,我们通常有如下方法:
然而,如果我们使用 eBPF,那事情简直不要太简单,写几行脚本就能实现,甚至有的还是现成的,比如 opensnoop
和 execsnoop
工具,以下是对某加固程序的 opensnoop
运行截图:
用 eBPF 可以很方便地编写各种 portable 的 hook 程序;它可以通过内核的 kprobe/uprobe/tracepoints/USDT 来动态监视甚至修改系统的状态。其中,kprobe 可以在内核的几乎任意地方注入代码, 不只包括函数的入口和出口, 也可以是函数内部的某个偏移地址;uprobe 是 kprobe 的用户空间版本,它可以对你的应用程序注入代码。比如说,我曾想观察 Android 应用对线程的创建,为此,我通过 plt hook 技术 hook 了 libc.so 中的 pthread_create 方法;而现在用 eBPF 的话,通过 bpftrace,我都不需要编写额外的代码,一行就搞定了:
1 |
bpftrace -e 'BEGIN { printf("%-10s %-6s %-16s %s\n", "TIME(ms)", "PID", "COMM", "FUNC");} uprobe:/apex/com.android.runtime/lib64/bionic/libc.so:pthread_create{ printf("%-10u %-6d %-16s %s\n", elapsed /1000000, pid, comm, usym(arg2));}' |
由于 eBPF 可以对内核中几乎所有函数插桩,如果我们在内核的某些关键链路加入观测逻辑,那我们就可以监控特定组件的性能;比如说,我们发现应用程序启动过慢,怀疑是有大量的文件读写积累耗时。传统的方法比如微信的 Mars 组件,通过 hook libc 中的 io 函数做监测,而如果使用 eBPF,我们可以直接在内核中监控 vfs,开销更低并且更准确,而且还简单..因为别人已经写好了,一个命令完事;如果我们想监控系统的 io 和网络模块,同样一个命令就能告诉你个大概。。
抓包也是一个很常见的需求,通常情况下,我们有这么些方式:
以上无论何种方式,都可能被应用程序检测到,并且,通常情况下 https 的包抓起来没有那么容易。而如果我们用 eBPF,可以直接在内核中对网络模块插桩,无需绕过 https 证书直接抓包;事实上,有大佬已经写好了:eCapture(虽然目前还不支持 Android,但理论上是可行的。
当然,eBPF 的功能还远不止这么多,正如官方所述,它释放了无穷的可能性,我们可以拭目以待。
另外,虽然现在 eBPF 已经很成熟了,但是让它在 Android 系统上运行的资料并不多,接下来的文章我将介绍如何在 Android 系统中使用 eBPF 以及我们具体如何使用 eBPF,敬请关注~
2022-02-11 13:57:14
去年春节之前,妈妈打电话给我说,我们老家那边有疫情,听说高速路口都封锁了;今年你在外面过年,一定要好好搞点吃的,过个好年。
我听完之后一时间有点恍惚:长这么大,我还没有在外面过年过;每年春节,无论我身处何地,无论回家要转多少趟车,我都会回家陪爸妈过节。电话完之后我赶紧又给老家那边的疾控中心打电话确认了一遍,被告知疫情是隔壁县的,可以放心回家,心里一块石头落了地。终于,我在腊月廿七那一天踏上了回乡的旅途。
那一天我早早地起了床,在经过大半天的转车之后,终于在下午五点左右下了高速出口;结果出人意料的事情发生了:路上的一座大桥被人为堵住,回家的路被彻底堵死了;我辗转千里,却在离家不到两公里的地方被拒之门外。
这座桥大约有四层楼房那么高,其实按照障碍物的高度我是可以从侧边翻过去的,不过万一掉下去我可能就凉了;从上面翻的话,没有着力点,而且对面的就是社区的管理人员,看到你爬越就过来推你。常规方法这条路行不通。
我也尝试过跟工作人员沟通,说我们(很多人都被堵住)大老远回家过年,也不知道路会封,行行好让大家回家团个圆;它的话让我印象深刻:我不管你回来干啥的,这条路就是不让过。
后来实在没有办法,我们选择了直接从河里渡过去;就这样,在室外温度 3℃ 的条件下,我们徒步渡过了 30m 宽的河,美其名曰:大冬天偷渡长江(支流)。
当天晚上我非常气愤,打算直接去举报,并且还准备在公众号发文揭露一下;深思熟虑之后还是作罢了。社区工作人员都是村里人,我的亲人们不可能像我一样常年旅居在外,举报之后难免被穿小鞋;唉,成年人的生活就是这么无奈。
到家之后我惊奇地发现,老妈竟然换成了智能手机;我爸妈都是 50 后,之前有好多次劝说给他们换智能手机,我还放了几个手机在家里,我爸慢慢学会使用了,可是我妈却坚持使十几年前那种小灵通样式的手机;我实在没办法,专程去给她买了个老人机。
没想到短短的一年间,她竟然自己主动换成了智能机。我就问我妈怎么突然想通了要用智能机了,她说老人机用不了健康码——疫情已经持续两年多,它深刻地改变了我们的生活方式。
后来有一天在家我打准备开电脑摸摸鱼,四处寻找插线板;然后我爸给我找了一个出来,一脸得意地跟我说,这是我两块五在拼多多上买的,又便宜又好用。我顿时大为震惊。
我爸,一个年过花甲的老人,当了一辈子农民,手机上连打字都不顺畅,竟然学会了网购?
用着那个两块五的插线板,我越想越不安:这玩意会不会漏电,便宜的没好货啊。于是我就去给我爸说,拼多多上好多假货,他们那个砍一刀啥的都是骗人的,然后我还费了老大劲给他解释砍一刀小数点后面六位数的故事;我告诉他插线板这种东西还是要看质量,万一出现安全问题就完了,以后你要是想买东西就给你儿子说,我给你买;说完我就把它手机上的拼多多卸载了。我爸听完之后一脸失望地走开了。
又过了几天,我叔叔他们来我家拜年,无意之中聊到了拼多多。没想到拼多多获得了大家的一致好评,甚至是追捧。这一次,我没有说拼多多的坏话,安静地听他们讲拼多多的各种好:拼多多上的纸巾几块钱一大袋,质量比街上的那些破纸好多了;拼多多上的空调几百块钱很耐用,街上买的空调几千块用了几个星期就坏了;拼多多上买的水果又便宜又新鲜;拼多多…又便宜质量又好。乡亲们对拼多多的评价,跟我们在公众号、微博,知乎上的评价大相径庭,仿佛彼此生活不在一个世界。
再后来,我去街上逛了一圈,终于释怀了:拼多多上某些商品的质量可能的确比不上京东淘宝,但是秒杀农村街上的那些劣质商品绝对是绰绰有余;拼多多,让五六线城市的普通民众感受到了「性价比」和「质量」。这个结论可能会让人觉得非常诧异,但这的的确确是我亲身体味到的;拼多多改善了亿万人民的生活,它让我肃然起敬。
年轻人回家过年,总免不了被七大姑八大姨围观;问一些诸如“工资多少呀?”、“这么大了还不结婚?”,“什么时候生小孩?”的问题。很久以前我也非常反感这种问题,甚至会恶意抬杠:“今年挣的不多,也就四五百万的样子”。谈到结婚生娃的问题,我会反驳说:咱们又不是猪,到了年龄就要生崽。
不过后来,我再也不会这样了。乡亲们没有去过外面的世界,他们一辈子待在农村,对他们来说,结婚生娃,一辈子就圆满了。虽然在我们看来这种观念非常不对,但在他们的世界观里面,这就是所谓的好日子;他们本质上是希望你过得好或者说往过得好的路上走的,并没有什么恶意。也许你会说:“我不需要这种关心”,“你觉得好跟我觉得好不是一回事”,“以爱之名”;但遗憾的是,在他们有限的认知里面,你跟他们讲这些,他们脑子里面也许是一片空白。他们不懂这些道理,不理解我们,这是时代的局限性,不怪他们;而我们有幸懂得了这些道理,更应该去包容和理解他们,而不是觉得自己懂几个道理就很了不起。
大年初六,我又踏上了离乡的客车。看着窗外消逝的轮廓,我想起了无数次离乡时的场景;这么多年在外漂泊,我们以为自己经历了很多,但不过是翻开了现实的一点皮毛;曾经有一句话很火:“我懂得很多道理,却仍过不好这一生”;我更想说的是:“我们懂得了很多道理,更应该好好过这一生。”
2021-09-26 18:37:53
你如今的气质里,藏着你走过的路、读过的书、爱过的人,以及学习过的编程语言。
如果把编程类比作武侠或者修仙里面的打怪升级,那我在入行的头几年,一直在痴迷于各种各样的招式;那时候我学习和体验了各种各样的语言:C/C++/Java/Javascript/Kotlin/Python/Ruby/Scala/Clojure/Scheme/Erlang/Haskell,不过自从我学习了 Haskell 之后,这份列表就基本停止增长了;虽然后来也对 Elixer、Go 也有过简单的了解,但是也仅限于了解了。
一方面随着从业时间的增长,要解决的问题逐渐变成了系统问题或者业务问题;另一方面也是体会到,语言只是工具,招式再花里胡哨也是徒劳,修炼内功才是王道。
不过,在一次机缘巧合之下,我再一次接触到了 Rust。
那是在某大型 5V5 手游上个赛季更新的时候,我制作的游戏体验增强工具就用不了。当时我就想,与其用一些第三方工具找基址,不如直接自己整一个?如果再配合上我自制的内核 rootkit,那岂不是所向无敌?
一不做二不休,我就开始着手写一个内存修改器了。这种类型的工具,最出名的莫过于 CE,于是我就直接去看它的源码;这个项目是用古老的 Pascal 语言写的,我花了点时间了解语法就开始啃;啃着啃着就觉得浑身难受,这门语言语法其实很简单,但是它提供的抽象能力实在有限,语法又十分地啰嗦,写一行代码要吟唱半天;再加上其业务逻辑本身就比较复杂,文件动辄近万行,我估计除了作者本人,这玩意已经没人维护得动了,因此我果断放弃,开始寻找其他类似项目。在寻找的过程中,我发现了一个用 Rust 语言写的运行在 Windows 系统上的原型,其作者还详细地描述了其制作过程,于是我就又开始看起了它的 Rust 代码。
其实在几年前 Rust 发布 2018 Edition 的时候,Rust 有过一波小热潮,那时候我简单地了解过这门语言,不过没有继续深入下去。这个世界上每 30 天就会诞生一门新的编程语言,没什么好稀奇的;并且语言只是一门工具,是工具就要讲究其实用性,如果学会了一门手艺却无用武之地,那这项技能只会慢慢被遗忘。我曾花了很大的力气去学习 Haskell,但是现在我可能连一个 Hello World 也写不出来。
不过,在再一次接触 Rust 之后,我的看法有稍许改变。Rust 是一门充分吸收了现代语言优秀特性,同时提供了高阶的零开销抽象能力以及面向操作系统底层的接口,注重实用、性能,安全和编程体验的语言。它值得每一个已经拥有一定编程经验的工程师去深入学习。
对我来说,一门语言最重要的就是实用性。如果一门工具没有了实用性,那就失去了掌握它的意义。编程语言的使用者,绝大部分都是为了解决实际问题的。我们不是理论研究者,语言的完备性、设计美感甚至一致性都不是我们关注的重点。Haskell 的纯函数式,Ruby 的一切皆对象,在我看来都是非常不实用的特性;这些纯粹的东西,除了理论上的美感,带给我们的还有什么?与之相反,Java、PHP 和 Go 就是非常务实的语言,因此它们在工程师群体内大受欢迎并且广为流行。Rust 也是一门蕴含这种设计哲学的语言。Rust 语言的 Unsafe 经常被人吐槽,很多人嘲讽说「Rust 不是宣称安全性吗,那还提供 Unsafe 干什么?」实际上,现实世界本就是 Unsafe 的,一只蝴蝶扇动翅膀都可能引发一场飓风,一只臭虫都可能引发电路板故障;与 Unsafe 世界打交道使用 Unsafe 是很自然的事。有人会说,Haskell 就没有 Unsafe 呀?不,我不是 PLT 理论家,我不需要学习「自函子范畴上的幺半群」,给我一个 unsafe,我能干翻整个世界[滑稽]
在很多场景下,性能并不是至关重要的因素;“过早优化是万恶之源”。但在某些场合,性能问题是 0 和 1 的问题。我们经常听到人们对 Java 和 C++ 性能的比较,很多人有一种错觉,在 Java 强大的 JIT 加持下,不说超越 C++,接近应该是没问题的;但很多场景并非如此。Android 平台上也使用 Java 系语言,其运行在专为移动设备设计的虚拟机 ART 上,在这种性能受限的场合,原生语言和托管语言之间有着不可逾越的鸿沟。太极 App 的核心现在是用 C++ 构建的(它曾经是 Java),在我从 Java 切换到 C++ 之后,其核心路径的性能最高提升了 10 倍有余!另外,epic 的早期实现使用 Java 提供的 dexmaker,切换到 C++ 之后,dex生成速度提升了将近 10 倍。这是什么概念呢?原本拦截一个函数需要 80 ~ 100ms,大型模块拦截几百个函数,你打开 App 就要黑几秒,现在只需要 6 ~ 15ms,同样的模块就只需要几百 ms,肉眼根本感知不到;这也是太极速度飞快的秘密。
Rust 也是一门注重性能的语言,咱们使用的很多命令行工具就是用 Rust 写的,它们都有超越同类工具的卓越性能,这是它性能最好的佐证;比如 fd, ripgrep, starship 等等。说到 startship,我曾经使用了 spaceship,我去那个龟速简直跟树懒一样;后来切换成了 powerlevel10k 体验好了很多,直到我切换到 starship,我的妈呀那叫一个 balzing fast!用起来简直就是一种享受。虽然我没有探究过 starship 快的原因,也可能是与语言无关的,但是我们可以知道,Rust 在性能方面,有着极高的上限,它完全可以胜任你对性能敏感的场合。
右值和移动语义是 C++11 中最为重要的特性之一,可以说它深刻地改变了整个 C++。Rust 语言没有历史包袱,它默认就是移动语义,不需要你去考虑右值引用、引用折叠,完美转发这种问题,使用起来非常自然。
最近我接触到一个程序,它从一个 socket 读数据,然后解析处理,最后通过 websocket 异步发送到远端。程序是用 Java 写的,它遇到了频繁 GC 导致 stop the world 的性能问题。其原因实际上很简单,读取数据的时候要频繁分配数组导致 GC 繁忙;解决的办法也很简单,那就是建立一个对象池,这个数组在读到 socket 数据的时候分配,在 websocket 异步发送完毕之后就可以重复利用了。然而在没有这种默认语义的语言中,这个方案可能没有那么容易实施;比如说 websocket 你用的是第三方库,它异步处理的时候并没有给你回调,你无法知道数组何时应该 recycle。如果在 Rust 中,处理这种问题就非常容易。
大多数使用高级语言的程序员可能没有意识到,我们写代码本质上就是在与 CPU、内存和外设打交道。我们使用的很多语言并不直接提供对这些资源的访问方式,但是它并不意味着我们应该忽略这些资源。我们学习的任何一门技术,从上层到底层,自顶向下到最后,你一定会接触到 CPU、内存等底层资源。
很久以前我也是一个页面仔,觉得指针简直反人类,GC 就是 yyds,我曾无比期待 Kotlin 1.0 的发布,觉得它就是未来。然而,随着时间的推移,我发现解决很多问题不与底层打交道根本不够;我们使用的上层业务系统,各种框架和库,实际上是另一部份人对底层的封装;而我们对这些框架的使用,不过是遵循某些人制定的游戏规则;我们自己不能成为规则的制定者吗?你们所看到的太极,它需要通过汇编控制寄存器,需要精准地控制 GC 和 JIT,还需要小心翼翼地处理各种内存布局;而我们即将实现的内存修改器,它需要直接操作内核控制内存物理页面,这些东西哪一个不需要关心底层?
Rust 的所有权、生命周期对一些人可能比较新奇,但是,它本质上就是提供了一种对内存的控制方式,仅此而已。而这种访问方式,给了我们一种内存安全的可能,它需要我们编写程序的时候付出更多,但是这种 trade off 是有价值的。
其实 Rust 还有很多东西值得一提,但是我这并不打算做一个 Rust 的全面介绍,感兴趣的话可以 Google 查阅相关资料7,8,9,10,11,12。
如果你是一个有一定经验的程序员,已经能对若干门语言熟练使用,那么我强烈建议你去学习一下 Rust,它的很多优秀特性博采众长,再不济你也可以从这里了解到很多其他语言的精华。当然,如果你是一门新手,那么 Python 和 Java 可能更适合你。
传闻中 Rust 很难学,但我认为并非如此;我曾花费过很长的时间学习 Haskell,你甚至需要翻阅各种论文才能理解其设计,Rust 与其相比真的不算什么,顶多称得上是有门槛;然而,这个世界上没有门槛的事情大多没有核心竞争力。
另外,本文可能提及了一些其他的语言并且赞扬了一波 Rust,但是并没有 diss 和吹捧的意思;如果你是一个语言技术 fans,那么几乎毫无疑问你会成为一个糟糕的决策者;《失败的逻辑》忠告我们,不要为了工具而工具,忘记了工具的目的是什么。
最后,欢迎你成为 Rustaceans 的一员!