这个宇宙的目的——智能的进化

在大刘的短片小说《朝闻道》的结尾,霍金坐着轮椅登上真理祭坛,向全知的高级文明——宇宙排险者提问:“宇宙的目的是什么?”

就在刚刚,排险者向全世界顶尖的科学家解答了困惑他们的终极问题。但因为不允许高级智能向低级智能直接传授知识,这些恍悟后的科学家自愿选择毁灭。如果霍金得到了排险者的回答,那么他也将带着恍悟的快感,化作一团火球而消失,正所谓“朝闻道,夕死可矣”。然而,面对这个问题,全知的排险者坦然承认——他也不知道。

那么宇宙的目的是什么?让我大胆设想,是持续不断的进化更加高级的智能。

让我们看看,到今天为止,我们的宇宙为了“更高级的智能”都做了什么吧!

我们的宇宙通过恰到好处的物理定律,让大爆炸初期混沌的能量最终凝结成我们熟悉的星系:不断发光发热的恒星,运行在各种轨道上的行星。这些奠定了目前最靠谱的智能形态所需的物质基础。在混沌初开的时候,单单是一缕气体,都要比虚无缥缈的能量或者基本粒子,要更加具有“智能”。物质,以及它们之间的物理、化学定律,是智能的第0层。

35亿年前,我们的宇宙通过某种不可知的神秘力量,在地球上播下了一组化学物质的编码——DNA,由此产生了第一个有生命的细胞。这就奠定了目前最靠谱的智能的基础结构——碳基生命。这是智能进化的一大步,这一个细胞的智能程度,远超所有的无机物。DNA是智能的第1层。

此后,单个细胞不断进化。还记得初中生物课观察过的单细胞草履虫吗?它可以通过鞭毛在水里自由地游动,这相比地球上的第一个细胞,已经进步了很多。但最终,单细胞无法满足智能对复杂度的需求,于是细胞开始堆积、分化成组织和器官,多细胞生物出现了。在一个多细胞生物中,每一个单独细胞都生死无常,但它们组成的整体却具有更长久的持续生命力,这比单个细胞“智能”多了。多细胞的协同是智能的第2层。

细胞的分化逐渐被玩出了花,出现了一种神奇的细胞——神经细胞。有了它,可以通过电信号在细胞之间快速协同信息。基于神经细胞的功能,动物产生了。神经是这个宇宙的一个了不起的发明。在此之前,DNA是生物唯一保存信息的地方。有了神经,生物具备了新的信息系统——神经系统。虽然神经系统中的记忆无法跨代遗传,但至少在每个个体的一辈子中,它可以不断学习、提升自己。这相比于没有神经的植物而言,简直太“智能”了!神经系统,是智能的第3层。

此后,随着各种动物神经系统的不断复杂,智能不断提升,终于,质变到来了。人类发明了通用的语言。这种通用语言,不仅可以表达听过、见过、遇到过的事情,还可以表示没遇到过的想象、愿景、故事。语言让人的个体之间可以传递神经系统中的概念,实现了神经系统中数据的跨代传递。经验和知识终于可以不依赖于DNA的进化而积累了。通用语言,成为了继DNA、神经系统后的又一个信息系统。借助语言,人类无需生理进化就可以提升认知。通用语言是“智能”的第4层。

通用语言可以表达人类的想象、愿景、故事,这让多个独立的人类个体,可以为了统一的愿景或故事,分工协作。就像智能的一开始,多个细胞学会了分化成不同器官一样,多个人类个体,也分化成了社会的不同器官。如《人类简史》中所述,“宗教”、“国家”、“金钱”等虚拟概念的发明,让数十亿人类群体得以向共同的目标努力,这是其它动物群体无法企及的协作规模。社会分工协作是“智能”的第5层。

在《人类简史》的最后一章,作者认为,人类不久就会具备改造自身的DNA的能力,从而可以加强自身的能力,甚至创造新的物种。这样的人类就变成了神。

然而我认为,这也太拿自己当回事了,也太小瞧“神”了!

相比混沌的能量和基本粒子,第一缕气体能让粒子有序的组织,那时候它是神。相比无机物,第一个细胞能逆热力学定律生产减熵的物质,那时候它是神。相比单细胞生物,第一个多细胞生物可以控制体内细胞的新陈代谢,那时候它是神。相比植物,第一只动物可以通过神经电信号控制自己的体态和运动,那时候它是神……

然而,我们现在看来,它们都不是神。它们只是智能进化路上的里程碑。那么凭什么能编辑DNA、创造新物种的人类就是神呢?当然不是。这不过是“智能”发展的一个新层次(第6层)。这样的质变已经发生过了很多次,每一次都在我们的这个宇宙的掌控之内。

让我们大胆地帮这个宇宙预测一下未来的发展吧。

人类社会通过娴熟的基因编辑和持续不断的协作和努力,发明出了完美的碳基生物——超人。他们具有完美的道德、体力、智力、协作能力、情感、审美,甚至具有类似USB的数字脑机接口。这是第6层智能的极限,也是碳基生物智能的极限。

社会终于被超人所取代,形成了更高效协作的超社会。超社会中的超人们接过宇宙的任务,继续探索智能的进化。既然碳基已经没得发展了,那么只能尝试别的途径。于是他们研制出了“宇宙模拟器”,用来并行模拟完全不同的物理参数下可能产生的不同的宇宙,看看这些宇宙是否能诞生出不一样的超级智能。让这些模拟器继续探索智能的进化,这就达到了智能的第7层。

在这些并行的模拟实验中,有一个刚刚模拟了一次奇点的大爆炸。混沌的能量和基本粒子逐渐冷却组成了一缕缕气体,然后是恒星、行星、星系。模拟器软件自动识别了气候最适当的一颗星球,并根据此星球特点、以及本模拟环境的物理定律,自动生成了生命的编码,投放到星球上。漫长的几十亿年后,这颗星球进化出了高级的智能生物。

这几十亿年,在超人的模拟器里,只用了几天就模拟完了。

“看,这个宇宙里的智能进化的很靠谱啊!” 一个超人博士生对另一个年长的超人说。

“好,把其它模拟先暂停,所有计算资源投入这个宇宙的模拟,争取赶上今年的‘宇宙模拟智能论坛’的投稿期限!”

通俗理解“量子纠缠”

就在前两天,科学家用图像展示了“量子纠缠”。但这个新闻并没有首张黑洞照片那么火,可能是因为量子纠缠的概念距离大众太远了。如果说黑洞是爱因斯坦预言正确的事情,那么量子纠缠就是连爱因斯坦都不相信甚至搞错了的事情。没关系,虽然搞不懂深入的原理,了解一些皮毛还是可以的,至少可以不被“量子骗子”忽悠。

首先要说啥是“量子”

量子是个形容词,“量子某某某”,指的就是对于非常非常小的物体的某某某物理现象的研究。当研究的对象小到原子大小,甚至更小如电子时,适用于它们的物理规律就完全不同了。

比如对于宏观的物体——砖头,你一搬它就起来,放在哪就是哪。但是如果考虑组成砖头的那些个原子,它们可不是像小号的砖头那样堆在一起的。你甚至根本说不清楚它们在哪。它们所遵循的物理规律和宏观的砖头完全不同。这大概就是为啥蚁人兄弟把自己缩小到量子领域后就能大显神通了。

什么是“量子纠缠”?

“量子纠缠”指的是两个以上的“小东西”之间存在的密不可分的联系。这样说很玄,我们用宏观事物举例子:

买一双鞋,然后把两只分别包起来,随机快递给两个朋友。在两个朋友打开包裹之前,两个人都不知道自己收到的是左脚还是右脚。直到有一个人拆开包裹,发现自己收到了左脚,那么他就可以立刻断定,另一个人收到的是右脚。并且,无论两个朋友距离多远,就算一个在地球上,一个带着包裹登月去了,那么这个实验仍然会成功。

如果把两只鞋子换成两个纠缠在一起的粒子,那么这就是一个量子纠缠的演示实验。怎样?并没有很稀奇。

强调一点,有一些文章说,量子纠缠状态的两个粒子,无论距离多远,如果改变其中一个的状态,另外一个都会瞬间改变。这是个绝对错误的论述!就像两只鞋子,打开包裹后,就算有一个朋友对收到的左脚不满意,他也不能把它变成右脚。就算他真的找裁缝把鞋子改了,也不会影响到另外那个朋友手里的那只右脚。对于微观粒子来讲,当你去做实验观察一边的粒子状态时,其实就已经打破了纠缠,两个粒子此后就没了关系,对一边的粒子做什么都不会影响另一边。

既然鞋子也能纠缠,那量子纠缠有什么神奇之处?

假想我们把实验中的鞋子换成两粒骰子,分别寄给两个朋友,并让他们记下打开包裹时看到骰子的点数。不用想,两人看到的点数没有任何联系。为什么两只鞋子一定是一左一右,而两颗骰子点数会毫无关系?

这是因为,对于鞋子,虽然在打开包裹之前我们不知道答案,但是可以确定,装着左脚的那个包裹,从你把两只鞋分开打包的那一刻起,自始至终装的就是左脚,再也没有改变过,哪怕没有人知道。物理学家管这种没人知道,或者无法测量,但实实在在确定的事实,叫做隐变量。所以说,鞋子总是一左一右,是因为每只鞋都存在对于两个朋友而言无法提前观测的隐变量。

两颗骰子则不同。根据经验,盒子里的骰子是完全随机的,打开盒子之前就不存在确定的点数。因此,你的经验告诉你,两个朋友打开盒子时,看到的点数肯定彼此无关。

然而,如果这个实验做了10次,发现两个朋友报告的点数总是相同的,那么你会如何解释?

可能性1:骰子不均匀,比如两颗骰子都有同一面很重,这样对面总是朝上。这就是说,每个骰子仍然有隐变量——不均匀的重心,只是更加隐蔽。但如果对于微观粒子而言,量子力学的理论基础认为并没有什么隐变量。相信隐变量,是动摇量子力学根基的事情。

可能性2:骰子中有智能通讯芯片,一旦一个盒子被打开、被看到,它瞬间会通过先进的5G网络联系另外一颗骰子,快速旋转到同一面。事实上,5G的速度都不够快。两个朋友距离之远、打开盒子的时间差之近,可以达到即使是光速都无法把一个盒子打开的信息传递到另一个去。

这就是爱因斯坦遇到的难题。以当时的技术,虽然没有做实验,但他用两个纠缠的粒子的量子力学表达式,当作这两个骰子,通过精妙的数学推理,证明了观测结果总是相关联的。对于两种解释,爱因斯坦认为可能性2简直就是扯淡,亏你们想得出这么荒谬的可能性,还超光速哩。那么解释只能是可能性1,所以量子力学的理论基础就不成立,量子力学“不完备”。这是个典型的反证法。

然而,之后越来越多的理论和实验逐渐将真理锁定在了可能性2上。这里根本就没有隐变量。的确存在一种神奇的力量,在打开一个盒子的一瞬间,协调了两个骰子的状态。并且这种协调的机制是瞬间的、超过光速的,不是5G,胜似5G。(注意,瞬间的“协调”,并不存在超光速的信息传递。事实上,两个朋友也只有通过低于光速的方式,比如打个电话,才会惊奇地发现他们看到的骰子是相同的点数。)

这彻底改变了物理学家们的世界观。

原本,大家认为,任何事件的发生,其影响就像水波纹一样向周围的空间扩散。扩散的速度不超过光速。比如说,特朗普发了一条脑残推特,一小时后大家奔走相告,一天内有些国家因此发表了声明,第二天国际空间站上不同国家的宇航员之间的关系发生了微妙的变化,等等等。但如果说这一条推特的“杀伤力”要影响到三体星人决定进攻地球,那就至少要4年后了。因为这条消息以光速传递到三体星,至少要4年时间。

这种特性被称为局部性、或可分性(Locality)。也就是说,对任何一个事情的发生,在特定的时间范围内,其影响的空间范围也是有边界的、是局部的。每一时刻,世界总可以被分成“潜在受到影响的区域”和“必然还未受影响的区域”。

量子纠缠证实了,人们一直所相信的局部性,其实根本就不存在。任何事件的发生,都可能受到了银河系另一端的某个其它事件的影响,也可能进而直接影响了另一个星系中的某个事件。这种影响,并不是“因果联系”,而是微妙的“瞬间协调”。

宇宙是一个不可分割的整体。

Libra、Move和Rust

脸书主导的Libra区块链是用Rust写的。而Libra中所推出的Move语言和Move虚拟机,主打概念是对于资源归属权的管理和控制,这又不免和Rust产生了千丝万缕的联系。究竟Libra团队是因为Move而选择了Rust,还是因为Rust产生灵感而设计出了Move,我们不得而知。我们不妨先看看Rust。

Rust是一个编译型语言,其最核心的特点就是编译型,且没有GC。所以,我们先……

插播什么是GC

垃圾回收(Garbage Collection,简称GC)是一个头疼的问题。程序的多个模块之间互相调用、传输或共享数据时,为了高效,应尽可能避免复制内存中的数据,而是传递一个内存指针(引用)过去。这样一来,系统就必须搞清楚每一个资源(最主要是内存空间)还有多少个程序模块可能访问。仅当没有人再要它的时候,它就成了垃圾(可回收的),被GC回收。因此,GC的典型工作原理就是:一方面要有对资源的引用计数,另一方面定期清理引用计数降低为0的资源。

C/C++语言没有GC。因此程序员必须自己管理资源的申请和释放,就是malloc/free,以及new/delete。但这很容易出错:如果你提前free/delete了一块内存,就很容易造成缓冲区溢出漏洞;如果你忘记free/delete了内存,就会造成内存泄漏,程序占用内存越跑越大;如果同一块内存你free了两次,程序直接就崩溃了。所有脚本语言,以及Java、Go,都有自带的GC,这使得程序员不需要手动管理内存资源的释放。

GC有什么弊端呢?那就是GC的工作必须对程序本身是完全无感、透明的。对于多线程程序,这意味着必须暂停所有线程的工作,而让GC专心运行。因此,一些专家认为,GC会让一个程序丧失实时性,执行时间变的无上限,效率不可估计。但真实情况是,正常情况下GC的开销完全可以忽略,况且咱们用的本来都不是实时的操作系统,谈什么实时性?而非正常情况下,GC开销异常时,恰恰是需要有经验的程序员解决的问题。

我认为让程序员把时间花在异常情况下的优化,比花在未见异常的时候精心管理内存,要更合理。毕竟优化的第一定律是:先不要优化。

另辟蹊径的Rust

Rust采用了一种全新的机制来避免GC。它也需要程序员在开发时主动配合来管理内存。

具体来讲,Rust认为内存资源和程序中的对象实例(变量)是紧密绑定的。当你创建一个对象时,就也分配了它的资源;一个对象走出作用范围时,它的资源就会被释放。资源不能复制,但可以在对象之间“借”或者“送”。

程序员要做的是,明确指名资源的“借”和“送”的操作。资源借出去了,你自己就没有了,但等对方消失了,资源就自然还回来了;资源送出去了,你自己也没有了,而且再也要不回来了。这样一来,只要那些拥有资源的对象走出了作用范围,被销毁了,就可以安全地把它的资源一并回收。

既然这一切资源的借和送,是在程序中明确指定的,那么Rust编译器就可以在编译时明确什么时候分配、什么时候释放资源。

原理上,Rust是通过语法方便了原本C/C++程序员需要记在小本子上的“资源属于谁”的问题。所以,如果你玩不转C/C++,分不清楚栈和堆,就很难理解Rust的资源借和送。

Rust和Move

看到这里,你有没有觉得Rust对于内存资源的理解,恰恰就是Move所宣称的对于链上资源的理解——资源不能被复制,只能在账户或者Move对象之间流动。

目前Move还处于早期开发阶段。Move IR的语法本身就大量借用了Rust的语法。可以肯定地说,Move的设计思想本身也很大程度上受到了Rust的启发。

然而,毕竟Move是个新语言,借鉴思想并不意味着必须用Rust实现呀。可能脸书选择Rust只是因为Go是谷歌在维护的缘故。但比既然要做Libra这么大的颠覆性的事情,没有这点胸怀,怎么可能成功?难道以后你脸书就不邀请谷歌加入Libra联盟吗?

Rust vs Go,槽点满满

对一个语言的运用,我觉得有这几个层次:

  1. 能读懂
  2. 能调试
  3. 能修改或写功能模块
  4. 能设计新项目架构
  5. 能设计新架构以重构旧项目

首先承认,我对Rust的运用直到现在还停留在1-2之间的程度,而起初仅仅为了看Libra代码才开始学。如果下面喷的不对,欢迎留言指出。

对于Go,我曾经用3个小时的时间认真完成了Tour(https://tour.golang.org/),之后就达到了2-3之间的程度,看懂state-of-the-art工程不成问题。我天真的以为,学Rust也可以花几小时看个Tutorial,加上对C语言malloc/free炉火纯青的运用,应该不难。事实上Rust要达到2以上的水平,绝非几个小时够用的——语言特性太多了。

计算机软件架构的最高水平,就是设计一门简洁而恰到好处的语言。Go所说的只有50页的语言spec,真的不是吹的,其本身的简洁和表达能力的丰富,的确堪称神来之笔。

接下来,我克隆了libra代码库,然后天真的跑了cargo build。足足半个多小时,我都吃了一顿饭回来了,才编译完成。我记得编译一遍内核也就差不多这么久了。此后,哪怕是在代码中加一个print!,重新编译大约都需要1分钟时间,这还取决于修改代码的模块是不是在依赖关系的上层。

最可怕的还不是编译时间,而是一共500多个组件,每个组件编译后的目标文件大概都要几十M,加载一起竟然一共有18G!一个工程18G!大家感受一下。要是同时开发几个Rust项目,电脑硬盘立马告急。要说Rust维护者里面有固态硬盘厂的股份,我绝对相信。

而最终生成的可执行程序,就拿客户端程序client举例,竟然有390M!390M,在20年前就是一个完整的光盘游戏。

没有对比就没有伤害。我的go-libra客户端,核心功能和rust libra客户端没有欠缺,在清除全部缓存的情况下,go build编译用时10秒,最终的可执行程序大小是13M。go build比cargo build快了2个数量级,产生的可执行程序体积小了1个半数量级。

就是这样残酷。

Libra第一笔转账——硬核方式

最近两周,由脸书发起的旨在颠覆现有金融格局的数字货币Libra项目,一直占据了链圈的风口。之前,网上已有若干篇关于“发起第一笔Libra转账”的教程,介绍如何下载、编译Libra项目的开源代码,并在testnet上完成一笔交易。身为硬核技术宅,那种跟着教程一步步编译代码的事情,显然不能满足我的好奇心。于是,借用几天休假的时间,奉上这样一份绝对的“硬核方法”,供同样不满足于RTFM的硬核宅们把玩。

有多硬?

我用golang重新写了一个全新的Libra区块链的客户端,完成了所有必要的密码学验证过程,并用它完成了转账交易。

上代码和教程:https://github.com/the729/go-libra

不走寻常路的区块链

要说Libra的不寻常,根源恐怕来源于它的宗旨——建立一套全球数十亿人使用的支付平台。它是由一篮子法币100%抵押支持的稳定币,所以Libra是用来花的,不是用来炒的,正所谓“币花不炒”。

币花不炒,导出了Libra很多设计理念,和比特币、以太坊等截然不同的地方:

中心化:Libra设计上就是中心化的,一开始由二十几家大企业共同掌管,一家一票,三分之二通过。未来逐步拓展到一两百家,但也只有组织内的成员有投票权。号称稳定运行五年后要去中心化,但目前还全然没有设计。

节能环保:既然有中心,就不用“电暖器式”的挖矿过程了,大家直接投票即可。线上的投票就是BFT算法。有重要的事要决策时,线下也可以投票,和线上一样三分之二以上通过。

容量大:既然直接投票,就没有了开会频率的限制,会可以一个接一个开,不像比特币10分钟才能出一个块。每次“开会”,大家表决一筐新的交易,达成一致了,就纷纷把这些交易按顺序写入自己的数据库中,签字画押。所以号称是“区块链”,其实通讯协议和存储上几乎就没有“区块”(装交易的“筐”)的概念了,保存的就是一个个按顺序的交易。

面向服务器的技术架构和优化:既然只有那么几十上百个大公司作为中心,目标又是要支撑全世界几十亿人的交易,那么代码的实现上就可以看出很多面向服务器级别的优化。比如,节点采用微服务架构,分为ac(访问流控)、mempool(交易内存池)、vm(Move语言虚拟机)、execution(交易执行单元)、consensus(共识算法(就是开会的代表))、storage(存储)。这些模块之间都采用RPC调用,可以横向扩展。根据Libra技术文档的理论计算,一台中等的服务器和大一些的硬盘(16TB),就可以支撑近期的使用了。不过它仍然是为服务器设计的,显然不适合在树莓派上跑。

Libra数据结构的设计

这里借用一张Libra区块链技术文档中的图。当我第一次看文档时,实话讲这张图完全看不懂。不过当我完成了go-libra的初步开发后,回头看,发现这就是整个Libra数据结构设计的精髓。

如果我们看Bitcoin,数据是按照一个一个区块形成一条单链保存的,每个区块中保存的一组交易,实际上可以看作是对整个区块链数据库状态的“补丁”(即patch或diff)。这样一来,当你想要知道某个账户的状态时,唯一的办法是从创世区块开始,一个一个“补丁”打,一直到目前最新的区块。因此,你必须在本地保存所有区块的数据。

Libra采用的是一种不同的方式,即每个“交易”(暂且对应于比特币的区块)都可以直接验证所有账户在那一刻的数据。换句话说,你可以直接取出并验证某个交易后,任何一个账户的状态,不需要从头开始捋。

这种方式,其实和git使用的是完全一致的思想。(感兴趣者请移步到《全球最强大的区块链项目,竟然不是比特币?》)Libra中的交易,对应着git中的commit。Libra中的账户,对应着git中的文件。通过Libra的每个交易,可以直接取出并验证任何一个账户在那一历史时刻的状态,就像通过git,你可以取出并验证任何一个commit提交时,代码树中任意一个文件的历史状态。

不同的是,Libra的交易只允许单线增加,而git允许commit分叉与合并。

重度MerkleTree使用

在Libra中,三度用到MerkleTree。

如果你还不了解MerkleTree:它是一种不需要拿到全部数据,就可以验证一小部分数据属于全部数据的方法。

比方说老板给秘书签署了1000份文件(设为n),放在一个大文件袋里,袋子上有老板的签名。这时,你找秘书要一个文件,为了验证真实性,你不得不要来整个文件袋,验证签名,然后再取出其中的一个文件。这是O(n)复杂度的验证过程。

如果使用MerkleTree,那么你可以只要你需要的文件,外加10个密码学哈希值,以及老板的签名,就可以验证这份文件确实是在老板签署的文件袋中了。这是O(log n)复杂度的算法,非常高效。

别问为什么不让老板把所有文件一个个都签了。老板若有时间签1000个字,也有精力把那个不懂MerkleTree而让他签到手抽筋的程序员开掉。

Libra中的三处MerkleTree:

所有历史交易按顺序组成的Ledger History树,即上面图中的(1)。每增加一个新交易,就增加一个叶子,同时增加相应的枝杈,一直延申到根。所以说,这棵树会通过增加叶子节点而不断产生新的根节点的。而根节点的哈希值,就是所有一两百家成员开会确定后签字画押的东西。

有了Ledger History树,你就可以验证某一个交易是否发生过。验证了单个交易后,它会含有另一个稀疏MerkleTree的根节点。而这个稀疏MerkleTree的每一个叶子节点,就是交易发生后每一个账户的数据。由于账户是256位的,最多有2^256个账户,一个稠密的MerkleTree就有2^257个Hash值要存,这显然不现实。因此,这里采用的是稀疏的MerkleTree。

第三个是Event Tree。从目前Libra官方的测试服务看,还没有实现整个Event的机制,因此这个树一直是空的。

总结

  • Libra从根本上和比特币等有很大的理念和技术差异。
  • Libra的数据结构更像是git,类似一个带有历史的内容寻址存储(CAS)。历史是交易,内容是账户数据。
  • Libra的设计者和实现者真的很辛苦,我用了几天时间,实现的也只是冰山一角。而真正的难点:Move编译器、虚拟机、CAS数据库,才是设计和开发难度很高的部分。
  • Libra目前状态真的很初级,只是将就着让testnet上线了。一些基础的功能都还没有,例如我们还无法查询一个交易执行的结果成功与否,只能看币到没到。但Libra的开发也很神速,每周三十多个commit。

碳基生命有没有可能是被设计出来的?

这篇文章是对知乎高赞回答《人类有没有可能是被设计出来的?-无fa可说的回答》的反驳。这里我偷换了标题,把“人类”换成了“碳基生命”。之所以换,是因为原文的前一部分通过科学研究的证据完美阐释了——人类是经过漫长的时间,从原始生物连续进化而来,不是被设计出来的。针对这一点,原文中也给出很多有趣的例子,有些让人大开眼界,有些让人大跌眼镜,有些让人拍案叫绝。

既然人类不是被设计的,那么我要反驳的观点, 是原文中间引用的一个读者的提问,一个“真正直击灵魂的问题”——即进化论只解答了生命的演化,没有解释生命的起源。事实上,一颗砂子想要演变成一个细胞,其难度要远远大于一个细胞进化成人类。为此,原文援引了生命游戏,试图说明“复杂而有秩序的系统可以自发产生”。然而,事实并非如此。

那么,碳基生命有没有可能是被设计出来的?不仅有可能,而且可能性很大。我们从生命游戏说起。

生命游戏

这个游戏全称是Conway’s life game。它不是个传统意义的“游戏”,而是一个根据特定规则模拟的“世界”。这是个无限大的二维的网格世界,每个格子中要么有生命,要么没有生命。

规则很简单,生命的存活与否,取决于它周围一圈8个格子中的生命:

  • 寂寞致死:如果一个生命周围的生命<2个,它会因寂寞死去;
  • 拥挤致死:如果一个生命周围的生命>3个,它会因拥挤死去;
  • 继续生存:如果一个生命周围恰有2或3个生命,那么它可以继续生存;
  • 新生诞生:一个没有生命的格子,如果周围恰好有3个生命,则会诞生出新的生命来。
Gospher’s Glider Gun 滑翔机枪,向右下方发射“滑翔机”
图片来自 http://www.conwaylife.com/wiki/Gosper_glider_gun

怎么玩?

最粗犷的方式是,先随机在网格中布置一些生命——随机布置被称为一锅汤——然后看生命的自生自灭。这样一来,你马上会发现生命收敛成了一些有规律的图案,最常见的是静止图案和周期变化的振荡器。一些情况下,你会看到一个不断向一个方向移动的图形,称为“太空船”。

如果人们精心排布整个网格的起始状态,则可以产生非常复杂的系统。例如,使用滑翔机可以构建出与、或、非门,这是计算机中的基本门电路。进而可以构建出全加器,最终构建出图灵机。事实上,人们甚至成功的用生命游戏构建了一个更大尺度的生命游戏。由生命游戏构建出的神奇世界,可以参见《生命游戏的奇葩玩法》。

生命游戏构建的时钟,图片来自 https://codegolf.stackexchange.com/questions/88783/build-a-digital-clock-in-conways-game-of-life/111932

然而,应该注意到,所有这些金光闪闪的结构,离不开四个字:精心设计。比如说,构建逻辑“电路”所需要的基础模块是“滑翔机枪”,它用来发射“滑翔机”,“滑翔机”相当于电路中电流。这个基本结构需要36×9的空间,即324bit的信息量。如果靠随机量碰巧构造一个滑翔机枪,那么需要尝试的次数超过可见宇宙中的原子总数!

事实上,2004年德国数学家Achim Flammenkamp 对180万锅汤进行了模拟,出现的图形大多是静止图形、周期性的振荡器,偶尔有孤零零飞行的“太空船”。没有产生机枪或更复杂的结构。

因此,生命游戏说明:简单的规则可以支撑复杂的系统,但并不能自发产生复杂的系统。而细胞正是这样一部复杂的机器。

细胞是一部精心设计的机器

生活中常见的动物和植物,都是由细胞组成的。细胞里有个细胞核,里面存着DNA。这些DNA记录着细胞乃至整个生物体的全部遗传信息。

DNA是双链组成的螺旋结构,一侧的链上是一排顺序的“数据单元”——碱基。数据采用4进制存储,分别是A、T、G、C这四种碱基,即每个碱基有2bit的信息量。另一侧的链是冗余的数据,其上的碱基恰好和第一条链互补对应。这样一方面便于DNA的复制,另一方面起到了校验的作用。

DNA中的数据需要经过“转录”和“翻译”后,制造出具有实际功能的蛋白质,这就是基因的表达。表达过程中,每3个符号为一个“密码子”,对应于一种氨基酸。随着基因的表达,这些氨基酸按顺序形成一个长链,随后自然折叠后,形成了具有功能的蛋白质。细胞就是一部“数字化蛋白质合成仪”。

mRNA密码子阅读框架,对应于DNA则是U换成T
图片来自https://en.wikipedia.org/wiki/Genetic_code

有趣的是,这部“数字化蛋白质合成仪”本身,也是通过DNA的转录和翻译产生的。这有点像我们可以用一台3D打印机,打印出另一台3D打印机所需的零件,然后组装成一台新的3D打印机。DNA就像存储的3D打印指令文件的U盘,你只要有了第一台3D打印机,就可以持续不断地制造出更多的3D打印机,甚至是功能更加强大的新型号机器。但如果没有第一台机器,你便无法开始。

我们再看看如今的冯诺依曼体系计算机,看看它和一个细胞有怎样奇妙的对应关系。

首先,计算机中有一块连续的存储空间——内存,这就像DNA一样,只不过我们采用的是2进制,而不是4进制。软件程序的代码被存储在内存中,这些数据可以被分成一个个机器指令,每个机器指令通常8bit至32bit不等,就像DNA密码子一样。这些指令被机器按顺序执行,目的是操作内存空间中的其它任意的数据。这就像DNA密码子被顺序地组成蛋白质,再作用于细胞的其它部件。

早期IBM计算机的程序卡片。现在的计算机则把程序保存在内存中。

值得一提的是,根据DNA生产的蛋白质,是可以反过来修改和剪切DNA本身的。而在冯诺依曼计算机中,软件程序也可以像修改数据一样修改程序本身,从而改变程序本身的功能。这种技术目前在机器码的层面已经很少使用了, 但在高级语言、解释型语言中仍在使用,例如monkey patch技术。

更有趣的是,在一些病毒的DNA中,会出现这样的情况:同一段DNA,通过错一个氨基酸的位置来翻译,可以产生截然不同的两种有效的蛋白质。如果用中文举个形象的例子,就是“禾灿邻晴”四个字,把每个字拆开重新组合,也可以被读作“秋岭阳青”。(这个例子来自中译本《GEB》。)

在计算机领域中,也有个类似的技巧,叫做指令重叠(overlapping instructions),通常被用作迷惑反向工程工具,在早期存储器空间受限时,也用所缩小程序的体积。例如下图中所示的一段X86机器码。 这样的精妙结构,让人无法不相信出自精心的设计或高级的压缩算法。

一段使用了重叠指令的X86汇编代码。其中的0x000C位置的跳转指向了0x0002位置。
来自 https://reverseengineering.stackexchange.com/questions/1531/what-is-overlapping-instructions-obfuscation

人工设计的计算机和碳基生命的细胞,在信息的存储和指令的解析方面竟然有如此多相似之处,让人怀疑是不是计算机的设计借鉴了细胞的原理?事实上,DNA的双螺旋结构是1953年才刚刚提出,而现代计算机的开创者阿兰图灵逝世于1954年。如果图灵先生能活到今日,也一定会惊讶于自己的设计与生命的基础多么相似。

生命大概率是被设计出来的

既然没有任何证据能证明复杂的细胞可以在自然随机条件下产生,而它的工作原理又和人工设计的机器有如此多的相似之处,那么完全可以合理地假设,生命是被设计出来的。这里,我给出两种我认为合理的设计论假设——实验和模拟。

至于为什么说设计论成立的可能性更大?举个例子,天然珍珠非常稀少,但自从人类掌握了人工养殖的方法之后,珍珠产品就变得普及起来。现在,你去市面上买珍珠产品,几乎不可能遇到天然珍珠。类似的,如果启动一个实验、或者一次模拟,对于高等生命来说的成本很低,那么我们自己碰巧不在实验或模拟中的可能性,就像遇到天然珍珠一样低。

可能性1:生命的目的是一个实验

碳基生命可能是高等生命设计的一个实验。就像人们自己尝试让计算机具有“人工智能”一样,高等生命可能也在探索,如何制造一个有智能的机器,而碳基生命就是这个机器的原型机。

注意,这里并不是说人类是原型机,而是碳基生命。在高等生物看来,人类只是自己设计的机器产生的一种合理演化结果,而任何一种地球上的生物都是一个演化结果。人类只是目前实验中演化程度最复杂的一种。

为此,高等生命可能设计了一种DNA编程语言,比如叫做LifeScript,可以通过高级语言来描述一个基本的细胞的功能,然后编译后形成一串4进制的序列,保存在DNA中。程序员都知道,如果把高级语言代码编译成机器码,反向工程的难度非常大。类似地,人类试图反向工程通过LifeScript编写的DNA代码,难度更大!毕竟生命的复杂度绝不亚于人类的计算机。

病毒,是高等生命设计的热更新补丁,在细胞系统设计出来的早期,用来更新系统中的个体。毕竟这是一个分布式的系统,所有个体形成之后,基因在物理上是相互隔离的,因此需要一种具备传染性的方式来发布热更新。

如果这真的是一场实验,那么其架构设计之宏大令人叹为观止。想想看,只要在一个环境适宜的地方培养一个细胞,它就可以自我迭代,改造环境,产生智能生命,并制造出现在人类的一切——建筑、机械、计算机等等。这些都可以看作是这场实验的产出。而所有这些的源头——细胞本身,其实并不具备任何“智能”,它只是具备了强大的演化出智能的潜力。

可能性2:我们的世界是模拟的

我们的世界也可能完全运行在一个软件模拟器中。

《黑客帝国》只是给人们一种便于理解的场景,即软件模拟中的世界和外面的真实世界具有几乎相同的物理属性。而真实情况可能比它复杂很多。很多基础的物理参数很可能不过是模拟器的设定参数,包括:这个空间是3维的,这里有个单向、线性的时间概念。而模拟器外面的“真实世界”,可能维度更高或更低,或者根本没有时间的概念。

这些物理参数定义了我们的思维方式。所有地球上的生命都无法假想自己处在一个没有时间的世界,或者一个超过三维的世界。这就像《西部世界》中的机器人无法感觉到真实的人类一样。

模拟器可能还有别的参数设定,例如:这个宇宙中只有一个星球来模拟碳基生命。这就是为什么我们无论如何都找不到地外生命——因为碳基生命才是本次模拟的核心研究对象。别的生命都在其它的模拟器中运行着。

如果是这样,那么我们大可不必担心地球毁灭之类的问题,至少在人类具备星际迁徙能力之前不需要。因为我们有主角光环!

智能的层级

不论是实验还是模拟,都不妨碍人们按照这个世界的规则继续生活,继续探索智能的本质。那么,让我们类比一下人类的计算机和人工智能技术,距离地球上碳基生命今日的巅峰——人类自身所达到的成就,还有多远。

目前计算机的基础是冯诺依曼架构的CPU,它忠实地执行其存储器中的指令,丝毫不差。如前面所述,这一点类比于细胞质,它实现了DNA转录和翻译的基础环境。只是计算机硬件是额外单独生产的,而细胞质中的物质也是通过基因表达制造的(就像能打印自己的3D打印机)。

计算机硬件上直接跑的都是机器码,但如果仔细区分,会看到这里面的多个层次。操作系统的内核代码直接跑在硬件上,它决定着存储器中的哪些功能代码,应该在何时被执行。例如,它可以调度多个进程跑在同一个处理器核心上,每个进程分到一些时间片和资源。而每个进程本身,可能跑的是一个解释器(比如python虚拟机),用它再来解释执行另一种编码(比如python字节码)的软件。而这一堆python字节码,可能大部分来自你使用的Django框架,而框架本身,又像是一个操作系统一样给每个执行具体业务的python子任务分配资源。

这样一来,虽然机器码可以类比与DNA的密码子,但是人们如果只看到一串机器码,完全无法理解上面跑的这个web服务本身的功能,就像只看基因密码子,无法推出表达后的蛋白质功能。事实上,计算机软件的多层结构,可以类比为蛋白质折叠中的二级、三级结构。

至此,人类计算机的硬件加软件,在功能层级上,大致可以等同于一个单细胞生物。

计算机和智能生命的对比

值得称赞的是,目前的深度神经网络技术,可以看作是计算机体系架构向着硬件、软件之上的第三个层级进发的尝试。在硬件、软件完全相同的情况下,通过输入不同的训练数据,可以训练出截然不同功能的网络。而使用这些网络时,也仅仅是加载不同的网络参数。

我倾向于把深度网络的参数,看作是比软件更高一层级的东西,暂且叫“符号件”吧。传统的软件也可以通过配置文件来重新定义功能,例如Nginx。然而不论其可配置性有多强,都脱离不开它是个网络服务软件。而加载不同的“符号件”,甚至可以把一个美颜的网络,变成一个下棋的网络。这就是“符号件”可以被理解为一个独立层级的原因。只是目前的“符号件”并不能完全覆盖软件层,需要和软件协同工作。

再看碳基生命。单细胞生物之上的层次,是多细胞生物。通过细胞的分化形成器官,多细胞生物中的每个细胞各司其职,形成了一个有机的整体。我们每天身上都有无数个细胞死掉,又产生无数个新的细胞,但我们作为一个整体依旧健康存活。多细胞的整体中,也分化出了一种特殊的细胞,用来支撑智能的更高层次,它就是神经细胞。

单个神经细胞本身的功能特点是简单的,但大量的神经细胞,就组成了更高层次——思维概念或符号。注意,这还不是人类特有的东西。小狗高兴时摇尾巴,饿了找东西吃,这些都是神经系统中大量的符号组合触发的结果。这一点,类似于计算机智能中的人工神经网络,这也是“符号件”这个名字的由来。

神经符号再上一层,是完备的语言。这是人类具备思维能力和意识的独门绝技。有了完备的语言,就有了讨论各层次的智能的工具。它是具有自指能力的工具。不论是神经符号,还是神经元,都不能对它的下一层结构产生理解或说三道四。而有了语言,就可以谈论从细胞到语言自身的所有问题,还可以谈论已发生的事情或未发生的设想。这就是自我意识。

语言/意识层次之上,是多个个体的协作,即社会。人类的每个个体,都造不出高楼大厦,造不出集成电路。这些成果是人类社会合作的结果。社会的组织方式,是智能的最高层级。如《人类简史》中所述,无论是共同信奉的宗教,强大的帝国暴力机器,还是对金钱的信仰,都是在不断演进的促进人类社会合作催化剂。

反观通过跳步才勉强到达“符号件”层级的计算机系统,距离通用智能,路还很长。

全球最强大的区块链项目,竟然不是比特币?

没什么意义的头图

提到区块链三个字,恐怕如今的小白也能说出来比特币。刚过去的2018年,区块链炙手可热,有人忙着割韭菜,有人忙着伸出头给人割,有人忙着想搭车融资,也有人在摸索区块链除了庞氏骗局和博彩以外,究竟有什么正能量的绝杀应用。

其实,有这么一个区块链应用,早已经深入到每个IT工作者的生活中了。在我自己的工作中就离不开它,但直到看到一期Linus Torvalds 2007年在Google的讲座视频,才恍然意识到它才是最成功的区块链项目。论时间,比Bitcoin早了4年;论用户量,它比Bitcoin多一个数量级。

它就是git。而一个更大胆的推演是: Linus或许就是中本聪本葱。

Linus Torvalds强烈鄙视CVS的用户

先说说什么是区块链。(了解请自行跳过)区块链其实是个无中心的分布式数据库(新近项目则是分布式的计算机,我们暂且不提)。例如,数据库中可以保存着每个人的账户有多少钱。所谓分布式,是指每个参与的用户都保存着部分或者全部的数据,大家对全部数据的内容通过一种机制达成共识。这样一来,就不用担心银行的系统被黑掉,或者数据中心遭到自然灾害而瘫痪,导致的数据丢失。

然而分布式的问题是,假如有一个恶意的用户在自己保存的数据上做了手脚,还宣称自己的数据是真实的,那么大家必须有能力分辨是非。区块链技术正是通过密码学的方法,给大家雪亮的眼睛,保证不可能被个别几个恶意的用户骗到。

Bitcoin区块链示意图1:4笔交易被记录在BLOCK 50中,其中引用了BLOCK 49的哈希值

之所以称为区块链,是因为数据是按照一个个“区块”为单位添加到数据库中的,区块只能添加,不能删除或修改。而每一个区块,都有一个哈希值(签字画押),这个哈希值的计算过程包含了上一个区块的哈希值。而上一个区块的哈希值又包括倒数第三个区块,以此类推,区块就成了个链条。如果有人要改动任何一个区块的内容,势必引起从这个块往后的所有块的哈希值改变(签字画押被破坏),别人一眼就看出来了。

重新画个押行不?行,但每一个画押的过程,都需要全球参与者的计算机共同努力一段时间才能完成(俗称挖矿,学名叫PoW,Proof of work)。靠攻击者一个人的计算资源,大概一辈子也画不出来一个押,更别提重新画押后面被破坏的所有区块,以及重新画押的这段时间中,正常用户在共识的数据库中添加的新的区块。据此,这个机制保证了无人能篡改已经进入数据库中的区块数据。

https://git-scm.com/

Git是什么?Git是目前全世界最受欢迎的版本管理工具,在2005年由Linux内核的作者Linus Torvalds设计开发。Git之于程序员,就如同知网之于博士。但如果一个博士不知道知网,那还可能是个外国博士;但如果一个程序员不知道git,那他只能是外星人了。

但是大家可能不知道,git的数据结构就是区块链。首先,git仓库是个数据库,其中保存的是一个文件夹和其中所有文件的全部更新历史。当git仓库用于保存多人协作的软件代码时,这个数据库需要在所有开发者之间共享,因此就形成了一个分布式的数据库。

Git的数据结构中,有3种相互引用的基本类型,分别是:

  • blob:用于保存文件的内容,不包含文件名
  • tree:用于保存文件夹中的子文件夹和文件的名称及属性列表。其中,子文件夹会引用另一个tree对象,而文件会引用blob对象。
  • commit:整个项目的一个历史状态。包括时间、提交者、一段描述文字、对一个tree对象的引用——即所保存状态的根文件夹。最终要的是,commit中还包含对若干个旧历史状态(父commit对象)的引用。

所有对象都有个唯一的标识符,就是这个对象完整内容(经过压缩后)的SHA1哈希值。

3个commit形成链条,分别引用tree对象。注意tree对象和blob对象是可以重用的,这大大提高了git的存储效率。
图片来自https://git-scm.com/book/en/v2/Git-Internals-Git-Objects

这样一来,如果你想更改一个文件在某个历史时刻的内容,你首先要修改它对应的blob,这会导致它的哈希值改变。为了能让文件夹能找到这个更改后的文件,文件夹tree对象中引用blob的那个标识符就得随之改变,这就引起了tree的哈希值改变。进而,那个历史的commit要引用修改后的tree,其哈希值也会变化,接着会引起这个commit的后续所有commit的哈希值变化。

事实上,由于密码学哈希算法SHA1的使用,只要给定任何一个commit的哈希值(仅仅20个字节),就可以唯一确定一个git项目某一对应时刻的完整状态,以及其完整的历史沿革。

这就是一个区块链。每个commit都是一个区块,commit之间的引用关系构成链条。由于每个commit可能引用若干个更老的commit,因此这不仅仅是个链,而是个有向无环图(DAG,directed acyclic graph)。

有人认为,仅仅是数据结构符合区块链特性,并不能足够说明git的区块链属性。而git的设计思想和使用方式,让git仓库成为了真正意义上的区块链应用。

在git仓库里怎么挖矿?当然是通过写代码、提交代码。这实在是太妙了,我只要在Linux kernel里面提交两行注释说:“幻腾智能招聘golang工程师”,就可以被所有内核程序员看到,就可以在家坐收英才简历了?

想得美!

软件开发中的共识机制是:谁的提交功能好、bug少,就能被大家认可,加入到共识的版本中去。因此,虽然人人都能在git上提交新的修改,但一个招聘广告显然对Linux内核没有价值,不会得到社区的认可。只有那些真正有价值的提交,才会被pull到大家都认可的仓库中。 这是一种真正意义上的proof of work,这里的work是真正意义上的工作、成果。

与比特币的“验证哈希值足够小”相比,这种对于代码提交质量的判断,完全是主观的。为了保持分布式的git仓库的一致,就要求一个项目的每个参与者,都有能力review所有人提交的代码,并以相同的标准接收高质量的提交。这在现实中几乎是不可能的。因此,人们就组织起来,在小范围内统一接收标准,形成了软件的维护者团队。而维护者以外的开发者或使用者,则完全根据自己个人的偏好,选择一个自己信任的维护团队。这就类似于有人维护比特币全节点,有人使用轻钱包。

bitcoin v.s. git

回到Linux内核,作为世界上最重要的开源项目,它由Linux基金会组织,由Linus和一群小伙伴们一起维护。全世界的开发者都信任Linus的团队,从而请求他们合并自己的提交,并仅仅信任被他们接受的提交。试想,假若有一天Linus黑化了(Linus自己的原话是,假若某天某个小伙伴忘了吃药了),在内核中真的发布了幻腾智能的招聘广告,那么一旦有人发现,就不会再信任Linus维护的版本。而另一个有信誉有技术的人,比如Minus,就会扛起大旗,继续内核的维护工作。于是Linux大旗不会倒,但可能会改名叫Minux吧。

这就是git中的硬分叉。在数字货币领域,分叉已经成了家常便饭(菜谱包括猪肉韭菜包,韭菜炒鸡蛋等)。在git管理的开源项目中,分叉也很常见,原因多种多样。

最主要的原因之一,是原先的维护团队“黑化”。当然,通常不是指在代码中夹带广告或者恶意代码,而是改变了软件的License授权协议,开始收费或者增加使用条款。这时,几乎一定会有人揭竿而起,基于原License的版本继续开发,从而形成分叉。例如,大家现在都在用的OpenSSH,就是从早期的SSH项目分叉而来,因为后者成为了商业软件。又比如CentOS从RHEL分叉而来,为社区提供免费的企业级Linux发行版。而LibreOffice从OpenOffice分叉而来,更是暴力地干掉了原本的OpenOffice项目——大部分OpenOffice的维护者都加入了LibreOffice,从而没有人再维护OpenOffice了。

LibreOffice的发展(分叉)历程

另一些分叉比较温和,可能仅仅是因为团队对于产品定位和方向的理解不同。比如Ubuntu从Debian分叉而来。可能Ubuntu看不惯Debian的产品和设计,但仍然依赖Debian的技术,两者还是好基友,代码补丁共享。

这些都是开源软件社区中活生生的分叉的例子,而git给了大家发起和维护分叉的工具。可见,git不仅仅在数据结构上使用了区块链技术,而且在使用思想上,也涵盖了区块链中的关键概念:分布式、共识机制、分叉等。你是否曾经怀疑,为什么在开源软件这样一个需要无中心的信任机制的领域,没有看到区块链的应用呢?答案是,大家早在2005年,就在Linus大神的带领下,走进了区块链的殿堂,那时候比特币还没诞生呢!

说到这里,我们甚至可以抱着吃瓜不嫌事大的态度揣测,以Linus的设计能力和技术实力,加上他早在2005年就把区块链玩得如此透彻,就差用在数字货币领域了。他难道不是发明比特币的不二人选吗?事实上,他在2007年的演讲中就说,“现在我只需要想明白第三步:怎么赚钱”。

清理即将爆炸的redis

近日发现运行多年的redis服务器越来越慢,已经接近爆炸了。

服务器配置为:2GB内存,2GB swap,只跑了redis服务,没有任何其它服务。

登录后发现redis的内存占用在80%左右,此外还有redis-rdb-bgsave进程,内存占用也在80%左右,同时还有接近80%浮动的CPU占用。这两项导致整个服务器的内存和swap几乎全部占满了,服务器响应非常缓慢。

redis-rdb-bgsave是redis的持久化保存进程,它是从redis主进程中fork出来的。因此,当fork刚刚发生时,由于内存的copy-on-write机制,内存占用并不多,随着bgsave的进行和redis服务进程仍然在修改内存数据,两者的差异逐渐增加,内存占用也越来越大。加之swap的存在,数据不停地在硬盘和内存之间传递,性能惨不忍睹。

登录到redis-cli上,查看info,发现一共有760万个key,总内存用量为1.5G。从业务上看,并不可能有这么多key。这都是什么呢?

> redis-cli scan 0 count 1000

执行上面指令,列出来1000个key,发现其中几乎全部是cache:*。这时Rails的session存储。进而发现是Rails session没有设置超时,导致这些key全部积累起来了。

如何增加Rails session的超时不表,下面我们要删除这700多万个cache:*。可惜redis没有提供根据pattern批量删除key的办法。

如果去网上找批量删除key,大概会发现两类方法:

  1. redis-cli keys <pattern> | xargs redis-cli del
  2. 利用exec lua脚本在redis内删除

如果你要删除的keys不多,可以参考这两种方法,参考链接如下:

https://huoding.com/2014/04/11/343

https://stackoverflow.com/questions/4006324/how-to-atomically-delete-keys-matching-a-pattern-using-redis

实际情况是,因为redis数据库已经处于爆炸边缘状态,任何keys <pattern>操作,都会返回700万个key,所占用的内存完全分配不出来,因此完全行不通。

解决办法如下:

首先需要暂时关掉bgsave,这样可以省出来几乎一半的内存,即几乎把swap都省出来了,内存还是满满的,不过已经可以顺畅操作了。

修改/etc/redis.conf,把其中的bgsave相关设置调成不保存:

#注释掉默认的备份数据
#save 900 1
#save 300 10
#save 60 10000
#增加一条“不可能”触发的,理论上也可以不增加,不过为了保险增加也无妨
save 3600 1000000

重启redis,可以发现,bgsave不会自动开始了,swap也几乎空着,只是RAM仍然占满,redis仍然占用80%以上内存。这里我直接kill -9杀掉了原来的redis和bgsave,然后重启。如果不强行杀,也不知道要等多久才能正常重启。如果真的在乎尽量不丢有用的数据,可以尝试正常重启。

即便这样,仍然不能用keys <pattern>的方式列出所有要删除的键——内存仍然不够。我的办法是用scan一点一点删

> redis-cli scan 0 match cache:* count 10000 | xargs redis-cli del

实测count 10000的时候,机器不会死掉,可以正常运行。

理论上scan会返回一个新的cursor,以便下一次scan的时候用(换掉scan后面第一个参数0)。不过咱们这种简单粗暴的方式并不会输出这个cursor给你看,所有只能多执行几次上面的命令,你会发现del成功的数量越来越少。

之后,可以换一个起始cursor,比如1000000,再跑几次。然后再换一个……这样删了几轮之后,逐步把count提高,例如20000、50000、100000。

注意,删除的过程中,通过info可以查看剩余的总keys数量。不过这个过程中内存并不会被释放出来,redis的内存占用只是从80%以上降低到了70%左右。事实上,这微小的内存释放,就是咱们增大scan count的资本。

当删除到剩下300万左右keys的时候,count可以增大到50万。这样再来几次,几乎所有键都删掉了。

接下来,怎么彻底释放内存呢?

先手动执行一次bgsave(因为之前已经”关掉”了bgsave,所以需要手动执行)。可以感受到bgsave并没有让系统变慢,查看一下保存的文件,尺寸小了一个数量级。

然后把/etc/redis.conf改回原有的保存策略,重启redis,可以看到重启后,redis内存占用变得很小了。

至此,大功告成。

总结本文要点:

  1. 暂时停掉bgsave,可以让redis暂时变得可操作,而不是完全不能响应
  2. 内存占用很大的时候,keys <pattern>操作或者count过大的scan都没法执行,只能通过count比较小的scan逐步删除。

幻腾Local Control固化边缘控制逻辑

本文适合对软硬件技术和物联网架构都有深入了解和实践的专业人事阅读。

考虑一个简单的使用场景:一个过道顶安装一个人体运动传感器(幻腾产品UFO),用来控制两盏智能灯(幻腾产品Nova)。当有人经过时,UFO可以检测到人的运动,从而控制Nova点亮到100%亮度;当人离开后,UFO检测不到人的运动,Nova亮度降低到20%。UFO和Nova之间采用无线通讯,之间并没有线缆连接。此外UFO是由纽扣电池供电的低功耗设备,需要有长达若干年的设计电池寿命。

上面这个场景,有三种实现方式:

  1. 大闭环:UFO将检测到的有人/无人状态发送到云服务器,云服务器根据IFTTT策略向Nova发送控制指令;
  2. 小闭环:UFO将检测到的有人/无人状态发送到本地网关或中控设备,中控设备根据IFTTT策略向Nova发送控制指令;
  3. 最小闭环:UFO直接向Nova发送控制指令

其中,方式1扩展性最强,但控制延时不确定,有时可能长达1~2秒。这样的延时对于IoT控制来讲,体验非常差。方式2延时较短,但引入了复杂的本地中控设备,不仅成本高,而且是个“单点失效”的设备——一旦中控设备不稳定,就会导致整个系统瘫痪。智能系统瘫痪后,连传统的开关灯都无法实现,这是不能接受的。

幻腾Local Control固化控制协议,采用的是上述方式3的控制方式。这样,只要Nova有电,控制就可以达成。它不依赖于网关或中控设备是否联网,甚至不依赖于网关是否通电开机。网关仅仅作为云端控制和收集统计数据的桥梁使用。

这好像并不复杂呀,人体运动传感器相当于一个遥控器嘛?

如果你曾经尝试过给一个四路或者六路的电动窗帘遥控器对码,你一定清楚这是一个多么痛苦的过程。幻腾将“遥控器”和“执行器”的控制策略保存在云端,并通过Topology Generator为每个设备生成需要的分布式控制策略,并可靠地同步到设备上。这样,用户只需要通过手机APP或者幻腾后台,就可以对固化控制关系进行更改。这和对码相比,即方便又安全。

有点意思,不过仍然不难实现。好,那我们把场景的复杂度提高一些:

还是走廊里的两个Nova灯,我们现在需要用两个UFO来控制,分别放在走廊的两头。我们希望,只要其中任意一个UFO检测到有人经过,就应该打开灯;但只有当两个UFO都检测不到人时,才减弱灯光。即:

  • 灯光明亮条件:UFO1或UFO2有人
  • 灯光减弱条件:UFO1和UFO2都无人

如此一个简单的与、或算符,该在哪里执行?如果采用大闭环或小闭环,那么云服务器或中控设备将毋庸质疑地担当“重任”。但我们不喜欢延时和单点失效,事情就变得复杂了。

值得一提的是,现在另一种单点失效的形式是:其中一个UFO在有人的状态下突然坏掉了,这时系统可能认为这个UFO一直处于有人状态,因此灯光总是不会变暗。这也是不能接受的。常理告诉我们,走廊中的人不会长时间停留,所以如果一个UFO长时间(比如超过4个小时)未上报无人,那么它应该是坏掉了。对于坏掉的UFO,默认情况应该认为是无人的,这样可以不影响整体策略的执行。

我们继续提高复杂度:

假设走廊尽头有一个SNPi按钮开关,它是一个无线按钮,并不会接通和断开Nova的电源,而是在被按动时,通过无线电给系统发送指令。现在我们希望,当SNPi按到“关闭”位置时,无论两个UFO是否有人,Nova都不点亮;当SNPi处于“开启”位置时,UFO可以控制Nova的亮暗。

  • 灯光明亮条件:SNPi开启,并且UFO1或UFO2有人
  • 灯光减弱条件:SNPi开启,并且UFO1和UFO2都无人
  • 灯光关闭条件:SNPi关闭

看起了我们的表达式变得更加复杂了。那么,是否支持任意复杂的逻辑表达式,就可以解决问题了?并不是。

我们继续给走廊增加一些动感:当有人从UFO1这边走进走廊时,Nova1先亮起,接着Nova2亮起;当有人从UFO2这边走进时,则先点亮Nova2,再点亮Nova1。此外,上面的SNPi开关逻辑仍然存在。当然,我们也可以用更多的Nova,使走廊灯光形成跑马灯逐个点亮的效果。这样一来,控制加入了时间的维度。

现在,你会想:灯里面不是都有MCU嘛,可以给每个灯设计定制的程序,那样就可以实现任意的效果。

是的,但那样是不可扩展、不可持续维护的系统。利用幻腾的Local Control系统,并不需要定制每个灯的固件,不需要MCU编程。只需要用市面上买到的Nova,就可以实现上面这些复杂的场景。当然,复杂的场景仍然需要专业人员的配置,不过大部分配置的工作量都可以通过电脑在云端完成。

这就是幻腾Local Control系统的能力——一个分布式控制系统。它无延时、无单点失效、可以通过云端配置、具有无限扩展性。

震惊!央视315炮轰无良LED厂,智能家居厂竟集体躺枪

2017年315晚会上,央视曝光了部分打着“护眼灯”旗号的LED灯泡,存在严重的频闪问题,并给出了一个简单的用普通手机摄像头检测频闪的方法。消息一出不到5分钟,立刻就有用户发来了微信,说我用手机看了,怎么这智能灯也有频闪呀!

赶快回看315晚会,果然,央视继续秉承一贯的“为了大众的认知能力,不能把科学讲太深”的精神,把大家的恐慌煽动起来后就撤了。是时候把央视没讲出来的部分写出来了。

什么是频闪(Flicker),哪些灯具会有频闪,哪些没有?

大家都知道,咱们的市电是50Hz的交流电,也就是说,在一秒钟之内,会交替变化50个周期,就像下面的图那样。

不仅仅瞬时电压是波动的,相应的瞬时电流和瞬时功率,都是以50Hz波动的。正因为这个50Hz的交流电,导致几乎所有插在市电上的电子设备,都或多或少带有一些50Hz的印记。

(315合格灯具,依然可见微小的波动)

日光灯:说到频闪,最典型的是需要用启辉器的日光灯管。这类灯管,是通过电流激发管里的气体,产生光线。电流无论正反流动,都能激发发光,只有电流接近和经过零点的时候,才不发光(但仍有辉光),因此,这类灯管会产生两倍于电流频率的频闪,在市电下就是100Hz(美国就是120Hz)。

白炽灯、卤素灯:这类灯具都是靠热来发光的,虽然灯丝的瞬时功率是波动的,但是其温度的变化是非常缓慢的,因此其光线是持续无频闪的。

节能灯:所谓节能灯,其实是指采用电子镇流器的荧光灯,发光原理和日光灯相同,但电子镇流器会把50Hz的市电先转换成直流,再转换成频率高达20k-60kHz的高频交流来驱动灯管。因此它也有“频闪”,只是频率高了近3个数量级。

LED:它是有潜力实现没有频闪的光源。LED灯具中,50Hz市电首先转换成直流,再转换成恒定的电流和电压,直接以直流驱动LED发光器件,因此几乎没有频闪。同时,LED也是目前量产商用的效率最高的光源。

很遗憾的是,一些无良LED厂商为了降低成本,并不用上述这种市电->直流驱动的方式,而是直接用简单整流后的市电来驱动LED器件。这样,LED就和日光灯一样,会产生100Hz的频闪。在(Wilkins-2010)中,记载有如下会产生频闪的简化LED驱动方式。事实上,我曾经在某个办公室看到过采用这种方案的灯泡。某宝上的1.9包邮的LED灯泡,大家就自己掂量着买吧。

(简化LED驱动图,摘自Wilkins-2010)

频闪对健康是否有影响?

频闪,是“频”和“闪”两件事组成的。离开频率谈频闪对健康的影响,是不科学的。遗憾的是,直到电子镇流器之前,人们对频闪的讨论都只集中在了50-60Hz工频,以及其倍频100-120Hz。

频闪对人的影响,大致有两类:

第一类:可见的频闪(Visible Flicker),大约3-70Hz。这类闪烁可以被人直接看到。短期暴露在这种环境中,就有可能产生癫痫(IEEE-Std-P1789,2010)。事实上,曾经有动画片中出现闪烁过于强烈的画面,就导致很多看电视的儿童发生癫痫。

第二类:不可见的频闪(Invisible Flicker),大约70-160Hz。这类闪烁虽然人不能感觉到,但是却实际上会被视网膜捕捉到(Veitch-1995),反馈在“视网膜电图”上(ERG,类似脑电图、心电图吧)。这类闪烁会导致人用眼疲劳、头疼、视觉能力下降等副作用。

比160Hz更高的频率,就超出了视网膜反映的速度,因而也没有在ERG中检出。这意味着,160Hz以上的频闪,已经超出了对人体产生影响的范围。也正因此,采用电子镇流器的节能灯虽然也有频闪,但对人是没有影响的。

除了频率,频闪的深度(最暗和最亮照度的比例关系),也对人有一定影响。根据(Bullough-2013):

  • 1000Hz 100%频闪,要优于100Hz 25%频闪,这说明远超过人体感觉能力的频率,哪怕频闪强烈,也比低频率的微弱频闪要好;
  • 100Hz 25%频闪,要优于100Hz 100%频闪,这说明相同的低频率下,更浅的频闪更好。

回忆一下,自己的童年貌似是在学校的老式日光灯管下度过的,怪不得眼睛疲劳学习效率低下呢。。

用手机摄像头测频闪,靠谱吗?

手机目前多采用的是CMOS摄像头,其显著的特点就是“滚动快门(Rolling Shutter)”,相信爱好者对这个现象都非常熟悉。正是利用这个原理,CMOS摄像头可以用来探测人眼看不到的闪烁。

(螺旋桨的Rolling Shutter效果,摘自(Cole))

CMOS摄像头每一行的采样速度通常在5-10微秒量级,因此可以捕捉到高达20-50kHz的闪烁。如果要在拍出的照片中表现出来,那么需要经过一系列软件对图像的滤波和处理。保守估计,这样最高仍然可以拍到5000Hz的闪烁。

因此,CMOS摄像头可以让你看到50-5000Hz的频闪,然而记住,只有160Hz以下的频闪才能真正影响人,而有证据的影响人健康的频闪,是更低的频率。

智能家居厂商的躺枪

这是我手头的测试设备:除了自家的幻腾Nova,还有飞利浦Hue(不小心摔碎了外壳),飞利浦LivingColor,飞利浦灯带,小米YeeLight灯泡,小米网关小夜灯。

(全家福,上排左起:飞利浦LivingColor,小米YeeLight,幻腾Nova,飞利浦Hue(破损版),小米网关夜灯;下排:飞利浦灯带)

如果把亮度开到100%,用手机摄像头查看,所有产品都“一片祥和”。

(从左到右:Nova,YeeLight,亮度100%,一片祥和。至于Hue,因为我把飞利浦网关搞丢了,所以没法演示100%亮度时候的祥和了。。。)

(从左到右:Nova,YeeLight,Hue,小米网关夜灯,亮度低于100%时,均由于PWM产生无害的高频频闪)

(从左到右:Nova,YeeLight,Hue,小米网关夜灯,亮度和上一张图相同,远看一片祥和)

但如果亮度不是100%,所有产品都会出现明显的频闪条纹!难道智能灯们都会影响健康吗?其实不然。

产生频闪条纹是因为,目前LED的数字调光方案,几乎全部是使用“脉宽调制(Pulse-width Modulation, PWM)”来实现的。通俗来讲,就是通过高频率地开关发光器件,实现对亮度地调节。再通俗来讲,就是通过频闪来实现调光。因此,其产生频闪本身是设计所致。

注意到,飞利浦Hue的调光频率是500Hz,YeeLight和幻腾Nova的调光频率都是1000Hz,远高于上面所说的可以对ERG产生影响的160Hz,完全不会对人产生任何影响。也正因此,业界一直采用高频率的PWM进行调光,这已经是LED数字调光的规范。

如果把智能灯调到100%亮度,则不再需要用PWM进行调光,则LED的驱动就是直流,完全没有频闪。

接着,我又试着把手头的一个Nova的PWM频率调到了10000Hz(10kHz),用手机摄像头就看不出任何频闪条纹了。当然,因为Nova的驱动性能非常好,在10kHz的调光频率下仍然可以工作自如,最低可调亮度仍然可以低至千分之4。

(幻腾Nova更新固件,把频率调至10kHz后,用手机无法看到频闪现象了。)

对于其它一些功率更大的驱动电源,如果调光频率过高,可能导致效率降低,或在低亮度情况下不稳定的情况。这就是为什么大部分厂商选择500-1000Hz的调光频率。

究竟怎么判断哪些频闪是有影响的?

还是手机。其实,咱们可以大致通过手机拍摄的条纹宽度,来估计频闪的频率。

(晚会展示的手机图像)

手机摄像头的帧率大约30帧/秒(随手机品牌不同可能不同),即整帧的刷新率是30Hz。因此,如果一幅照片中可以看到大约3个条纹,我们就可以估计,实际闪烁的频率大约是30Hz*3 约 100Hz。

从上面实验用的智能灯照片也可以看出,频率在500Hz-1000Hz的时候,条纹是非常密实的,并不像晚会中展示的那种,一副画面只有3个条纹。

总之,手机看到条纹很宽,闪烁很明显的,那就是工频(100Hz)左右的频闪,是不好的;手机看到条纹很窄很密的,那就是高频(500Hz以上)调光信号,对人没有任何影响。

参考文献

Arnold Wilkins, Jennifer Veitch, Brad Lehman, LED lighting flicker and potential health concerns: IEEE standard PAR1789 update, Energy Conversion Congress and Exposition (ECCE), 2010 IEEE

IEEE Standard P1789, A Review of the Literature on Light Flicker: Ergonomics, Biological Attributes, Potential Health Effects, and Methods in Which Some LED Lighting May Introduce Flicker, 2010 IEEE

Veitch, J.A., McColl, S.L., Modulation of fluorescent light: flicker rate and light source effects on visual performance and visual comfort, Lighting Research and Technology, Vol. 27 No. 4, 1995, pp243-256

John D. Bullough, Nicholas P. Skinner, Kathryn Sweater Hickcox, Visual Task Performance and Perceptions of Lighting Quality Under Flickering Illumination, Journal of Light & Visual Engironment, Vol. 37, No. 4, 2013, pp189-193

Jason Cole, The Math Behind the Rolling Shutter Phenomenon, https://petapixel.com/2014/10/13/math-behind-rolling-shutter-phenomenon/

 

雾霾天,宅在家干什么最环保?

16日-21日,北京雾霾红色预警,单双号限行。出不了车了,这个周末估计很多人都选择宅在家里看电视、刷剧、打游戏。那么宅在家里会不会也危害社会呢?让我来一本正经地算一笔账。

既然看电视、刷剧、打游戏都要用电,那咱们算一算1度电产生的PM2.5。电能是二次能源,电来自于风、水、火、核能等等其它能源。其中产生PM2.5的最主要来源,就是烧煤炭的火力发电。

(下面是艰深的计算,不感兴趣可以快进到=TL;DR=分割线)

那么1度电要烧多少煤?根据《2015年全国电力工业统计快报》,我国2015年总发电量是56045亿度,其中煤电是37649亿度。因此,煤电占我国总发电量达到67.18%。其实这个比例非常高,在美国,化石燃料发电只占40%左右,所以人家的电更清洁,天更蓝。

言归正传,因此每用1度电中,有0.6718度来自烧煤,这要烧多少煤?我们看看《国家能源局发布2015年全社会用电量》,其中说大型火电站的效率是每度电315克煤。因此,我们用的1度电,消耗的煤就是0.6718*315=211.6克。

那么烧这些煤产生多少PM2.5?我找到了一份科技日报的采访《一公斤煤到底产生多少PM2.5?》,其中说到“1公斤常规烟煤中会产生8—12克PM2.5”,“……目前电厂的电除尘器对PM2.5脱除效率大约在95%上下”。我们取中间值,每1公斤煤实际排放的PM2.5大约是10克*5%=0.5克。

这样一来,我们每用1度电,相当于消耗了211.6克煤,这产生了0.2116kg*0.5g/kg=105.8毫克PM2.5。

这是多少PM2.5呢?按照我国标准,空气污染指数是200的“重度污染”雾霾天,PM2.5的浓度是150微克/立方米。105.8毫克PM2.5可以制造105.8/0.15=705立方米地重度污染空气!

====TL;DR分割线====

因此:

如果你是土豪,在北京有一套70平米房子,那么705立方米可以把你的房子填满4次。如果你是屌丝,租一间20平米的合租卧室,那么705立方米可以把你的屋子填满14次。

回到宅在家里刷剧的问题。如果你用一台笔记本电脑刷,笔记本大约功耗是60W。也就是说,每分钟耗电是0.001度电,可以把0.7立方米,即700升空气变成重度污染的雾霾空气。

还不直观?传说中地铁里面PM2.5非常低,是因为地铁人多啊,每个人的肺都是个生物净化器呀。静息情况下,一个人的肺通气量大约是7L每分钟。因此,你刷剧持续产生的PM2.5,够100个人吸,这大约相当于小半截地铁车厢的人。

如果打游戏,你可能不满足于笔记本电脑,而是用一台300W的台式机,那么就可以污染500人的空气。冬天冷,打开空调吧,平均功耗按600W算,那就又污染了1000人的空气。加上你的台式机,整整一趟地铁的人都被祸害了!

电水壶烧1.5L水?污染了100,000升空气。

电热水器洗个澡?污染了800,000升空气。

用手机看现在这篇文章?污染了500升空气。

看到了吧,为了北京的蓝天,别刷剧,别打游戏,别洗澡,别刷朋友圈。。。安安静静发愣吧(请注意别开灯!)。

如果你真的想做点贡献,可以把你的空气净化机放在外面转,一台30W的净化机(CADR=300立方米/小时),每分钟耗电产生350L污染空气,但可以净化5000L的空气,这个还是很划算的!

不太现实?那来点现实的。在办公空间安装“幻腾智能”照明系统,自动根据光照和人员占用调整照明,节约照明用电30%以上,真真切切减少PM2.5。