梦断代码--一个程序员的自白 (二)

25 篇文章 0 订阅
本文谢绝转载
梦断代码--一个程序员的自白 (二)

    AIRMax是一个庞大的项目计划,需要3~5年的时间来完成。这个缩写是取公司的主打产品首字母和特征单词组成的,也意味着这个项目要影响到所有这些支柱产品。公司的这些产品都是不同领域的设计软件。这个项目计划在三个方面做资源整合,即为所有产品提供统一的程序库或者框架:程序外观(GUI),渲染(主要是3D引擎),和文件格式(保存设计成果)。这些产品都是公司的现金牛,都很强势。这就意味着AIRMax是一个极富挑战性的项目,而且需要高超的组织协调艺术才能成功。

    AIRData项目就是这三架马车之一,目标是打算统一文件格式和相关程序库。项目名称中的“Data”表明了项目的性质。后来又简称为ADP,是取Axxx Data Package的意思。但这个简称实际上要到一年后才被普遍使用。ADP项目美国那边有一位架构师总负责,和另外三位程序员。我们这边最初好像也是四个,有一个同事来了没几天就因为没兴趣而去了别的项目组。
    
    在项目一开始的时候,我最想弄清楚的是项目的定位和要解决的问题。我尝试过拿这些问题去问美国的设计师,只能得到没营养的答案。我始终得不到关于项目蓝图的清晰概念,而设计师似乎也不在乎。“统一文件格式和程序库”不是一个合格的答案,它无法给我们的项目树立一个灯塔。在这样的纠结中,我们只好去做那些给出了明确指示的事情。第一件事是了解OPC。这个OPC是微软提出的一个所谓Open Packaging Conventions的文件包组织标准,将在当时尚未发布的Windows Vista上得到操作系统直接支持。Office 2007实际上已经采用了OPC,就是现在经常看到的docx,pptx什么的。

    尽管我只是ADP的普通一小兵,但是我还是希望ADP会是一个成功的项目,希望将来可以为自己的贡献而自豪。从我那些朴素的观念出发,我认为ADP的大方向是正确的,技术上没什么风险或是很大的困难,又是个新项目,还没有任何历史包袱。这样的项目正是许多程序员梦寐以求的,然而还是有两个隐忧让我不放心。其中之一就是文件存储的问题。前面提到我们的文件要遵从OPC规范,简单说,就是一个Zip文件,而OPC规定了Zip内部的文件应该怎么组织。我们的数据将以XML文件的形式打包到Zip中。关于数据XML化的问题,据说公司以前就有过尝试,最后因为性能问题而失败了。我向来对XML就没有好感,除了写一个“能用”的XML解析器对于三流程序员也能做到外,看不出技术优势。当年那些用XML写配置文件的,在我看来都是脑子坏掉了:XML那是给人读的吗?同样的,和写Javascrpt比起来,HTML那罗嗦糟糕的方式不让人觉得痛苦吗?但是很不幸,市场上XML还是有很大优势的,为什么?我不知道。但是我一直恶毒地认为是XML迎合了一大批不懂词法分析器的劣质程序员,劣币逐良而已。和JSON一比,高下立判,JSON至少是人可以读的。扯远了。

    和Office的产品不同的是,我们的软件产生的文件一般都相当大。200M算是常见的大小,如果采用一种很罗嗦的表示方法,文件大小就会膨胀到很危险的程度。另一个问题,这些现有的设计文件是有复杂的内部结构的,比如某产品在其设计文件内就做了类似数据库的东西。如果我们只是简单地把它们转成XML,并压缩放入Zip,除了性能会降到一个不可接受的程度,现有的文件访问工作流也将被破坏。所以,所谓的“统一的文件存储”绝不意味着一个万能的单一文件格式。在我看来,我们至少要做两种文件存储机制,一是供产品工作过程中使用的,高性能的文件存取机制,这种机制是附着在产品所在的机器上的,我们的目标是压榨所在机器上的所有资源获取高性能。另一种机制是设计成果的保存,这种文件是以“交换”为目的的。一个产品生成的文件,要能够在别的产品,别的操作系统,别的CPU架构上读取。这种为交换目的而作的设计,性能要求可以不那么苛刻,但是需要一个紧凑的格式,且能严格向后兼容。

    要解决这两个问题不是很简单,但是如果确立了目标,然后一个个地扫除要解决的问题,也还是很稳当的。但是很可惜,面向性能的那个设计从来没被单独考虑过。ADP实际做的一直是面向交换的那个部分,并企图在其中实现高性能。为了“可交换性”,ADP应该做的事情是为包格式设计一个规范,或者说“协议”。OPC只是解决了包的高层结构,对于具体的文件,如何解释,数据如何存放都是要慎重考虑的。当时对数据(对应到C++中的基础数据类型)的存储有文本和二进制两种格式,那么如何读写他们呢?考虑到“可交换性”背后的含义,如果不做规格设计,存下来的文件必然是千疮百孔,一旦这种含有错误的文件广泛使用后,修复的代价将是及其高昂甚至是不可能。我后来为此写过一个很长的邮件,讲述其中的利害,可惜是泥牛入海。这方面的问题并不会马上暴露,但是在随后的岁月中它一再地折磨着开发和测试。

    除了文件存储,还有一件事是ADP想做的,那就是运行时的数据管理。这个东西在一开始我并不理解。对对象存储,常规的方法不外乎是序列化,ORM,手工读写这些常规方法。但是,ADP没有这么做,而是提供了一个我怎么概括都觉得不准确的方法。让我们站在产品的角度来看一下这个运行时数据管理。对于写数据的过程,产品首先通过ADP的API创建一个数据集,接着创建内存视图,然后把自己对象的数据(和类型)写到视图,最后ADP扫描这个视图,写到外部文件中。读取的过程则相反。对于同一个逻辑数据,比如Point,各个产品当然都是早就有了的,而且因为代码不相通,显然在C++中,A产品的Point和B的不是同一个东西,甚至还有各不相同的方法。但是从数据模型的层面来说,这两个Point又是一样的,都是表示空间中的一个点,都有float型的x,y,z。这就意味着ADP无法写出一个和所有产品都一样的Point。所以,ADP只能是定义一个自己的Point,而各个产品自己再把ADP的Point转换成他们的。这么看,ADP的数据类型只是个中间产物,如果所有产品都能服从相同的Point的存储方式,实际上无需ADP的内存块那一层,直接把文件数据读取并转换成它们的数据就行了。ADP管理对象纯属多余,多做了一层,既慢还浪费内存。我当时第一时间就认识到这是一个浪费,但是也觉得ADP中似乎只能这么做。也就是说直觉和经验告诉我有问题,但是没找到问题根源是什么。

    ADP还定义了一些基础数值类型,对应到C++的基础类型。对于一个内存块,就看作是数据的连续存放。这样,用户在访问数据时,要知道需要的数据在内存块的偏移量和类型,然后ADP的函数帮助读取出来。这种读取方式当然是很不方便且相当危险的。因为缺乏对数据类型和布局进行描述的元数据,这要求应用程序对数据必须有精确的掌控。当时对那些固定长度的数据类型都已经写好了,但是有一种数据类型,字符串,因为长度不固定,所以还没弄好。怎么放字符串呢?这就是美国的O同事所谓的“challenge”。

    当时有两个显而易见的选择。一个是把字符串就写在内存块中,在前面标一下字符串长度就行了。另一个选择是,存一个指向new出来的字符串数组。两者各有优劣。考虑一个含有字符串数据成员的C++struct,第一个方案的好处是数据连续存放,数据复制很友好,memcpy就行。缺点是不能简单地定位特定数据成员在内存块的偏移了,因为字符串是变长的,必须从头开始扫描,才能计算出特定数据成员在内存块中的偏移。而后一个方案则相反,在内存块中定位数据成员仍旧容易,但是要多出一个分立的内存单元,memcpy显然不能工作了。一方面,ADP希望能支持memcpy写出数据到文件,另一方面,又想能简单定位数据成员。这种两难就是“challenge”的来源。我选择了第二种方案。

    选择第二种方案的理由之一是用户访问数据的方式已经很差了(麻烦且危险),不能再复杂了。另外,memcpy用在IO的过程中,IO本来就慢,多做点处理也影响不大。还有一个隐含的理由就是,ADP内部实际上是必须知道内存块中对象的布局的,否则在读取的时候,就不可能从文本重建出那个内存块。只是ADP当时还没有认识到这一点,所以也没有相关的API和专门的抽象机制,但是实际上是做了这个事的。我当时认为未来做数据布局乃至类型的抽象是必须的,那么现在在此假设上工作就不算浪费。于是我加入了字符串,实际上就是C++的字符数组的支持。然后小心地处理好和文件的读写、内存的复制,分配和释放问题,就觉得工作差不多完成了。一周后,我把结果发给了美国的O。

    另外的中国同时当时好像在设计IO的实现。美国的设计师G好象是打算用istream/ostream接口来访问数据,可是众所周知,这是一个恐怖的接口。除了istream,ostream之外,还有对应的streambuf接口,还有localization支持等等。所以G要求用boost的iostream来实现。这又是一个不面向问题,却面向技术的选择。这个东西不是我来做,反正我的记忆中是怎么弄也没能好到符合预期的程度(好像主要是性能问题,@托尼点儿张 来补充吧,我记不清了),最后则完全从代码中消失了。

    于此同时,我也积极地读那些已有的ADP代码,发现所有的基础设施都是从DWF项目中复制过来的,未作丝毫改进,我的心凉了半截。那些DWF中存在的问题,现在依然存在。所幸的是DWF的string类型没有复制过来,看来我上次的报告还是起了一点作用的。这也让我升起了写一个全新的string的念头,因为对ADP类型来说,string类型太重要了,我从没想过要工作在C的字符串上。出于对代码质量的不满意,我打算接下来在这方面做点前期的工作。

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 14
    评论
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值