消息终于来了
这个难熬的四月,到上周五晚上11点,终于结束了。命令在当晚7点多到了学校,而且没有再节外生枝。在这件事情上,我不再想看到任何支线情节。
这本来应该是20多天,不,应该是两年多以来睡得最踏实的一晚,但我那天几乎彻夜未眠。
新的旅程终于可以开始了。
这个难熬的四月,到上周五晚上11点,终于结束了。命令在当晚7点多到了学校,而且没有再节外生枝。在这件事情上,我不再想看到任何支线情节。
这本来应该是20多天,不,应该是两年多以来睡得最踏实的一晚,但我那天几乎彻夜未眠。
新的旅程终于可以开始了。
本章并不是想对C语言进行致命打击。C是一门神奇的编程语言,具有许多优点。它是一种非常流行的实现语言,被许多平台所选用,而它确实也值得人们如此看重。但是,正如我的祖母曾说过的那样,当在超导条件下进行超级碰撞时不可能连一个原子也不撞碎。所以在欣赏C语言的优点时也不要忘了分析一下它的缺陷。总结——进步是计算机软件工程和编程语言设计艺术逐步发展的重要动因。这也是为什么C++语言令人失望的原因:它对C语言中存在的一些最基本问题没有什么改进,而它对C语言最重要的扩展(类)却是建立在脆弱的C类型模型上。所以,本着改进未来编程语言的探索精神,让我们对C语言进行望闻问切,详细记录病例。
本段摘自Peter Van Der Linden所著,徐波翻译的《C专家编程》,人民邮电出版社,2008年第二版第28页。我喜欢这段话。
在经历了反复的思想斗争之后,我还是在老婆的不断催促下决定卖掉我的Thinkpad T60,但我真的十分不舍。
昨天,我把它从柜子里拿出来时,上面有一层薄薄的灰尘,但用3M布轻轻一掸就光洁如新了,就像2007年刚买回来时一样。从2007年到2012年MBA成为主力机之前,它一直是我最稳定可靠的工作伴侣,性能下降曲线十分平缓,说明了小黑硬件设计师和工程师们的深厚功力。而它也是最后一代拥有IBM血统的小黑,尽管上面已经开始在屏幕下方和电源上出现了恶心的Lenovo标记,但那时它还仅仅是个标记,暂时没有如T61那样深度腐蚀Thinkpad的肉体和精神。从这点上说,我觉得非常幸运。
然而,再顽强坚固耐用的电子产品也难逃贬值到报废的魔咒,西门子和诺基亚的手机如此,Thinkpad也一样。尽管它陪伴了我的整个博士阶段,从中国到德国再回到中国,依然运行良好,依然被我擦拭的一尘不染,但它还是已经老去了,该退出历史舞台了。
为了延续它的生命,我早就为它换装了Ubuntu。当我昨天时隔将近两年再次启动它时,它没有任何抱怨,进入系统依然如丝般顺滑。但既然已经决定和他告别,就要让他有尊严的离开,备份了重要的数据,全新安装了Ubuntu 14.04.4,然后关机,缓缓合上了盖子。
上闲鱼,卖货。
描述“事件”,如果是仅用自然语言,那么似乎是小学生就应该可以办到的事情。但如果想要结构化的去描述一个事件,却不容易。比如这个事件:
昨天我们一家一起上午9点半驱车前往长沙县江背镇,在油菜花田里走了一会,然后在江背镇吃了午饭,之后又到乌川湖水库看了看,然后下午3点半返回长沙。
这件事情里面涉及到了至少两个时间点,两个层次中的三个位置(都在江背镇这个粗粒度层次,但具体包括了一处油菜花田和一处乌川湖水库两个细粒度层次),以及驾车和步行两种交通模式,还有两条不同的驾车线路(去的时候是走长浏高速然后一直到江背出口才下去的,而回来的时候是先走了一段省道,然后从永安上高速回的长沙)。这个前后仅用时6小时左右的旅行都包含了这么多细节,更不用说那些时间跨度更长的旅程了。
就昨天这条事件来说,如果我用“时间、地点、人物、事件”来描述,那么就会遇到粒度控制的问题。说白了就是这个描述可繁可简,我既可以说我们仨去了趟江背镇,也可以像上面那样把它详细的描述出来。因为事件本身就是多尺度的,我们既可以非常粗线条的勾勒出事件的大致经过,也可以一秒一秒的逐帧观察。到底需要哪种详细程度,就看使用目的了。
过年的时候,知乎上对年的讨论自然也会多起来。在现在这个80后基本掌握主流话语权的时代,出现诸如“年味为什么变淡了”之类的问题也会让我很有共鸣。在同类问题下的诸多回复中,我看到了一个大体意思是这样:
年就不是给我们这个年纪的人过的,而是给小孩和老人过的。我们的任务就是多做家务,帮老人分担负担,让小孩开心。
我深以为然。所以说,现在过年回家压根就不应该想着怎么休闲,怎么玩,怎么放松。ALL CRAP! 干活就是了。但我们的那些个同龄人,似乎还没有都看明白这个道理呢。
我没有考证过嫁闺女的时候娘家大规模要彩礼到底是我国古已有之,还是近年来才出现的新风尚。但我觉得,把男方家人叫过来,然后告诉他们说:“我们这里的习俗就是这个过程总共分为n步,第1步需要提供m样东西,分别是w箱A、x箱B、y瓶C和z斤D,一万〇一(表示万里挑一)的见面礼等等,另外还有6万、8万、10万、20万…上不封顶的礼金自己看着办,然后是第2步…”然后进一步表示这还是简单的,如果是乡里那更厉害。
我在观察他们提需求的时候,感觉到的是这样一种心态:我们这黄花大闺女是天下第一好的姑娘,要嫁给你可没那么容易,没过个五关八关的你休想从我们这娶走我们闺女。
但让我感觉无力吐槽的是,上面说的那些个ABCD之类的,都是些一听起来就会有“这都是些神马奇奇怪怪的鬼!?”的感觉。
婚姻本是两个人你情我愿的事情,但在好事将近时却突然蹦出一帮关系不大的人在这里大谈各种条件门槛。这背后透着一股深深的自卑情绪,这种深入骨髓的自卑一定要通过一些物化的东西去弥补,于是就出现了ABCD。然后借着这些ABCD,可以让提要求的一方产生一种我们就是壁垒森严坚不可摧居高临下舍我其谁的莫名优越感。然后再美其名曰:这就是我们这里的规矩。
但在我看来,除了一股子浓浓的土气和欲盖弥彰的自卑以外,什么都没有。这也是我深深厌恶的所谓传统之一。
全球化的时代,经济好与不好都是联动的,很难有哪个国家或地区能独善其身。区别只是在差的时期谁能不那么差;在好的时期谁能更好而已。而且不仅是经济层面,大众情绪、社会思潮都会与之联动,这些再进而影响政治,逐渐演变成波及世界各地、影响各个社会阶层的历史大事件。这是在宏观层面。
而在微观层面,每个工作团队、每个家庭、每个个体的情况又会表现出巨大的差异。他们有的可能深受宏观层面事件的影响而随之潮起潮落,也有的似乎事不关己,还有的甚至能逆势而动。
有趣的是,无数这些微观的个体又共同组成了宏观的图景。然而,在这幅壮丽图景上,又有哪些微观个体能跳出自己那方寸之地,以上帝视角看清整体的样貌,甚至是大潮前进的方向呢?
这个工作缘于我在指导实验室今年的一篇硕士论文时蹦出来的一个想法。本来,我是想在这篇论文中把CH应用到mmrp中,通过减少IO来提高mmspa的整体性能。但后来我发现我想错了,因为我错误的理解了CH。CH并不是把道路网进行类似影像金字塔那样的多层次组织,尽管它的名字叫“分层压缩”,而是通过在原始网络中增加很多的“快捷边”,让后续的路径搜索算法可以“绕过”很多不必要去考察的顶点,从而大幅减少松弛操作的次数,达到加速最短路径搜索的目的。当然了,这种加速是有代价的,那就是在事先寻找、确定并建立这些“捷径边”所需的时间开销,以及增加了这些捷径边所带来的空间开销。
虽然应用CH来改善mmspa的IO看来暂时无望,但我在去年年底又想到了另一个可以改善目前每次计算都慢得要命的办法,尽管有点简单粗暴。那就是在程序启动时就把所有mode的图都读进来,然后后面针对每次routing plan进行graph装配的时候都不要再去访问数据库了(那个读graph的sql着实费时),而是从内存里的这个graph cache中取数据,装配成临时的graph集合,然后计算结束后再释放掉这些临时图。
顺着这个思路,我就开始着手于去年12月中旬实现这个feature,顺便也把mmspa这个老库好好整理了一下,动了动手术。最终在1月5号发布了2.0版本的mmspa,在增加了内存开销的代价下,把单次路径规划的性能提升了大约10倍,但到了http服务的层次,这个提升被打折到了5倍左右。
这次改造,对代码的结构进行了大幅调整,但仍然保留了原来的主要实现方式,没有动基础的算法和数据结构。尽管如此,过程也并不是一帆风顺。最主要的障碍就是装配公交系统graph,因为按照新方法组装起来的graph中的vertex不再是按照id排序的。所以只能又加入了一个快排函数,专门为公交graph进行顶点排序。这里费了一点时间。最后,用valgrind彻底清理了一遍所有内存泄漏的问题,确实找到了好几个,而且其中还有当年留下的千年老bug。昨天,我又发布了一个2.1.0版本,删除了对原来1.x版本的接口兼容(因为我知道受影响的只有我自己的pymmrouting),但也保留了能兼容1.x版本接口的2.0.1版本。
至此,虽然还有很多可以改进的东西(都写在issues里面了),但这次的工作暂时告一段落。路径规划,multimodal路径规划,是个永无止境的工作,而且就如杨剑说的,后面的优化工作量会非常大,而带来的收益会越来越小。这也算是一种边际效应递减吧。不知道今后是否还有机会继续3.0的工作,抑或把它变成未来某块工作的基础,那也算是幸事一件。
跨年的时候,我扫了一眼很久没打开的朋友圈,不出所料的发现很多人都在尝试用一两句简短的话总结和挥别2015,再用一两句话寄语2016。为了赶在那一两分钟之内把消息发出去,也只能写的很短。我当时也想写两句,但发现好难。而且,没有仔细的思考,很难客观的评价自己过去的这一年,更难一下子规划出新一年的目标。
跨年,尽管只是一个瞬间,但应该用一段更长的时间沉下来,反思2015,规划2016,就像过去每年这个时候一样。还有一点,2016对我和我们全家来说,必将会成为一生中的重大转折点。
2015,我33,2016,老婆33。常言道:三十三,大转弯。看来,要想全家一起大转弯,必须得从2015开始,然后在2016完成。
其实重构mmspa的想法早已有之,因为那是我2009年4月份开始写的库,后面在2010年集中开发Multimodal Route Planning System for Munich的时候基本就不再动它了,主要是有一种怕一碰就不能用了的心理在作怪。直到去年把上层的wrapper重新用Python写成pymmrouting的时候都没敢大动mmspa的代码,只是稍微添加了几个函数。但实际上,它的代码已经too(不) ugly(忍) to(直) read(视)了,而且现在又有了个改进效率的想法作为契机,那就做个大扫除吧。
之前的代码问题很多,要列清单可以列很长,这里就不详述了,当自己重构的动作提起速度来的时候也是脑子中几条线同时前进,有的问题随着发现就随着解决了,可能都没来得及反映到commit comments中。这里只提纲挈领的记几个要点:
parser
(负责从数据库里读取数据并装配multimodal graph set)、multimodal-twoq
(负责最短路径算法)和mmspa4pg
(一个…大杂烩,里面既有routing plan的创建,还有最终路径的获取与销毁函数)。这样的组织显然混乱,所以把代码打散并重新组织成了routingplan
、routingresult
、graphassembler
、mmtwoq
和mmspa4pg
这几个部分,数据结构主要都定义在modegraph.h
中。结构比之前清晰了很多graphassembler
,也就是之前parser
中的几个超长函数,提取出了一票小函数extern
、static
等,用以理清全局、局部、公有和私有的关系这次的重构过程也是一次对C的再学习,《K&R》和《C Interfaces and Implementations》帮了很大的忙。特别是K&R,尽管只是薄薄一本,但其实信息密度很高,在里面总能找到自己的C知识短板。
重构后的版本作为1.1发布了,当然距离一个真正好用的库还差很远,不过总归是迈出了有意义的一步。
下一步,就是2.0。它最大的特点就是会比之前的库快,效率的提升会主要体现在Multimodal Route Planner for Munich的demo中。敬请期待。