欢迎评论。。以后还可能写个版本2。。。 冗余的人类语言 研表究明,汉字序顺并不定一影阅响读!事证实明了当你看这完句话之后才发字现都乱是的。 以上这句话很多人都看到过,知乎上也有很多人讨论问题的原因[2],对于这段中文,有人从贝叶斯决策的角度分析,指出这是由于人借助了上下文的相关信息+日常的经验。对于英文,如果把一段话的每个单词的字母顺序打乱,也会出现不影响阅读的情况,有人指出这可能是人在阅读一个单词时只看第一个字母和最后一个字母的原因。我感觉都是有道理的。 但现在我们要从另一个角度考虑这个问题: 假设在另一个平行宇宙中,汉语“研表究明”和“研究表明”存在截然不同表示的是两种截然不同的意思,那么就算那个宇宙中的人们可能就觉得这两个词是有区别的。 再假设一个更极端的平行宇宙,他们每个单词都长为6个字母,且只能由ABCDEF 6种字母组成(我算了下有4万多种组合,够多了),那么,他们对词序的判别应该就特别严格吧。 可见,我们所在的平行宇宙中,语言中是存在“冗余”的。这样,不管依据什么经验,语言的冗余,让我们辨识出调换词序的句子的原本意思成为可能。 其实不光调换词序体现了这种冗余。类似的,当我们在一个噪音大的环境中讲话的时候,或者在FM收音机中调出一个信号不好的电台时,我们一般情况下照样可以听明白对方所讲,靠着也是这种“冗余”,我们可以根据已知的信息,猜出完整的信息。 0和1:抗干扰的冗余 接着说收音机。如前边所说,我们的语言中存在”冗余”,所以接收的电台中音调偶尔的变化或者一些字的不清晰依然不影响我们接受信息。那么为什么现在的网络电台没有这些失真、底噪呢,人们靠什么高科技来消除这些噪声干扰? 我们常见的收音机,不管是调幅的还是调频的,都是以模拟信号进行传输的,这种信号直接将要传输的信息以幅度或者频率的形式均匀地映射(调制)到负责传输信号的电磁波(载波)上,由于传输的过程(信道)中信号必然是被各种干扰(噪声)的,于是我们的收音机接收到的电磁波转换到声音的形式(解调)时,干扰信号(噪声)也就被一一映射到了声音(噪音)传到我们耳朵中。 但是现在的网络传输,多数基于数字信号。信号都不再是一一映射到频率值或幅度值了,而是仅映射为一个由0或1(两种频率或幅度)组成的编码。我们假定一个最简单的串行通信,在一个时刻,只能有0或1两种可能,假设信道中存在干扰(比如0被干扰到0.2还会被认为是0信号,1被干扰为0.9还会被认为是1信号),接收后的结果也大概率没有出错。其实这也是一种冗余,一种防止出错的冗余。 0, 1, 2, 3:高效的冗余? 我们为了信息的稳定传输,将无限种状态映射到两种状态(0和1)的编码。但是,我们为什么一定要2种状态呢?为什么不能是0, 1, 2三种状态呢?如果这种可能容易实现(电路上),而且人们最开始没有走上普及二进制计算机这条不归路,那么可能我们现在用的都是基于三机制的电脑了[4]。 技术上的难度或者转换的巨大成本,让人类点了”二进制”这条科技树。不过人们虽然没有用3种状态,但是用4种状态或者8种状态的情况倒是有很多,因为这能保证了和系统其他部分的二进制组件兼容。 比如在信息存储中,固态硬盘(SSD)的MLC/TLC技术,就是在每个存储单元中把以前SLC的两种状态(电压)记录信息,转为用4种/8种电压来记录信息,这样做便增加了SSD的存储密度。 (0, 1, 2, 3) + X: 不可避免的冗余 但是,这种4种或8种状态的SSD,虽然提高了存储密度,但它们的存储单元的寿命会缩短,所以制作这种SSD的时候,就一定要在设备内部预留更多的备用存储单元,这也是一种冗余。看来冗余从抗干扰的冗余被迫转为空间的冗余。 当然,我们也可以采用校验/纠错码技术,用更小的空间冗余来保证信息的可靠。但是编码或数据恢复都需要话费额外的计算时间。如果我们比起冗余空间的开销,更不看重数据恢复的时间,那么看起来空间的冗余又转为了时间的冗余。 看来,冗余无法避免,0/1到(0, 1, 2, 3),虽然它们减少了信号抗干扰的冗余,但是最终也无法避免的使用了其他的冗余。X可能是空间,可能是时间,但到今天这个世界中,都会是money,无法预测的money。 扩展阅读 [1] 熵:宇宙的终极规则, http://www.ruanyifeng.com/blog/2017/04/entropy.html [2] 知乎:为什么汉字顺序有时候不影响阅读? https://www.zhihu.com/question/20428571 [3] 知乎:如何从心理学上解释『打乱英语单词首尾字母之外的字母顺序仍然不影响阅读』的现象?https://www.zhihu.com/question/20804531/answer/16237704 [4] 知乎:苏联三进制计算机Сетунь到底是怎样一个计算机?https://www.zhihu.com/question/35937929

“新司机”虽然上路了,但并不知道走了多少弯路,因为还知道有没有走上正路。但上路也快一年了,当然有一些体会,就算是错的,也该想过些什么。如果我什么体会都不写出来,那么这些想法总会在我的脑子中绕啊绕啊的。所以我希望写些出来,也许这样就可以不用刻意提醒自己不要忘记它们,也好专心”开车”。而且我想,车技总是不断积累联系和回看总结的过程,也许我将来看现在自己的体会,不是嘲笑自己的车技太差就是羡慕自己的没有迷路运气。当然还有一种情况,就是我能因这些感想能将其他”新司机”带上(歪)路,我也是很欣慰的。。。 这系列文章,不去反省自己的缺陷,不去叙述自己的经历,只写下当前所总结的体会。本篇博客,我将写一下对存储栈的理解。后边,我还可能从存储组织(文件系统/存储结构)、存储缓存和虚拟化存储等几方面写下体会。 1. 层次化封装 就像OSI网络参考模型一样,存储也是有层次的,如果在网络中我们将这些层次称为网络协议栈(TCP/IP协议栈),那么在存储中我们经常用存储栈(storage stack)或I/O栈(I/O stack)与之对应。 顾名思义,栈这一种直上直下一层叠一层的结构。在网络的层次中,数据链路层关心的是MAC地址,网络层关心的是IP地址,栈中的所有层次分工协作,大家像流水线一样将数据层层传递如下图。 在存储层次中,也有类似的分工。比如以Linux存储栈为例,文件系统主要关心的是文件的组织结构,向上将接口暴露给用户;而文件系统下一层的块存储设备层则关心实际数据的去向,主要和存储设备打交道。下图是一个单机上比较复杂的情况,以一个运行在KVM虚拟机中的MySQL为例。当然如果考虑远程网络分布式存储,应该更复杂。 2. 为什么层次是必要的 除了网络和存储,其实编程语言中封装的这种概念,在我看来就类似这种层次化的形式。比如在很多编程语言中常用的SHA256、MD5等安全Hash算法或者链表、各种常用数据结构的库等。这节的问题就以编程中的封装和层次开发为例进行说明。 对编程语言的库来说,问题在于,既然哈希算法的计算方法都是开源的,数据结构都是固定的形式,为什么不自己写呢?首先想到的可能是自己写费时费力,其次一个被很多人使用的库一般都是由各路大神优化而来,应该比自己实现的效果要好,但最重要的,库是可以复用的。 比如那么当我们在这些其他人实现的库的基础上构建自己的应用的时候,就是在库的封装之上进行的开发,这就形成了库–>我的应用的这样的层次结构。进一步说,如果我们是在其他人的库的基础上开发了更高层的库,比如我在其他人写的二叉树库的基础上开发了用于排序的库,那么我们的封装层次就又涨了一层:二叉树库–>排序库–>其他人的应用。再展开说,我们甚至可以冲破一种编程语言的界限,表示出更完整的封装层次:数字逻辑–>指令集–>汇编语言–>系统调用–>编程语言标准库–>二叉树库–>排序库–>其他应用。 如果只是从左到右或者从上到下画出这种层次图,我们还是无法理解层次的必要性,但是如果我们想横向扩充,这种层次就是很必要的。极端的情况:如果我们所有的层次都重叠在一起,那么也许我想开发一个gui小应用,就要从汇编开始,一直沿着操作系统改到库和应用逻辑,简直不现实,这也是软件工程中耦合和内聚的概念。换句话说,不论我们是靠近硬件还是靠近应用的程序员,我们都可以从自己出发,只要向上层和下层提供不变的接口,那么不论我们在自己的范围怎么折腾,都不会破坏其他层次的工作。 回到存储,我们只有将管理文件的file system和下层的设备驱动通过块设备层和虚拟文件系统(VFS)层解耦,才能在单独编写多中设备驱动不用再去修改文件系统代码,也才能在添加新的文件系统功能的同时不用担心对老的设备是否可以工作。 3. 为什么层次可能影响性能 还是以编程语言为例,很多都认可C语言相对其他语言是高效的,但学汇编的人可能觉得汇编才是最高效的,这都没有错,汇编比C高效,但并不属于高级程序语言,C比Python高效,但是指针这种东西没有谁可以一时半会搞定。实际上,汇编–>C–>Python就可以认为是这样封装起来的。某种程度上来说,我们开发C语言应用可以看成是和开发Python是在一个层次上的。。。对于存储也是一样的,层次总是一层一层网上堆的。堆到越上层,越具有其简单易用的特性,而这种层次化页必然会带来性能上的降低。 现在在我看来,存储层次升高带来的性能损失的根本原因是因为在层次间信息的不对称的同时还有些层次要越层”搞事情”。具体来说,层次之间的沟通通过接口,由于层次间较松的耦合,某一层中无法获知其他层中的具体细节。比如,应用程序可能要告诉文件系统具体要读的文件名,但并不知道文件具体存在什么地址;文件系统可能告诉块设备驱动它具体要读内容的地址,但是块设备层就不知道这内容属于什么文件了。 但是,存储的管理不光“你告诉我你要什么数据,我把数据传给你”那么简单:首先,由于数据的重要性,各个层次都要用自己的机制保证数据的完整性和一致性;其次,也要利用数据的局部性,用dram作为cache来弥合内存和二级存储设备间的性能差异。那么大家都要保证数据可靠,大家也都在加把劲降低IO读写延迟,真的是齐心协力力量大吗? 当然大家都想整个系统变得更好,但是既然层级之间信息不对称,一定会因为缺少合作,而导致自己所做的工作不如预期,甚至起到副作用。我们是需要加强各层间的耦合交互?还是提升各层的“智能”让其揣测其它层的想法?还是应该聘请一个“存储栈独裁程序”来统一发出号令?这块儿学问就大了,还是新司机的我理解并不够深入,车技提升后还要展开多写写。。。 4. 在适当的层次做适当的事 Python认为程序员要指针没啥用,于是没有设计出指针的概念。而可能在一些C语言程序员眼中,Python因此就是一种有缺陷的语言。但是我们可能忽略了一点,就是没有谁禁止过Python程序员学习C语言,也没有人禁止过C程序员学Python。对于一个项目来说,没有最好的语言,只有最合适的。 对于存储层次同样如此,比如同样是数据加密,我们可以在应用程序中加密数据,可以在文件系统中加密数据,可以在数据网络传输之前进行,极端的情况,甚至也可以在硬盘硬件中进行加密。对于cache,我们可以在应用层开辟内存作为cache、或者利用文件系统缓存,或者磁盘的硬件缓存等。选择很多,不同的功能,适合的位置也是不同的,同样和上节一样,这块儿学问就大了,还是新司机的我理解并不够深入,车技提升后还要展开多写写。。。