【悦读】松本行弘眼里的代码未来
代码的未来,松本行弘在2013年所著,该书对云计算、大数据时代下的各种编程语言以及相关技术进行了剖析,并对编程语言的未来发展趋势做出预测。
以下是我阅读这本书的笔记摘抄,这本书能提高程序员的素养。
第一章
对摩尔定路的局限的思考
在不远的将来,会出现一些因素,对摩尔定律的继续生效构成障碍
- 最近的LSI(大规模集成电路)的宽度已经缩小到只有数十纳米,只能排列几个原子,这样在原子尺度上来制造电路是相当困难的
- LSI中的电路是采用一种印刷技术印上去的,光的波长成了问题,如果图像的尺寸比光的波长还小,就无法清晰地转印。可见光的波长范围约为400-800nm,因此最近45nm制造的LSI是无法用可见光制造的,而是使用X光代替
- 在这种原子尺寸的电路中,保持绝缘也是相当困难的。电流通过了原本不该通过的地方,这种漏电流还会引发发热,LSI越来约精密,其密度也越来越高,热密度也随之提高,如果没有充分的散热措施,连LSI本身都会溶化
漏电流和热密度等问题,CPU的性能提高遇到了瓶颈。多CPU化,内存容量增大,SSD硬盘会成为主流,真正的并发编程也会成为主流
算命先生预言的原理
1.2节
巴纳姆效应(福勒效应)
每个人都会很容易相信一个笼统的、一般性的人格描述特别适合他。即使这种描述十分空洞,仍然认为反映了自己的人格面貌,哪怕自己根本不是这种人
冷读术(cold reading):通过观察对方的言行举止中的一些细微之处来进行揣测,了解到他人的心理活动。像福尔摩斯对他的委托人所运用的那种技巧差不多。
热读术(hot reading):通过事先对对方进行详细的调查,藉时机说出对方的情况,此时就会显得神奇无比。
IT未来之预测
未来应该是位于过去到现在这个方向的延长线上
从价格看未来
- 普通人所能拥有的计算机性能将比现在大大提高
- 现在还没使用计算机的地方,以后都会安装上计算机
从性能看未来
看看现在的超级计算机,再过20年,就是现在的一般般的配置了,比如数万CPU,数十万核心配置会成为普遍
基于这样的环境,编程又回编程什么样子呢?为了充分利用这么多的CPU,软件开发又会如何进化呢?未来的编程语言应该在如何利用CPU资源这个方面进行争夺,并行处理的功能。
从容量看未来
在不久的将来,由超高速低容量的核心内置缓存,高速但断电会丢失数据的主内存(RAM),以及低速但可永久保存数据的外部存储器(HDD)所构成的结构将会消失,取而代之的可能将是由大规模的缓存,以及高速且能永久保存数据的内存所构成的新层次结构。相应的依赖过去存储结构的数据库系统将产生大规模的结构改革。
从带宽看未来
1G bit/s的上网服务已面向某些国家的家庭推出了
在性能和带宽寻求平衡的过程中,网络彼此两端的系统构成也会像钟摆一样摇格不停,随着每次钟摆的来回,系统的规模,拓展性和自由度都能够得到提高。
第二章 编程语言的世界
世界上第一位程序员实际上是一位女性
机器语言就是一串数字,将计算机的步骤从指令表中查出对应的机器语言编码,再人工写成数列。(机器语言和CPU执行之间至少还有一层:指通过指令表找到并输入对应的机器语言,机器就能执行一条条指令,指令让机器产生计算和处理)
查表这种工作本来应该是计算机最擅长的,人们用更加容易记忆的指令(助记符)来代替数值,并开发了一种能够自动生成机器的语言程序,这就是汇编器。汇编器用来解释汇编语言的程序,汇编语言中所使用的助记符和计算机指令是一一对应的关系。
也就是说汇编语言是由编程者自己的需要发明出来的
编程语言的进化进程看,一个显著的关键词就是“抽象化”。随着抽象化的不断深入,程序员即便不去关心内部的详细情况,也可以编写出程序。人类一次所能掌握的概念的数量,有说法称,大部分人一次只能驾驭5到9个左右的概念。抽象化程序高的编程语言不必描述详细过程,从而可以用简短的代码达到目的。
我的思考:
语言的发展:机器语言--》汇编语言---》高级语言(比如Java,Python),现在几乎程序员都处在使用高级语言上;高级语言经过编译器执行编译处理转化成汇编语言,再经过汇编器和链接器进行汇编和链接处理转化成机器语言,被计算机执行。而现在,编译器和汇编器和链接器都已经很成熟了,程序员在高级语言阶段写代码就可以了
一方面,汇编语言虽然速度快,但是在工业化领域,一个系统用汇编语言,代码量将是极为庞大的,我推测有百万行的代码了。而高级语言,可能只有不到一万行。另一方面,编译器和汇编器非常完善了,引入了很多机制,保证最好的还原。同时,在现在CPU 达到2GB的能力下,高级语言编写的代码带来的损耗完全可以被忽略
未来编程语言
格雷厄姆主张,100年后的编程语言的进化主线应该是以最少量公理为基础的“拥有最小最简洁核心的语言”,在现有编程语言中,最具有这一特征的莫过于他最喜欢的Lisp了,Lisp是100年后编程语言的进化方向。
松本行弘认为,编程语言的进化动机,不是工具和语言本身的简化,而是将通过这些工具和语言所得到的结果更简洁地表达出来。抽象化的趋势会一直持续下去
100年后的编程语言有这三种可能:
- 可能性1:变化不大
- 可能性2: 使用编程语言来编程这个行为本身不存在了,通过人机对话等方式
- 可能性3: 发明了更高抽象度的编程语言。是比现在更加强调what,而对如何解决问题的how部分的细节,则不需要人们去过问。
20年后的编程语言:
编程语言本身不会发生多大的变化,实际上现在使用的很多语言,在20年前就已经存在了。
20年后的语言,应该是在分布处理和并行处理功能上进行强化,使得开发者不需要特别心思就能使用这些功能。
线程,RPC(远程过程调用)等显式地使用分布处理和并行处理的形式,早晚会遇到瓶颈,当核心数超过数千个的时候,显式地指定就变得毫无意义了。未来的编程无需显式操作就能实现分布处理和并行处理。
DSL(特定领域语言)
所谓DSL(Domain Specific Language),是指利用为特定领域所专门设计的词汇和语法,简化程序设计过程,提高生产效率的技术。是对特定目的的小规模语言的称呼。
DSL的优势:拥有为特定领域所设计的词汇,可以在高级层面上编写代码,节约程序开发的时间。即,不涉及对象领域的内部详细,而是在高级层面上进行描述。
外部DSL
由专用语言引擎来实现的DSL,称为外部DSL。比如,数据库访问使用的SQL就是一种典型的外部DSL。正则表达式用来描述字符串模版,也是一种外部DSL。
外部DSL的优点:它独立于编程开发所使用的语言之外,在不同语言中都可以使用。实际上是全新设计的语言和语言引擎,不必被特定的执行模块和现有语言的语法所左右。
内部DSL
不是创造一种新的语言,而是在现有语言中实现DSL,而作为DSL基础的这种现有语言称为宿主语言。Lisp,Smalltalk,Ruby这些语言适合作为DSL的宿主语言。
内部DSL的优点:内部DSL借用宿主语言的语法,在理解内部DSL含义时,宿主语言的常识依然有效,可以使用宿主语言所具备的全部功能。
元编程
程序在运行过程中,本身的信息可以被访问和操作,就是元编程。程序由数据结构和算法构成,如果环境允许程序本身作为数据结构来操作的话,那么元编程也就和面向一般数据结构的一般操作没什么两样了。
内存管理与GC
根判断对象是否可被引用的起始点
GC算法
标记清除(Mark and Sweep)
从根开始将可能被引用的对象用递归的方式进行标记(通过对象内部的标志来实现),将没有标记的对象视为垃圾进行回收。标记清除原理详细介绍
缺陷:
- 每次清除都需要全局扫描一次,当在分配大量对象时,只有一小部分存活的情况下,所消耗的时间会大大超过必要的值。
- 垃圾收集后有可能会造成大量的内存碎片
标记压缩
将没有标记的垃圾对象不断压缩
优点:能够有效的缓解标记清除算法回收对象造成的内存碎片问题
缺陷:它引入了额外的开销,比如说额外的空间来保存迁移地址,需要遍历多次堆内存等
复制收集
将从根开始被引用的对象复制到另外的空间中,然后再将复制的对象所能够引用的对象用递归的方式不断复制下去。如果旧空间里都是垃圾对象,能一次性回收该旧空间
优点:和标记压缩相比,它不需要遍历堆内存那么多次,节约了时间
缺陷:可用堆内存减少了一半
引用计数方式
在每个对象中保存该对象的引用计数,当引用发生增减时对计数进行更新。引用计数的增减一般发生在变量赋值,对象内容更新,函数结束(局部变量不再被引用)等时间点。当一个对象引用计数为0时,则说明它将来不会再被应用。
优点:
- 对象不再被引用的瞬间就会被释放
- 释放操作是针对每个对象个别执行,GC产生的中段时间比较短
缺点:
- 无法释放循环引用的对象
- 必须在引用发生增减时对引用计数作出正确的增减,如果漏掉了某个增减,就会引发很难找到原因的内存错误。
- 引用计数管理不适合并行处理
分代回收(Generational GC)
对分配不久,诞生时间较短的年轻对象进行重点扫描,可以更有效地回收大部分垃圾。对所有涉及修改对象内容的地方进行保护,称为写屏障。
刚刚生成不久的年轻对象划为新生代,存活时间较长的对象划为老生代。
全部区域对象GC操作称为完全回收;只扫描新生代对象的回收操作,称为小回收(Minor GC);将从老生代对新生代的引用记录在一个叫做记录集的表中,在执行小回收的过程中,这个记录集也作为根对待。
缺点:
- 最大中断时间没有得到改善
增量回收
对实时性要求很高的程序中,比起缩短GC的平均中断时间,往往更看重GC的最大中断时间。GC操作细分成多个逐一执行。
写屏障:已经完成扫描和标记的对象被修改,对新的对象产生了引用,这个新对象就不会被标记,明明存活的对象却被回收掉。
并行回收
原有的程序运行的同时进行GC操作,用写屏障对当前的状态信息保持更新。不过,让GC操作完全并行,而一点都不影响原有程序的运行,是做不到的,在GC操作的某些特定阶段,仍然需要暂停原有程序运行
任何一种GC算法,都是跟踪和引用计数回收两种思路的组合。
为什么需要引入异常处理
- C语言,if-else方式,通过返回整数,实现繁琐
- 有多处同样的错误,则要重复实现
闭包
函数对象:将函数作为值来利用的方法
闭包:在函数对象中,将局部变量这一环境封闭起来的结构被称为闭包。被封闭起来的变量的生命周期与封闭它的函数对象生命周期相同
对象是在数据中以方法的形式内含了过程,而闭包则是在过程中以环境的形式内含了数据
动态与静态
改善JavaScript性能的技术:
- JIT(Just In Time)
- 特殊化;对高频的特殊条件下,运行高速版本的代码
JavaScript是目前最快的动态语言
无论任何程序,或多或少都包含了动态的特性。
动态的编程语言,动态部分主要指运行模式和类型;动态运行模式就是运行中的程序能够识别自身,并对自身进行操作。动态类型有且只有数据拥有类型,静态类型是数据拥有类型,存放数据的变量,表达式也拥有类型,且类型在编译时固定。
动态类型优点:
1.简洁;即少了类型描述的代码和减少与算法本质无关的代码分量
2.灵活性高;少了类型带来的制约
静态类型优点:
1.程序中对类型的描述,可以帮助对程序的阅读和理解 2.编译时确定类型,比较容易发现bug,对于这一点,程序中的bug大多数其实是与逻辑有关的,单纯类型错误而导致的bug是少数的。
Duck Typing
If it walks like a duck and quacks like a duck, it must be a duck.
隆重推出鸭子类型,如果某个对象的行为和鸭子一模一样,那无论它真正的实体是什么,我们都可以将它看做一只鸭子。不考虑某个对象到底是哪一个类的实例,只关心其拥有怎样的行为。
小结
松本行弘的代码的未来一书还有其他有趣的内容,我想每个程序员都需要知道未来编程是什么样子,知道如今的编程语言正在朝哪些什么方向发展,Go,Lua,Dart这些语言有哪些设计差异。
欢迎关注CodeThings公众号