第二天,他跑了全套测试。
把新旧两套流程吐出来的几十gb结果进行比对。
结果全部一致。
再看最终成绩。
旧流程跑完要五个多小时。
换上他这个专门给五个数排序的小东西之后,省下二十一分钟。
二十一分钟。
放进一个五六小时的大任务里,这数字小得可怜,像从一桶水里舀走一勺。
换个外行看,会觉得他熬了一整夜,就为这点东西,简直是捡了芝麻丟了西瓜。
但江临不这样认为。
因为这套工具不会只跑一次。
这个项目后面还有几十组要重跑,这套工具以后还要接別的活儿。
今天省二十一分钟,明天省二十一分钟,跑上一百次,省下的就是三十多个小时的高强度算力。
这意味著,他能用更少的机器,跑出更快的叠代速度。
更何况,这只是他揪出来的第一颗小石头。
一个年久失修、堆满了懒惰和冗余的老系统里,这样的小石头,一定还有成百上千颗。
他在记录的末尾这样总结。
一件事被重复的次数足够多,最微小的效率差距,就会撕裂成生与死的鸿沟。
在这个城市,代码写错了,大不了多扣几块钱伺服器费用,大不了半夜爬起来重启,大不了回滚重来。
有售后,有同行,有无数冗余可以挥霍。
但在那个他迟早要回去的废土世界里,没有云伺服器,没有售后,没有第二个工程师替他擦屁股。
在那里,机器慢了,不能先要资源,必须先问。
为什么慢。
底层快一点,省一点,从来不是写进简歷的性能指標。
而是活下去的余量。
写下这句话,江临把这个给五个数排序的小东西,单独归到一个新建的文件夹里。
然后,一个新的念头浮了上来。
五个数,他靠纸笔和经验,一晚上能推出来。
可如果是六个数、七个数、八个数呢?
要排序的数字每多一个,可能的排法就成倍地,再成倍地往上翻,很快就会多到一张纸根本画不下。
而且,比较次数最少不等於实际跑得最快。
还要看机器喜不喜欢这套顺序,看那些岔路藏在哪里。
到那时候,凭一个人的脑子去硬想哪一套手势最好,会越来越不靠谱。
不是不能写。
只是人会变成那个最慢的瓶颈。
江临忽然想到一件事。
如果给五个数排序的完美手势,是可以靠一台机器,把所有可能的排法挨个试一遍,自己找出来的。
那別的小动作呢?
那些同样被重复几千万次,同样卡著整个系统脖子的小动作呢?
人来定规矩,定好什么算对,什么算错,什么是绝对不能碰的红线。
然后,让程序自己去试,去试错,去在那片大得可怕的可能性里,替自己捞出那一段更小,更快,更稳定的代码。
不再是人埋头一行行去写。
是写一个程序,让这个程序,去寻找別的程序。
好吧,让程序去找程序,听著挺玄,到底有什么用?
打个最简单的比方。
你家里有一把瑞士军刀,剪刀、开瓶器、螺丝刀、锯子,什么都有,出门带一把,啥都能应付一下。
可如果你开了家工厂,流水线上每天只干一件事。
拧同一种螺丝,一天拧几百万颗,你还会用军刀上那个小螺丝刀去拧吗?
当然不会。
你会专门定做一把电动螺丝枪,只为这一种螺丝量身打造,又快又稳。
电脑也是一样。
它平时用的那些现成功能,全是瑞士军刀。
为了应付天下所有情况,做得万能,可靠,可每用一次都得把整套复杂东西开动一遍。
但现实里很多任务,其实就是那条每天拧几百万颗同款螺丝的流水线。
动作简单,规矩固定,就是次数多得嚇人。
江临前一晚乾的,本质就是给这样一条流水线,亲手定做了一把专用的螺丝枪。
而让程序去找程序,是把这件事再往前推了一步。
定做螺丝枪本身也是累活儿,得懂材料,懂力道,反覆试。
他不想每遇到一种新螺丝,就熬夜亲手去打一把新枪。
他想要的,是一台会自己造螺丝枪的机器。
你只管告诉它我要拧这种螺丝,必须拧紧,不能滑丝,剩下的,它自己去试一千种枪头,自己测哪种最快最稳,最后把那把最好的递给你。
人只负责说清楚我要什么,什么绝对不能出错。
造枪的活儿,交给机器。
那这东西在生活里能干嘛?
答案是,几乎所有动作不难,却要重复亿万次的地方,背后都藏著这样一条可以被改造的流水线。
你滑手机相册,几千张照片唰一下按时间排好,是排序。
你网购时商品按价格从低到高一列,是排序。
地图给你算最近的路,外卖给你派最近的骑手,游戏里每一帧成百上千个物体重新排先后……
这些你天天在用,却从不留意的瞬间,底层全是同一批被重复了千万上亿次的小动作。
平时没人在乎它们快一点还是慢一点,因为单看一次,都是眨眼的事。
可一旦放到重复上亿次的尺度上,这眨眼的差距,就会变成实打实的电费、伺服器和等待时间。
江临要做的机器,就是钻进这些没人留意的角落,把那些还能更快的小动作,一个个揪出来,定做,替换,提速。
而且它不挑活儿。
今天能给五个数排序,明天就能优化別的小动作。
这个项目用得上,下个项目照样用得上。
一念及此,江临越来越兴奋。
因为排序,只是个敲门砖。
真正让他头皮发麻的,是这道门缝背后那个更大的东西。
如果连找最优程序这件事本身,都能交给机器去做呢?
那么在废土世界里,他就不必再为每一个装置,每一种控制顺序,每一套观测流程,都亲手去写最好的方案。
他一个人,精力有限。
但如果有一天,机器能替他搜出一部分小程序,小流程,小动作。
那么它就不再只是一个排序工具,而是一个单人科研系统的增幅器。
过去,人写程序,是亲手给出每一步。
第一步比这两个数,第二步比那两个,第三步交换……
一步步把答案铺出来。
这要求写的人脑子里先有答案,再把答案翻译成代码。
江临现在想做的,是反过来。
他不再亲手给答案,只告诉机器三件事。
什么是起点——一堆乱掉的数。
允许做什么动作——挑两个位置,比一下,错了就换。
什么算贏——所有可能的乱序,最后都被捋顺。
然后,让机器自己去试。
说白了,就是把排序这件事,变成了一个游戏。
乱掉的数,是棋盘。
每一次比一下,该换就换,是落子。
把所有乱序都捋顺,是通关。
机器要做的,就是自己去玩这个游戏,一步步试,直到找出一套能通关的走法。
这个想法有个让人头皮发麻的地方。
人,从此不必知道答案了。
人只负责定义这个游戏。
画好棋盘,定好规则,標好胜利的样子。
至於具体怎么走,走哪条路最快,交给机器去穷举,去试错。
江临新建了一个文件,敲下三行注释,作为这个游戏的全部规则。
有一堆乱掉的数。
每一步,挑两个位置比一下,错了就换。
直到所有数都被捋顺。
很短。
短到不像一个程序的开始。
但他盯著这三行看了將近一分钟。
因为这三行的意思,已经不再是手写排序,而是让机器自己搜索排序程序。
第一个要解决的问题是,机器怎么知道自己试对了?
这恰恰是这条路最乾净的地方。
五个数,所有可能的乱序,一共只有一百二十种。
一百二十种,小到可以一种不漏地全列出来。
所以机器根本不需要猜某套走法对不对。
它可以把这一百二十种乱序全部丟进去,让那套走法挨个跑一遍,只要有一种没被捋顺,这套走法就当场判死,扔掉。
这是数学意义上的乾净。
不是我觉得它应该对,而是我把所有可能都试过了,它就是对的。
人靠直觉,会漏,会错。
机器靠穷举,一种都不会放过。
凌晨,第一版找程序的程序跑了起来。
它很笨,几乎不会偷懒,只是从所有该比哪两个的选择里,一层一层往下试。
每走一步,就回头看看那一百二十种乱序,还剩多少没被捋顺。
哪条路让混乱减少得最多,就顺著那条往下走。
江临给它定了个朴素的评判標准。
剩多少没排对,用了几次比较,有没有引入岔路,有没有破坏两个数相等时保持原顺序的规矩,有没有把缺失的数据提前隔离出去。
没有什么玄乎的智能模型。
只有最原始的三样东西:挨个试,及时砍掉死路,把所有可能都验一遍。
这是他这台机器现在跑得动的全部。
第一轮结果出来,屏幕上列印出一串走法。
江临先验证。
一百二十种乱序,全过。
两个数相等的情况,过。
缺失数据的隔离,过。
然后才看速度。
没比他自己手写的快多少,某个设定下甚至更慢。
他反而长长鬆了口气。
这才正常。
第一版找程序的程序,怎么可能一出来就碾压一个人熬了一整夜的经验。
但它已经证明了那件最重要的事。
机器,可以自己找到正確的程序。
快不快,是下一层的问题。
能不能找到,才是这扇门开没开。
接下来的时间里,江临一点点餵养这个还很笨的东西。
第一个台阶,是让它分清对不对。
这个最简单,穷举就行。
五个数的一百二十种,八个数的几万种,机器都能一种不漏地验完。
第二个台阶,是让它分清值不值。
同样能把数排对,可能有十几套不同的走法,它们不是一样好的。
有的比较次数多,有的岔路多,有的要占用更多临时空间。
江临得把这些代价一项项写进机器的评判標准里,让它在一堆都对的走法里,挑出最划算的那个。
第三个台阶,最麻烦。
让它明白机器认不认。
同样一套走法,写在纸上看著差不多,可一旦真的跑起来,换一台电脑,换一种翻译方式,快慢竟然会不一样。
因为最终真正执行的,不是纸上那几行字,而是机器把它翻译成的更底层的一长串指令。
一套看起来比较次数最少的走法,翻译到机器里,未必就是跑得最快的那个。
到了这一层,纸笔和直觉彻底失效。
人根本算不清,哪一套走法在真实的机器里会更快。
而这,恰恰是让程序去找程序最大的价值所在。
人算不清的事,机器可以一套一套地真跑,用秒表去量。
它不需要理解为什么快,它只需要把成千上万套候选走法挨个跑一遍,然后告诉你。
这一套,最快。
但江临也很快撞上了这条路的天敌。
爆炸。
数字稍微多一点,可能的走法就成倍再成倍地往上翻。
五个数还能全试,到了更大的规模,哪怕机器一刻不停地试到宇宙尽头,也试不完所有的路。
这时候,砍就比试更重要了。
江临想起之前解过的一道竞赛题。
那道题的关键,不是把所有可能的答案都算一遍,而是先证明哪些答案根本不可能是最好的,然后整批整批地划掉,只在剩下的小范围里找。
搜索也是一样。
机器不可能走完所有的路,但它可以先证明,一大批路,从某一步开始就註定不可能更好了。
於是这些路,连试都不必试,整批扔掉。
把这件事干好,需要的不是更快的机器,而是更聪明的放弃。
在每一个岔路口,提前算出往这边走,最好也只能到这个程度,一旦发现这个上限还不如手里已经有的答案,立刻掉头。
省下的,是天文数字般的无用功。
某个深夜,第一个真正有分量的结果,出现了。
带著脏数据规矩的的五个数排序。
允许有相等的数,允许有缺失的空洞,还要保证排完之后,相等的数维持原来的先后。
机器找出来的那套走法,和江临亲手写的,不完全一样。
比较次数一样多。
但岔路更少,结构更规整。
他把所有的脏情况一种种餵进去验。
全过。
再上秒表。
比手写版快了百分之二点几。
换一台机器,换一种翻译方式,领先的幅度变小了,但没有消失。
百分之二点几。
听起来甚至有点可怜,没有谁会为这个数字鼓掌。
事实上,最后留在硬碟里的,也只是一个不起眼的小文件,旁边附著一份自动生成的验证报告。
所有乱序通过,相等值通过,缺失值通过,与標准做法零差异,速度中位数快百分之二点七。
就这么点东西。
但江临心里清楚它和昨天那个手写版的本质区別。
这一次,那套漂亮的走法,不是人写的。
是机器自己,从茫茫的可能性里,捞出来的。
標准库里的通用流程,被无数工程师打磨过很多年。
而现在,它在一个特定的场景里,被比了下去。
哪怕只是一点点,哪怕没人鼓掌。
动手的,也不再是某个天才的脑子,而是一台肯把所有可能都试一遍的机器。
他没有为这百分之二点几高兴多久,反而很快皱起眉。
太小了。
不是嫌弃这个结果,而是这个结果在告诉他一件事。
方向是对的,方法还很粗。他给机器定的游戏规则太简单,让它放弃的本事太弱,能让它玩的盘面太小。
机器现在像个刚学会规则,只会埋头硬试的新手。
它能贏,但贏得笨拙。
而江临已经看到了这条路真正的尽头在哪里。
现在的机器,是按死规矩去试。
但如果有一天,它能从自己过去试过的成千上万套走法里,回头去学。
哪一类结构容易快,哪一类机器特別喜欢,哪一类看著比较次数少,跑起来却慢,那它就不再是埋头硬试的新手了。
它会开始有直觉。
人定义游戏,机器去玩,再从玩过的每一局里,长出自己的偏好。
到那时候,让程序去找程序这件事,才算真正长出了牙齿。
但江临没有立刻去碰那一步。
现在还太早。
他手里只有一个笨拙的找程序的程序,一个快了百分之二的小文件,几份测试报告。
这不是成果,只是门缝。
可很多最终会改变一切的东西,一开始,都弱得可笑。