2008年11月8日星期六

计算机组成学习笔记(5)--RISC 和CISC

人们很早就发现,虽然复杂的指令单条执行的时间要长一些,但是却能使整个程序运行的更快,原因在于:执行指令的步骤有时可以重叠,或者用不同的硬件并行执行。所以高性能计算机的指令一般要多一些。到了20世纪50年代后期,当时占统治地位的IBM意识到支持一个所有计算机都执行相同指令的计算机序列是十分重要的,无论是对IBM自己还是IBM的用户都具有很多的优势,于是IBM引入了体系结构这个属于来描述这一个层次的兼容性。他们希望同系列的计算机的体系结构相同,可以用不同的实现的方法来执行同样的指令,只是在价格和速度上有所不同。

但是如何是低成本的计算机可以执行高价高性能的计算机的复杂指令呢?答案就是Cambridge的Wilkes在1951年提出的关于解释的理念。这个理念成就了IBM System/360体系结构的一系列兼容计算机,从通过解释来执行复杂指令的低端机到不通过解释直接由硬件实现复杂指令的高端机,IBM形成了一条完备的产品线。

解释执行指令的简单计算机有很多的优势。首先,可以在解释过程中改正指令实现的错误,甚至是补偿基础硬件中的设计缺陷;其次,可以以最小的代价来增加新的指令,甚至是在计算机发货之后也可以做到这一点;再次就是结构化的设计使对复杂的指令有效的进行开发、测试和编制文档成为可能。

到了20世纪70年代的后期,人们发现几乎所有的复杂的指令都可以被解释,设计者于是开始试图弥补高级语言和机器语言之间的“语义代沟”,几乎没人想到设计一台简单点的计算机,就像现在很少人去研究设计功能简练些的操作系统一样。

到了80年代,Berkeley的一个研究小组开始研究不用解释器的超大规模集成电路CPU芯片,并首次创造了RISC这个概念,并将他们的CPU命名为RISC I,接着又推出了RISC II。这种处理器和当时的商业处理器有着很大的区别,新的CPU并不存在与过去的产品间的兼容性问题,而且它可以自由选择新的指令集从而最大限度的提高系统的整体性能。由于设计的初期就强调选用可以快速执行的简单指令,大家很快意识到设计出可以快速启动的指令才是提高性能的关键,每秒钟能启动的指令的条数要比单条指令执行时间更重要。

实际上RISC是Reduced Instruction Set Computer的缩写,而与之对应的CISC则指代Complex Instruction Set Computer。RISC的支持者认为 即使RISC需要4条或5条指令来完成CISC的一条指令的功能,但是如果RISC能比CISC快10倍的话(因为RISC并不需要解释执行),那么还是RISC的速度占优。另外,随着主存的速度越来越快,解释执行的代价已经高了很多。

也许有人会想,依靠RISC在速度上的优势,RISC机(Sun的UltraSPARC)应该已经在市场上超出CISC机了(Intel的Pentium),但事实上并非如此,这是为什么呢?

首先由于向后兼容的问题,要考虑到N多的公司在Intel系列的CPU上投资的数十亿美元的软件,同时要注意到,Intel在它的CISC架构中也采用了RISC的思想,从486开始,Intel的CPU就包含了RISC核心,能在单个数据通路周期中执行一些最简单也是最常用的指令,同时复杂的指令还是用CISC的方式执行的,这样一来,常用的指令执行速度很快,而不常见的指令执行就会慢一些。显然这种混合的方案不如纯RISC方案快,但是它却可以在不加修改旧程序的前提下给出极具竞争力的整体性能。

2008年11月5日星期三

设计模式解析-学习笔记(1)--引言

其实在大三的时候,就已经听说过Design Pattern这个概念了,只不过当时只是知道有这个名词的存在,而并不知道它的具体含义。

后来搞了一本Head First Design Pattern,断断续续的看了前几章,由于当时并没有实际的编码经验,所以看到Command Pattern的时候就卡壳了。在那个时候只使用过Singleton Pattern,而且用的还是那种最原始的实现方式。至于Factory Method啊,Strategy Pattern这些东西,只是在和同学扯淡的时候提提而已。

大三结束后就是实习了,实习让我有了从来没有过的编程经验,我敢说我大学前三年编的代码的总和也超不过5k行,而实习第一个月怎么也得突破10k了。可见我平时在学校的腐败生活… 面壁去。

随着实习工作的不断深入,接触到的东西也越来越多,我惊奇的发现在编写代码的时候,我开始领悟到一些设计模式的理念,比如子类延迟初始化、依赖倒置、抽象算法族、封装变化之类的思想。而且也发现,客户的需求总是在变化的,自己编写的代码灵活度太低,添加一点功能就得修改一大片,根本满足不了要求。

于是我又回想到了设计模式、敏捷开发这些曾经接触过的名词,正好在公司有几天不是很忙,就在闲暇的三天看完了Judith Bishop的那本C# 3.0 Design Patterns并敲完了上面大部分的代码,总算是对3大类23种设计模式有了一个初步的了解。

然而感觉这样有点太快了些,必须得承认自己的实力实在有限,短时间还没办法把设计模式这玩意彻底贯彻成大脑里的一种编程思想甚至是习惯,于是决定再来一遍,当然这次就不能再看那本书了(C# 3.0 Design Patterns这本书可以让人短时间了解设计模式在C#中的应用,但要提及思想的话,就差的有点远了),挑选了下,决定还是看这本设计模式解析第二版,毕竟是03年Jolt的得主。

在一开始,我先总结下自己现在对优秀的代码的理解吧:高内聚,低耦合,易扩展,易追踪错误,易读。而且我一直认为就像经济学、生物学这些学科一样,编程也是分微观和宏观的,可以把微观的编程看作为对具体编程语言的理解和应用,而把宏观的编程看作为对实际工程项目中对象的抽象和建模。不知道在看完这本书后这个理解会不会有变化。

引言到此结束,下把开始正式的内容。

2008年11月4日星期二

CLR via C#学习笔记(5)--System.Object和new

CLR via C#的第二章和第三章讲了大量的生成和部署.Net应用程序的知识,里面涉及到了大量我从来没接触过的知识,于是决定先跳到和自己现在工作联系比较紧密的第二部分,也就是类型的基础,回头再看什么强名称、程序集之类的内容。

.Net运行库要求所有的类型都最终从System.Object类型派生,所以,可以保证每个类型的每个对象都拥有一套最基本的方法,下面是System.Object提供的方法。

首先来说说它提供的公共实例方法:

Equals:如果两个对象具有相同的值,则它返回true。

GetHashCode:返回调用该方法的对象的值得一个哈希码。

ToString:默认情况下,返回的是该对象类型的完整名称(this.GetType().FullName)。然而通常需要重写这个方法,使它返回一个更有意义的字符串对象。

GetType:返回由Type派生的对象实例,它表示用于调用GetType对象的类型。返回的Type对象可以和反射类配合使用,从而可以获取与类型有关的元数据信息。需要注意的是这个方法是一个非虚方法,这样可以防止某个类重写该方法,并隐藏实际的类型,从而破坏类型的安全性。

然后说说它的受保护方法:

MemberwiseClone:这个非虚方法可以创建类型的一个新实例,并将新对象实例的各个字段设置成与this对象实例的字段完全一致。注意它返回的是对新实例的一个引用。

Finalize:在GC判断对象该作为垃圾收集之后,在对象的内存被实际回收之前,会调用这个虚方法。需要在回收之前执行一些清理工作的类型(比如SqlConnection、FileStream)应该重写这个方法。

CLR要求所有的对象都需要使用new操作符来创建对象,比如下面的语句:

Employee e=new Employee("ConstructorParam1");

接下来说说new操作符所具体做的事情:

1,首先它计算类型及其所有基类型(最高到System.Object,虽然它并没有定义自己的实例字段)中所定义的所有实例字段所需要的字节数。堆上的每一个对象都需要一些额外的成员——称为"类型对象指针(Type Object Pointer)"和"同步块索引(Synchronous Block Index)",这些成员将由CLR来管理对象,这些额外成员的字节数会加到对象大小上。

2,之后它从托管堆(Managed Heap)中分配制定类型所需的字节数,从而分配对象的内存,分配的所有字节都设为零。

3,然后初始化对象的"类型对象指针"和"同步块索引"成员。

4,调用类型的实例构造方法,向其传入在new调用中制定的任何实参(也就是"ConstructorParam1")。大多数编译器都在构造方法中自动生成代码来调用一个基类的构造方法。这样在调用每个类型的构造方法的时候,构造方法都负责初始化由这个类型定义的实例字段,因此最终会调用到System.Object的构造方法,不过这个方法仅仅是简单的返回,并不去做其他任何事情。

new在执行了上面的所有的操作后,会返回对新创建的对象的一个引用(或者是指针),在前面的实例代码中,这个引用会保存到变量e中,后者则具有Employee类型。

顺便说一下,与C++不同,new操作符并没有对应的delete操作符,也就是说,没有办法显式释放为一个对象分配的内存。不过CLR采用了垃圾回收机制(GC),它可以自动检测到一个对象不再被使用或访问,并自动释放对象的内存。

2008年11月3日星期一

经济学原理学习笔记(5)--两个经济模型

就像高中生物教师会通过塑料人体模型来讲授基础解剖学一样,经济学家也用模型来了解这个世界,虽然模型并不是真实的,但是由于模型去除了很多并不重要的细节,从而使我们可以将注意力集中到重要的方面上来。所有的模型都建立在一些假设之上,就像物理学家通过假设不存在空气阻力来分析石头的下落一样,经济学家也使用假设来撇开与所研究问题无关的许多经济细节。

首先提到的模型叫做循环流量图(Circular Flow Diagram)。在这个模型中,经济由两类决策者——家庭和企业——所组成。企业用劳动、土地和资本(建筑物和机器)这些投入来生产物品和劳务。这些投入被称为生产要素。家庭则拥有生产要素并消费企业生产的所有物品和劳务。



家庭和企业在两类市场上相互交易。在物品与劳务市场上,家庭是买者,而企业是卖者,特别是家庭购买企业生产的物品与劳务。而在生产要素市场上,家庭是卖者而企业是买者。在这些市场上,家庭和企业提供用于生产物品与劳务的投入。循环流量图提供了一种把家庭与企业之间发生的所有经济交易组织在一起的简单方法。


第二个提到的模型叫做生产可能性边界,它是用来阐明一些基本的经济思想。

生产可能性边界(Production Possiblities Frontier)是一个图形,它表明在生产要素和生产技术既定时,一个经济所能生产的产品——在下面的例子中是汽车和电脑——数量的各种组合。



下图是生产可能性边界的一个例子:如果这个经济把全部的资源都用于汽车行业,那么它可以生产1000辆汽车而不生产电脑;如果全部用于电脑行业,那么它可以生产3000台电脑而不生产汽车。生产可能性边界的两个端点代表这两种极端的可能性。

当然更为可能的情况是经济把资源分在两个行业中,于是在生产可能性边界上就存在了其他的点。这些点组成了生产可能性边界。

如果经济从它可以获得的稀缺资源中获得了它能得到的全部东西,就称为这种结果是有效率的。在生产可能性边界上的各点代表了有效率的生产水平。而边界内部的点代表无效率的结果,这可能书出于普遍失业的原因,经济的产量小于他从可以获得的资源中所能得到的最大可能产量。由于资源是稀缺的,在其它条件不变的基础上,边界外的点是不能实现的。

CLR via C#学习笔记(4)--CTS和CLS

继续上回提到的CTS,CTS还提到了类型可视性规则以及类型成员的访问规则。比如,任何程序集都能看到并访问一个公共类型,另一方面,将一个类型标记为程序集的时候,这个类型只有在同一个程序集的内部才是可见的,而且只能从这个程序集的内部来访问。因此也可以这么理解:通过CTS制定的规则,程序集为一个类型建立了一个可视化的边界,同时CLR则用于来强制维护这些可视性规则。调用者可见的一个类型可以进一步限制调用者访问同类型成员的能力,下面列出了用于控制成员访问的各种有效选项:

private: 方法只能由同一个class中的其他方法去调用

family: 方法可由派生类型调用,无论那些类型是否在同一个程序集中(C#中用protected来标识family)

family和 assembly: 方法可由派生类型调用,但这些派生类型必须是在同一个程序集中定义的(注意是"和",像C#和VB中并没有提供这样的访问控制)

assembly: 方法可由同一个程序集中的任何代码调用(C#中用internal来标识assembly)

family和assembly: 方法可由任何程序集中的派生类型调用,也可由同一程序集中的任何类型来调用(C#中使用protected internal来标识"family或assembly")

public: 方法可由任何程序集的任何方法调用

同时,CTS还定义了对类型继承、虚方法、对象生存期等进行管理的规则。

接下来说说CLS吧。

COM允许用不同语言创建的对象相互通信。另一方面,CLR集成了所有语言,并允许在一种语言中使用由另外一种语言创建的对象。之所以可以做到这样的集成,是因为CLR建立了标准的类型集、元数据(自描述性的类型信息)以及公共执行环境。

虽然语言间的集成是一个很不错的目标,但是这中间存在一个很大的问题:各种编程语言之间有很大的区别,比如说VB在处理符号的时候并不区分大小写,而有的语言不支持unsigned类型、操作符重载。

所以说,如果想要创建很容易从其他编程语言中访问的类型,那么在自己的编程语言中提供的所有特性中,只能选用其他所有语言都保证支持的那一些。为了做到这一点,MS定义了一个Common Language Specification,它详细的定义了一个最小特性集。对于任何编译器厂商的编译器产品所生成的类型,如果他们要兼容位于CLR顶部的、支持CLS的其他语言,就必须得支持这个最小的特性集。

CLR/CTS支持的特性要比CLS定义的子集丰富的多,所以,如果不关心语言之间的互操作性的话,完全可以开发一套非常丰富的类型,而且这些类型仅受语言的特性集的限制。要注意的是:外部可见的类型和方法如果要从任何CLS相容的编程语言访问,就必须遵从CLS定义的规则。当然,如果代码只需要从定义程序集的内部访问,CLS规则就不适用了。

CLR/CTS提供了一个特性集:多数语言只是揭示了CLR/CTS的一个子集,如果程序员用IL来编写程序的话,那么他可以使用到CLR/CTS提供的全部特性,但是考虑到这种复杂性,大多数开发人员还是倾向于C#,VB这类语言,不过,C#、VB只向开发人员揭示了CLR/CTS的一个子集。