2007年11月29日星期四

去滑雪(Nanshan ski)

今天组织去南山滑雪,这两天天气晴朗的一塌糊涂,所以都是人工造雪。中午在山上吃了一顿黑心饭,到下午发现雪都被晒化了,被那么多人一压,都快成冰了。非常难以控制速度。
最大的感受就是:人真多。上一次坡顶要排长队,到了顶上哧溜一下就下来了,然后就是再一次漫长的等待。


人工造雪

一半是雪,一半是草

游人如织

排长龙

都化成冰了

2007年11月28日星期三

生成等值面(Marching Cube)

看看文件的日期,最初的开始是21日,到现在又是一周了,才有了个初步的版本。
上周布置了一个活,让用Marching Cube算法实现在数据体中生成一个等值面的算法。听说这是上上届一位师兄毕业论文的内容,让我也照着实现一个。可后来发现找不到当初的代码了,没办法,只能自己动手。在UAF的网上课程中查到一份实现代码,接下来的工作就是看懂它然后应用到程序中来。我料到了开头,可是没有猜到过程会这么艰难。
MC算法的原理还比较简单,就是分析一个正方体体素八个顶点分别位于等值面外和内两种情况时的组合,根据这256种不同的情况生成体素内的三角形。在代码中用到了八叉树,用来保存等值面穿过的体素,为了理解这个结构,花了相当多的时间,一连几天都在研究这个算法,好不容易有点眉目了,巴巴地实现出来,结果被李博一针见血地指出每次生成的时候都要重新生成八叉树,虽然不一定每次都更新到整个树,但是考虑到其本身存储空间的开销,还是有点吃不消,真是备受打击。只好三下五除二把八叉树砍掉,原来1200多行的代码一下子砍的不到700行,其中约300行还是两个大的查找表。当初就是被这两个大表里面密密麻麻的冰冷的16进制数给吓住了。没有深入分析就去看八叉树了。后来发现其实里面的数据相当规律,就是在第一个表用01表示各个边是否被等值面截,然后到下一个表中找到相应的三角形组合方式。256种情况一一在目,要是让我自已写可能要多花几倍的功夫还不能保证穷尽所有情况。想到中科院自动化所田捷博士说的,图形学算法的特点就是容易理解,但是细节太烦琐。总结这一周的经验,对于网上下的源码的每一个部分,都应该吃透,不能留下死角,然后根据自己的实际需要,选择需要的部分,不能为了赶时间把整个块拿过来,期望它能够像一个黑盒似的工作正常。即使侥幸别人的代码写的不错,为了把其与自身代码结合所花费的改写时间也已经超出了读懂代码所要花费的时间了。

2007年11月20日星期二

需求变更(Software Change Control)

“什么东西是绿的,有两个轮子,长在房子周围?”
“……不知道,是什么?”
“草,轮子是我瞎说的。”

绕了一圈,又回到原点了。当初因为不知道将来的扩展情况,在效率和可扩展性之间选择的是后者,采用尽量独立的数据结构。随着项目的深入,原来的速度渐渐的变得不可容忍,而未来的应用范围已经可以基本上确定了,不用再考虑过多,于是又重新把过去写的数据结构去了,改成了当初那种ungraceful的写法,速度一下暴增,将来会出什么问题,还不知道,一步一步调,一点一点试吧。既然干编程这一行,就得随时做好需求变更的准备,一方面上是心态上的,对变化不要厌烦;一方面是技术上的,程序的编写要尽量灵活以利于扩充。看来有必要再好好学习一下设计模式的内容。

2007年11月18日星期日

保护眼睛(改变窗口颜色和Pdf背景颜色)

首先需要改一下设置,如果常常用电脑很容易导致眼睛过度疲劳而得干眼症,所以大夫建议电脑屏幕不要用白色,因为白色对眼睛的刺激是最大的。像我们这样整天对着电脑,也应该注意一下。其实,只要稍微设置一下,就能让你电脑上的窗口从白花花的颜色变成淡淡的绿色。

设置方法:
打开控制面板中的"显示" 选择外观(appearance)-高级(advanced), 然后在项目(items)那栏选窗口(windows),再点颜色(color)-其它(others),然后把Hue(色调设为85,Sat(饱和度)设为90,Lum(亮度)设为205。 然后单击 添加到自定义颜色(Add tocustom colors),一直确定。
把窗口设成绿色之后,再来把IE的网页背景也变成养眼的绿色吧:
打开IE,点击工具(TOOLS),点INTERNET选项(INTERNET OPTIONS),点右下角的辅助功能 (Assessibility),然后勾选 不使用网页中指定的颜色(ignore colors specifiedon web pages),然后点 确定退出。

现在你就会发现屏幕已经变成淡淡的绿色了。这个颜色会比白色柔和许多,刚开始可能你还有些不适应,但确实对我们的眼睛有好处,建议大家试一下。

PDF格式背景改变方法如下:

点击 编辑 -》首选项-》辅助工具-》选中“替换文档颜色”和“
自定义颜色”-》将背景颜色改成你想要的背景颜色。。-》

2007年11月13日星期二

为什么会出现LNK2005,LNK1169错误

许多Visual C++的使用者都碰到过LNK2005:symbol already defined和LNK1169:one or more multiply defined symbols found这样的链接错误,而且通常是在使用第三方库时遇到的。对于这个问题,有的朋友可能不知其然,而有的朋友可能知其然却不知其所以然,本文就试图为大家彻底解开关于它的种种疑惑。

大家都知道,从C/C++源程序到可执行文件要经历两个阶段:
(1)编译器将源文件编译成汇编代码,然后由汇编器(assembler)翻译成机器指令(再加上其它相关信息)后输出到一个个目标文件(object file,VC的编译器编译出的目标文件默认的后缀名是.obj)中;
(2)链接器(linker)将一个个的目标文件(或许还会有若干程序库)链接在一起生成一个完整的可执行文件。

编译器编译源文件时会把源文件的全局符号(global symbol)分成强(strong)和弱(weak)两类传给汇编器,而随后汇编器则将强弱信息编码并保存在目标文件的符号表中。那么何谓强弱呢?编译器认为函数与初始化了的全局变量都是强符号,而未初始化的全局变量则成了弱符号。比如有这么个源文件:

extern int errorno;
int buf[2] = {1,2};
int *p;
int main()
{
return 0;
}

其中main、buf是强符号,p是弱符号,而errorno则非强非弱,因为它只是个外部变量的使用声明。

有了强弱符号的概念,链接器(Unix平台)就会按如下规则(参考[1],p549~p550)处理与选择被多次定义的全局符号:

规则1: 不允许强符号被多次定义(即不同的目标文件中不能有同名的强符号);
规则2: 如果一个符号在某个目标文件中是强符号,在其它文件中都是弱符号,那么选择强符号;
规则3: 如果一个符号在所有目标文件中都是弱符号,那么选择其中任意一个;

虽然上述3条针对的是Unix平台的链接器,但据作者试验,至少VC6.0的linker也遵守这些规则。由此可知多个目标文件不能重复定义同名的函数与初始化了的全局变量,否则必然导致LNK2005和LNK1169两种链接错误。可是,有的时候我们并没有在自己的程序中发现这样的重定义现象,却也遇到了此种链接错误,这又是何解?嗯,问题稍微有点儿复杂,容我慢慢道来。

众所周知,ANSI C/C++ 定义了相当多的标准函数,而它们又分布在许多不同的目标文件中,如果直接以目标文件的形式提供给程序员使用的话,就需要他们确切地知道哪个函数存在于哪个目标文件中,并且在链接时显式地指定目标文件名才能成功地生成可执行文件,显然这是一个巨大的负担。所以C语言提供了一种将多个目标文件打包成一个文件的机制,这就是静态程序库(static library)。开发者在链接时只需指定程序库的文件名,链接器就会自动到程序库中寻找那些应用程序确实用到的目标模块,并把(且只把)它们从库中拷贝出来参与构建可执行文件。几乎所有的C/C++开发系统都会把标准函数打包成标准库提供给开发者使用(有不这么做的吗?)。

程序库为开发者带来了方便,但同时也是某些混乱的根源。我们来看看链接器(Unix平台)是如何解析(resolve)对程序库的引用的(参考[1],p556)。

在符号解析(symbol resolution)阶段,链接器按照所有目标文件和库文件出现在命令行中的顺序从左至右依次扫描它们,在此期间它要维护若干个集合:(1)集合E是将被合并到一起组成可执行文件的所有目标文件集合;(2)集合D是所有之前已被加入E的目标文件定义的符号集合;(3)集合U是未解析符号(unresolved symbols,即那些被E中目标文件引用过但在D中还不存在的符号)的集合。一开始,E、D、U都是空的。

(1): 对命令行中的每一个输入文件f,链接器确定它是目标文件还是库文件,如果它是目标文件,就把f加入到E,并把f中未解析的符号和已定义的符号分别加入到U、D集合中,然后处理下一个输入文件。
(2): 如果f是一个库文件,链接器会尝试把U中的所有未解析符号与f中各目标模块定义的符号进行匹配。如果某个目标模块m定义了一个U中的未解析符号,那么就把m加入到E中,并把m中未解析的符号和已定义的符号分别加入到U、D集合中。不断地对f中的所有目标模块重复这个过程直至到达一个不动点(fixed point),此时U和D不再变化。而那些未加入到E中的f里的目标模块就被简单地丢弃,链接器继续处理下一输入文件。
(3): 当扫描完所有输入文件时如果U非空或者有同名的符号被多次加入D,链接器报告错误信息并退出。否则,它把E中的所有目标文件合并在一起生成可执行文件。

上述规则针对的是Unix平台链接器,而VC(至少VC6.0)linker则有相当的不同: 它首先依次处理命令行中出现的所有目标文件,然后依照顺序不停地扫描所有的库文件,直至U为空或者某遍(从头到尾依次把所有的库文件扫描完称为一遍)扫描过程中U、D无任何变化时结束扫描,此刻再根据U是否为空以及是否有同名符号重复加入D来决定是出错退出还是生成可执行文件。很明显Unix链接器对输入文件在命令行中出现的顺序十分敏感,而VC的算法则可最大限度地减少文件顺序对链接的影响。作者不清楚Unix下新的开发工具是否已经改进了相应的做法,欢迎有实践经验的朋友补充这方面的信息(补充于2005年10月10日: 经试验,使用gcc 3.2.3的MinGW 3.1.0的链接器表现与参考[1]描述的一致)。

VC带的编译器是cl.exe,它有这么几个与标准程序库有关的选项: /ML、/MLd、/MT、/MTd、/MD、/MDd。这些选项告诉编译器应用程序想使用什么版本的C标准程序库。/ML(缺省选项)对应单线程静态版的标准程序库(libc.lib);/MT对应多线程静态版标准库(libcmt.lib),此时编译器会自动定义_MT宏;/MD对应多线程DLL版(导入库msvcrt.lib,DLL是msvcrt.dll),编译器自动定义_MT和_DLL两个宏。后面加d的选项都会让编译器自动多定义一个_DEBUG宏,表示要使用对应标准库的调试版,因此/MLd对应调试版单线程静态标准库(libcd.lib),/MTd对应调试版多线程静态标准库(libcmtd.lib),/MDd对应调试版多线程DLL标准库(导入库msvcrtd.lib,DLL是msvcrtd.dll)。虽然我们的确在编译时明白无误地告诉了编译器应用程序希望使用什么版本的标准库,可是当编译器干完了活,轮到链接器开工时它又如何得知一个个目标文件到底在思念谁?为了传递相思,我们的编译器就干了点秘密的勾当。在cl编译出的目标文件中会有一个专门的区域(关心这个区域到底在文件中什么地方的朋友可以参考COFF和PE文件格式)存放一些指导链接器如何工作的信息,其中有一项就叫缺省库(default library),它指定了若干个库文件名,当链接器扫描该目标文件时将按照它们在目标模块中出现的顺序处理这些库名: 如果该库在当前输入文件列表中还不存在,那么便把它加入到输入文件列表末尾,否则略过。说到这里,我们先来做个小实验。写个顶顶简单的程序,然后保存为main.c :


/* main.c */
int main() { return 0; }


用下面这个命令编译main.c(什么?你从不用命令行来编译程序?这个......) :

cl /c main.c

/c是告诉cl只编译源文件,不用链接。因为/ML是缺省选项,所以上述命令也相当于: cl /c /ML main.c 。如果没什么问题的话(要出了问题才是活见鬼!当然除非你的环境变量没有设置好,这时你应该去VC的bin目录下找到vcvars32.bat文件然后运行它。),当前目录下会出现一个main.obj文件,这就是我们可爱的目标文件。随便用一个文本编辑器打开它(是的,文本编辑器,大胆地去做别害怕),搜索"defaultlib"字符串,通常你就会看到这样的东西: "-defaultlib:LIBC -defaultlib:OLDNAMES"。啊哈,没错,这就
是保存在目标文件中的缺省库信息。我们的目标文件显然指定了两个缺省库,一个是单线程静态版标准库libc.lib(这与/ML选项相符);一个是oldnames.lib(它是为了兼容微软以前的C/C++开发系统,基本不用了,为了简化讨论可以忽略它)。另外,如果在源程序中用了

/* xxxx代表实际的库文件名 */
#pragma comment(lib,"xxxx")

编译指示命令(compiler directive)指定要链接的库,那么这个信息也会被保存到目标文件的缺省库信息项中,且位于缺省标准库之前。如果有多个这样的命令,那么对应库名在目标文件中出现的顺序与它们在源程序中出现的顺序完全一致(且都在缺省标准库之前)。

VC的链接器是link.exe,因为main.obj保存了缺省库信息,所以可以用
link main.obj libc.lib
或者
link main.obj
来生成可执行文件main.exe,这两个命令是等价的。但是如果你用
link main.obj libcd.lib
的话,链接器会给出一个警告: "warning LNK4098: defaultlib "LIBC" conflicts with use of other libs; use /NODEFAULTLIB:library",因为你显式指定的标准库版本与目标文件的缺省值不一致。通常来说,应该保证链接器合并的所有目标文件指定的缺省标准库版本一致,否则编译器一定会给出上面的警告,而LNK2005和LNK1169链接错误则有时会出现有时不会。那么这个有时到底是什么时候?呵呵,别着急,下面的一切正是为喜欢追根究底的你准备的。

建一个源文件,就叫mylib.c,内容如下:

/* mylib.c */
#include

void foo(void)
{
printf("%s","I am from mylib!\n");
}



cl /c /MLd mylib.c
命令编译,注意/MLd选项是指定libcd.lib为默认标准库。lib.exe是VC自带的用于将目标文件打包成程序库的命令,所以我们可以用
lib /OUT:my.lib mylib.obj
将mylib.obj打包成库,输出的库文件名是my.lib。接下来把main.c改成:

/* main.c */
void foo(void);

int main()
{
foo();
return 0;
}


cl /c main.c
编译,然后用
link main.obj my.lib
进行链接。这个命令能够成功地生成main.exe而不会产生LNK2005和LNK1169链接错误,你仅仅是得到了一条警告信息:"warning LNK4098: defaultlib "LIBCD" conflicts with use of other libs; use /NODEFAULTLIB:library"。我们根据前文所述的扫描规则来分析一下链接器此时做了些啥(加一个/VERBOSE选项就可以看到详尽的链接过程,但要注意,几乎所有的C编译器都会在符号前加一个下划线后再输出,所以在目标文件和链接输出信息中看到的符号名都比在源程序中见到的多出一个'_',此点不可不察。)。

一开始E、U、D都是空集。链接器首先扫描main.obj,把它的默认标准库libc.lib加入到输入文件列表末尾,它自己加入E集合,同时未解析的foo加入U,main加入D。接着扫描my.lib,因为这是个库,所以会拿当前U中的所有符号(当然现在就一个foo)与my.lib中的所有目标模块(当然也只有一个mylib.obj)依次匹配,看是否有模块定义了U中的符号。结果mylib.obj确实定义了foo,于是它加入到E,foo从U转移到D,未解析的printf加入到U,指定的默认标准库libcd.lib也加到输入文件列表末尾(在libc.lib之后)。不断地在my.lib库的各模块上进行迭代以匹配U中的符号,直到U、D都不再变化。很明显,现在就已经到达了这么一个不动点,所以接着扫描下一个输入文件,就是libc.lib。链接器发现libc.lib里的printf.obj里定义有printf,于是printf从U移到D,printf.obj加入到E,它定义的所有符号加入到D,它里头的未解析符号加入到U。如果链接时没有指定/ENTRY(程序入口点选项),那么链接器默认的入口点就是函数mainCRTStartup(GUI程序的默认入口点则是WinMainCRTStartup),它在crt0.obj中被定义,所以crt0.obj及它直接或间接引用的模块(比如malloc.obj、free.obj等)都被加入到E中,这些目标模块指定的默认库(只crt0init.obj指定了kernel32.lib)加到输入文件列表末尾,同时更新U和D。不断匹配libc.lib中各模块直至到达不动点,然后处理libcd.lib,但是它里面的所有目标模块都没有定义U中的任何一个符号,所以链接器略过它进入到最后一个输入文件kernel32.lib。事实上,U中已有和将要加入的未解析符号都可以在其中找到定义,那么当处理完kernel32.lib时,U必然为空,于是链接器合并E中的所有模块生成可执行文件。

上文描述了虽然各目标模块指定了不同版本的缺省标准库但仍然链接成功的例子,接下来你将目睹因为这种不严谨而导致的悲惨失败。

修改mylib.c成这个样子:

#include

void foo(void)
{
// just a test , don't care memory leak
_malloc_dbg( 1, _NORMAL_BLOCK, __FILE__, __LINE__ );
}

其中_malloc_dbg不是ANSI C的标准库函数,它是VC标准库提供的malloc的调试版,与相关函数配套能帮助开发者抓各种内存错误。使用它一定要定义_DEBUG宏,否则预处理器会把它自动转为malloc。继续用
cl /c /MLd mylib.c
lib /OUT:my.lib mylib.obj
编译打包。当再次用
link main.obj my.lib
进行链接时,我们看到了什么?天哪,一堆的LNK2005加上个贵为"fatal error"的LNK1169垫底,当然还少不了那个LNK4098。链接器是不是疯了?不,你冤枉可怜的链接器了,我拍胸脯保证它可是一直在尽心尽责地照章办事。

一开始E、U、D为空,链接器扫描main.obj,把libc.lib加到输入文件列表末尾,把main.obj加进E,把foo加进U,把main加进D。接着扫描my.lib,于是mylib.obj加入E,libcd.lib加到输入文件列表末尾,foo从U转移到D,_malloc_dbg加进U。然后扫描libc.lib,这时会发现libc.lib里任何一个目标模块都没有定义_malloc_dbg(它只在调试版的标准库中存在),所以不会有任何一个模块因为_malloc_dbg而加入E。但因为libc.lib中的crt0.obj定义了默认入口点函数mainCRTStartup,所以crt0.obj及它直接或间接引用的模块(比如malloc.obj、free.obj等)都被加入到E中,这些目标模块指定的默认库(只crt0init.obj指定了kernel32.lib)加到输入文件列表末尾,同时更新U和D。不断匹配libc.lib中各模块直至到达不动点后再处理libcd.lib,发现dbgheap.obj定义了_malloc_dbg,于是dbgheap.obj加入到E,它的未解析符号加入U,它定义的所有其它符号加入D,这时灾难便来了。之前malloc等符号已经在D中(随着libc.lib里的malloc.obj加入E而加入的),而dbgheap.obj及因它而引入的其它模块又定义了包括malloc在内的许多同名符号,导致了重定义冲突。所以链接器在处理完所有输入文件(是的,即使中途有重定义冲突它也会处理所有的文件以便生成一个完整的冲突列表)后只好报告: 这活儿没法儿干。

现在我们该知道,链接器完全没有责任,责任在我们自己的身上。是我们粗心地把缺省标准库版本不一致的目标文件(main.obj)与程序库(my.lib)链接起来,引发了大灾难。解决办法很简单,要么用/MLd选项来重编译main.c;要么用/ML选项重编译mylib.c;再或者干脆在链接时用/NODEFAULTLIB:XXX选项忽略默认库XXX,但这种方法非常不保险(想想为什么?),所以不推荐。

在上述例子中,我们拥有库my.lib的源代码(mylib.c),所以可以用不同的选项重新编译这些源代码并再次打包。可如果使用的是第三方的库,它并没有提供源代码,那么我们就只有改变自己程序的编译选项来适应这些库了。但是如何知道库中目标模块指定的默认库呢?其实VC提供的一个小工具便可以完成任务,这就是dumpbin.exe。运行下面这个命令
dumpbin /DIRECTIVES my.lib
然后在输出中找那些"Linker Directives"引导的信息,你一定会发现每一处这样的信息都会包含若干个类似"-defaultlib:XXXX"这样的字符串,其中XXXX便代表目标模块指定的缺省库名(注意,如果在编译时指定了/Zl选项,那么目标模块中将不会有defaultlib信息)。

知道了第三方库指定的默认标准库,再用合适的选项编译我们的应用程序,就可以避免LNK2005和LNK1169链接错误。喜欢IDE的朋友,你一样可以到 "Project属性" -> "C/C++" -> "代码生成(code generation)" -> "运行时库(run-time library)" 项下设置应用程序的默认标准库版本,这与命令行选项的效果是一样的。

---------------------------------------------------------------------------------------------------
在vc 2003下,如果使用了静态库,很有可能会发生link 2005的连接错误,主要是由于crt 库引起的,CRT 库对 new、delete 和 DllMain 函数使用弱外部链接,如果我们改变一下库的连接顺序,就可以解决这个错误

解决方案
解决该问题有两种方法:第一种解决方案涉及到强制链接程序按照正确的顺序链接库。第二种解决方案是让您查找导致问题的模块并纠正它。
解决方案一:强制链接程序按照正确的顺序链接库
1.在“生成”菜单上,单击“设置”。2.在“项目设置”对话框的“以下项目的设置”视图中,单击以选中出现链接错误的项目配置。3.在“链接”选项卡上,单击以选中“类别”组合框中的“INPUT”。4.在“要忽略的库”中,插入库名(例如,Nafxcwd.lib;Libcmtd.lib)。

注意:等效的链接程序命令行是:/NOD:。5.在“对象/库模块”框中,插入库名。必须确保这些库按顺序列出,且作为命令行中的前两个库(例如,Nafxcwd.lib 和 Libcmtd.lib)列出。要在 Visual C++ .NET 中设置该选项,请阅读“设置 Visual C++ 项目属性”联机帮助主题。
解决方案二:找到并纠正出现问题的模块
要查看当前的库链接顺序,请按照下列步骤操作: 1.在“生成”菜单上,单击“设置”。2.在“项目设置”对话框的“以下项目的设置”视图中,单击以选中出现链接错误的项目配置。3.在“链接”选项卡上,在“项目选项”框中键入 /verbose:lib

Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1431093

LNK2001 unresolved external symbol错误


LNK2001 unresolved external symbol错误

这个错误一般有两种情况
1) 选择了错误的工程类型,导致系统寻找其实不需要的_main符合。

我们编制的程序有两种,Windows程序和Console程序,如果你编制的程序不是Console程序,你必须首先创建一种合适的工程,然后在这个工程内添加代码。如果先写代码然后直接编译,就会默认是console工程导致连接失败。具体IDE使用方法请买一本介绍VC编程的书看。
2)使用了一个实体(包括函数、全局变量等),但是使用时的名字、原型与定义时不一致,典型错误包括:
a) 函数原型声明和函数定义时的参数类型不一致
b) 函数原型声明和函数定义时的修饰符不一致,典型的是遗漏了PASCALL(WINAPI)修饰符或者extern "C"修饰符
c) 函数原型的返回类型和定义时不一致。
d) 在动态连接库中定义,但是没有把动态连接库对应的lib文件加入工程,其中很多系统函数出现的LNK2001错误就是这个原因。
e) 动态连接库函数在.c文件中定义,但是没有在头文件中用extern "C"修饰
f) 动态连接库没有输出这个函数
g) 变量声明和定义不一致。例如声明为指针,实际是一个数组。这种错误也许不会出2001,但是会导致运行失败,而且错误得莫名其妙。
h) 函数或者变量用static修饰,导致文件外无法看到这个实体。

在创建MFC项目时, 不使用MFC AppWizard向导, 如果没有设置好项目参数, 就会在编译时产生很多连接错误, 如error LNK2001错误, 典型的错误提示有:
libcmtd.lib(crt0.obj) : error LNK2001: unresolved external symbol _main
LIBCD.lib(wincrt0.obj) : error LNK2001: unresolved external symbol _WinMain@16
msvcrtd.lib(crtexew.obj) : error LNK2001: unresolved external symbol _WinMain@16
nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __beginthreadex
nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __endthreadex

下面介绍解决的方法:
1. Windows子系统设置错误, 提示:
libcmtd.lib(crt0.obj) : error LNK2001: unresolved external symbol _main
Windows项目要使用Windows子系统, 而不是Console, 可以这样设置:
[Project] --> [Settings] --> 选择"Link"属性页,
在Project Options中将/subsystem:console改成/subsystem:windows

2. Console子系统设置错误, 提示:
LIBCD.lib(wincrt0.obj) : error LNK2001: unresolved external symbol _WinMain@16
控制台项目要使用Console子系统, 而不是Windows, 设置:
[Project] --> [Settings] --> 选择"Link"属性页,
在Project Options中将/subsystem:windows改成/subsystem:console

3. 程序入口设置错误, 提示:
msvcrtd.lib(crtexew.obj) : error LNK2001: unresolved external symbol _WinMain@16
通常, MFC项目的程序入口函数是WinMain, 如果编译项目的Unicode版本, 程序入口必须改为wWinMainCRTStartup, 所以需要重新设置程序入口:
[Project] --> [Settings] --> 选择"Link"属性页,
在Category中选择Output,
再在Entry-point symbol中填入wWinMainCRTStartup, 即可

4. 线程运行时库设置错误, 提示:
nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __beginthreadex
nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __endthreadex
这是因为MFC要使用多线程时库, 需要更改设置:
[Project] --> [Settings] --> 选择"C/C++"属性页,
在Category中选择Code Generation,
再在Use run-time library中选择Debug Multithreaded或者multithreaded
其中,
Single-Threaded 单线程静态链接库(release版本)
Multithreaded 多线程静态链接库(release版本)
multithreaded DLL 多线程动态链接库(release版本)
Debug Single-Threaded 单线程静态链接库(debug版本)
Debug Multithreaded 多线程静态链接库(debug版本)
Debug Multithreaded DLL 多线程动态链接库(debug版本)
单线程: 不需要多线程调用时, 多用在DOS环境下
多线程: 可以并发运行
静态库: 直接将库与程序Link, 可以脱离MFC库运行
动态库: 需要相应的DLL动态库, 程序才能运行
release版本: 正式发布时使用
debug版本: 调试阶段使用

2-------------------------------------------------------------------------------------------------------
在VC/MFC社区中经常看到有人问一些编译错误怎么解决的问题,很多错误都是VC++的链接错误LNK2001,所以把这篇我记不得从什么地方获得的文章贴出来,也许对大家有点帮助。在此要感谢本文的原始作者,为他的专业精神!

学习VC++时经常会遇到链接错误LNK2001,该错误非常讨厌,因为对于编程者来说,最好改的错误莫过于编译错误,而一般说来发生连接错误时,编译都已通过。产生连接错误的原因非常多,尤其LNK2001错误,常常使人不明其所以然。如果不深入地学习和理解VC++,要想改正连接错误LNK2001非常困难。
  初学者在学习VC++的过程中,遇到的LNK2001错误的错误消息主要为:
  unresolved external symbol “symbol”(不确定的外部“符号”)。
  如果连接程序不能在所有的库和目标文件内找到所引用的函数、变量或标签,将产生此错误消息。一般来说,发生错误的原因有两个:一是所引用
的函数、变量不存在、拼写不正确或者使用错误;其次可能使用了不同版本的连接库。
  以下是可能产生LNK2001错误的原因:
  一.由于编码错误导致的LNK2001。
  1.不相匹配的程序代码或模块定义(.DEF)文件能导致LNK2001。例如,如果在C++ 源文件内声明了一变量“var1”,却试图在另一文件内以变量
“VAR1”访问该变量,将发生该错误。
  2.如果使用的内联函数是在.CPP文件内定义的,而不是在头文件内定义将导致LNK2001错误。
  3.调用函数时如果所用的参数类型同函数声明时的类型不符将会产生LNK2001。
  4.试图从基类的构造函数或析构函数中调用虚拟函数时将会导致LNK2001。
  5.要注意函数和变量的可公用性,只有全局变量、函数是可公用的。
  静态函数和静态变量具有相同的使用范围限制。当试图从文件外部访问任何没有在该文件内声明的静态变量时将导致编译错误或LNK2001。
  函数内声明的变量(局部变量) 只能在该函数的范围内使用。C++ 的全局常量只有静态连接性能。这不同于C,如果试图在C++的
多个文件内使用全局变量也会产生LNK2001错误。一种解决的方法是需要时在头文件中加入该常量的初始化代码,并在.CPP文件中包含该头文件;另一种
方法是使用时给该变量赋以常数。
  二.由于编译和链接的设置而造成的LNK2001
  1.如果编译时使用的是/NOD(/NODEFAULTLIB)选项,程序所需要的运行库和MFC库在连接时由编译器写入目标文件模块, 但除非在文件中明确包含这些库名,否则这些库不会被链接进工程文件。在这种情况下使用/NOD将导致错误LNK2001。
  2.如果没有为wWinMainCRTStartup设定程序入口,在使用Unicode和MFC时将得到“unresolved external on _WinMain@16”的LNK2001错误信息。
  3.使用/MD选项编译时,既然所有的运行库都被保留在动态链接库之内,源文件中对“func”的引用,在目标文件里即对“__imp__func” 的引用。
如果试图使用静态库LIBC.LIB或LIBCMT.LIB进行连接,将在__imp__func上发生LNK2001;如果不使用/MD选项编译,在使用MSVCxx.LIB连接时也会发生LNK2001。
  4.使用/ML选项编译时,如用LIBCMT.LIB链接会在_errno上发生LNK2001。
  5.当编译调试版的应用程序时,如果采用发行版模态库进行连接也会产生LNK2001;同样,使用调试版模态库连接发行版应用程序时也会产生相同的问题。
  6.不同版本的库和编译器的混合使用也能产生问题,因为新版的库里可能包含早先的版本没有的符号和说明。
  7.在不同的模块使用内联和非内联的编译选项能够导致LNK2001。如果创建C++库时打开了函数内联(/Ob1或/Ob2),但是在描述该函数的相应头文件里却关闭了函数内联(没有inline关键字),这时将得到该错误信息。为避免该问题的发生,应该在相应的头文件中用inline关键字标志内联函数。
  8.不正确的/SUBSYSTEM或/ENTRY设置也能导致LNK2001。
  其实,产生LNK2001的原因还有很多,以上的原因只是一部分而已,对初学者来说这些就够理解一阵子了。但是,分析错误原因的目的是为了避免错
误的发生。LNK2001错误虽然比较困难,但是只要注意到了上述问题,还是能够避免和予以解决的。

CString,int,string,char*之间的转换

string 转 CString
CString.format("%s", string.c_str());

char 转 CString
CString.format("%s", char*);

char 转 string
string s(char *);

string 转 char *
char *p = string.c_str();

CString 转 string
string s(CString.GetBuffer());

1,string -> CString
CString.format("%s", string.c_str());
用c_str()确实比data()要好.
2,char -> string
string s(char *);
你的只能初始化,在不是初始化的地方最好还是用assign().
3,CString -> string
string s(CString.GetBuffer());
GetBuffer()后一定要ReleaseBuffer(),否则就没有释放缓冲区所占的空间.

《C++标准函数库》中说的
有三个函数可以将字符串的内容转换为字符数组和C—string
1.data(),返回没有”\0“的字符串数组
2,c_str(),返回有”\0“的字符串数组
3,copy()
---------------------------------------------------------------
CString与int、char*、char[100]之间的转换- -

CString与int、char*、char[100]之间的转换- -

CString互转int

将字符转换为整数,可以使用atoi、_atoi64或atol。
而将数字转换为CString变量,可以使用CString的Format函数。如
CString s;
int i = 64;
s.Format("%d", i)
Format函数的功能很强,值得你研究一下。

void CStrDlg::OnButton1()
{
// TODO: Add your control notification handler code here
CString
ss="1212.12";
int temp=atoi(ss);
CString aa;
aa.Format("%d",temp);
AfxMessageBox("var is " + aa);
}

sart.Format("%s",buf);

CString互转char*

///char * TO cstring
CString strtest;
char * charpoint;
charpoint="give string a value";
strtest=charpoint;


///cstring TO char *
charpoint=strtest.GetBuffer(strtest.GetLength());

标准C里没有string,char *==char []==string

可以用CString.Format("%s",char *)这个方法来将char *转成CString。要把CString转成char *,用操作符(LPCSTR)CString就可以了。


CString转换 char[100]

char a[100];
CString str("aaaaaa");
strncpy(a,(LPCTSTR)str,sizeof(a));


Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=1438125

http://blog.csdn.net/ben1978/archive/2006/12/11/1438125.aspx

2007年11月9日星期五

九十月工作阶段小结(Fast Marching Methods)

不知不觉,距当初工作刚开始已经过去了九周了,中间过程曲折反复,本来预计月余即可完工的“小活”,至今仍距完工遥遥无期,现在已经把机子搬来打算“常驻”了。预期的结果中未来的迷雾中。
因为是第一次独立从事研究性的开发工作,所以对于时间的计算很没有把握,抱着一步一步来的心态来进行。开始比较紧张,直接搬入公司,附近就有技术大牛,随时可以得到支持。这在后面证明是保证进度相当重要的一点,很多的东西不是没有想法,大量的时间都消耗在代码的实现上了。在与冰冷的Error和Access violation的斗争中曲折前进,不知道什么时候自己才能达到“自在编程”的境界。
九周的时间进度是这样的,九月份的前三周主要是实现与导师商量好的算法,期间涉及到一个最小堆结构的实现,动态三维数组的设计,空间种子填充算法,以及对微分方程的数值求解等。最小堆是从算法导论上学的,不愧是经典,短短几十行代码效率非常高;动态三维数组申请空间时要多申请几个字节(参考:生死疆界——在new与delete之间);种子填充算法是自己想到,每次压栈的点选取当前点对面的五个点,事后证明运行的还不错。微分方程的求解是按文献中的说法来。期间穿插着网络实验的课程。到了第四周,程序大体结构已经完成,可是方程求解过程中老是出现无解的情况,拖了好几天。最后商量的结果是改变赋初值的方法看看有没有改善,十一之前还没改完,带着这个问题开始了两周的十一假期。
十一归来,新算法依然没有改进,只是无解的情况少了一点,只能采用近似的方法,最后程序是过了,效果也还令人满意。可还存在两个问题,一个是运行速度比较慢,一个是对初始地层扩展后形态的保持性较差。接下来的工作主要集中在这里。因为要应付网络实验考试,中间又占了将近一周时间。最后改来改去结果仍然不令人满意,期间顺手把地层数据体的存储操作做了一个类。对函数指针的使用和各个包之间的关系有了进一步的了解。到了现在,又进入了一个瓶颈阶段,目前是上下不能,体会煎熬的感觉。不过听说另一种方法的实现已经取得了初步的成功,而且没有出现数值不稳定的情况,也算是得到了一点慰籍。(人看到是05年的paper,我看的是99年的书。)
总结这9周,扣掉放假考试和零零碎碎的时间,工作时间5周,编了3000余行代码,平均一天不到100行当有效输出,最大的感觉就是“人力有穷尽”,虽说有数学基础的问题,有时仿佛就被魇住了,几天没有进展,然后旁人一点拨就发现原来只是一个微不足道的疏忽。但这段时间不应该放弃对原著的钻研,另一种方法取得进展就是一个证明,当初如果看了说不定就能从中获得启发。另外一个问题就是对于系统现在还不是很得心应手,虽说改过不少Bug,可是都停留在知其然不知其所以然的情况,很久以前改过的错误再碰到又要想半天,看来有必要深入底层研究一下。

搬,还是不搬?(Move, or not?)

这是个问题。最近莫名其妙的Blogspot突然好了,访问数突增,速度仍是那么快,当然不知什么时候又会莫名其妙地当掉。现在Blogspot在国内已经彻底变成一个小众产品,中国人看不了,外国人看不懂。曾经很认真的考虑换一个,可是发现面临的最大问题不是选择太少而是多大无法决定。目前博客网站多如过江之鲫,除开通过程的繁琐之外,已有文章的迁移实在是一个痛苦的过程。
新浪的Blog应该是不会选了,模板死的要命,还有强加的广告,而且与Google Reader的兼容性似乎不太好,独有图片无法由外部抓到,特排外的感觉。听说yo2不错,申请了一下,可最近悲哀地发现页面上是冰冷的“503 Service Temporarily Unavailable”。小网站的缺陷就在于:不知道什么时候就没了,辛辛苦苦积累的文章全部灰飞烟灭,找谁哭去?所以现在对于那些不是做博客出身,只是敢时髦的网站,都敬而远之。
算了,暂时作为个人的一个文集存档吧,还是很喜欢blogger周边各种丰富的插件。当了又不是没办法,道高一尺,魔高一丈,咱有tor,怕啥?Blogger本身的功能也很强大,目前用到的十中无一,看看师兄的博客(http://www.livingpearls.com/),就是在Blogspot上写完之后发布在申请的独立域名上,感觉自己这个还有相当大的开发余地啊。

附文一篇:我无法访问的国外优秀网站

2007年11月8日星期四

第五章 奔腾的芯(英特尔—Intel)(zz)

目录
1. 时势造英雄
2. 英特尔摩托罗拉之战
3. 指令集之争
4. 英特尔和 AMD 的关系
5. 天步艰难

在美国西海岸旧金山到圣荷西市(San Jose)之间,围绕着旧金山海湾有几十公里长,几公里宽的峡谷,通常称为硅谷。(在图中绿线圈出来的部分)那里之所以叫硅谷并不是因为它生产硅,而是它有很多是使用硅的半导体公司,包括全世界最大的半导体公司英特尔公司。全世界一大半的计算机都是用它的中央处理器(CPU),它对我们日常生活的影响是很少有公司可以匹敌的。我们在上一章介绍了摩尔定理和安迪-盖茨定理,其中摩尔是英特尔公司的创始人,而安迪.格罗夫是第四个加入英特尔并把英特尔公司真正发展成世界上最大的半导体公司的 CEO。今天,英特尔已经有近十万人,年产值达三百六十亿美元,市值高达一千四百亿美元。三十多年来,英特尔公司成功的关键首先是搭上了个人电脑革命的浪潮,尤其是有微软这个强势的伙伴;第二,它三十年来严格按照它的创始人预言的惊人的高速度在为全世界 PC 机用户提高着处理器的性能,用它自己的话讲,它给了每台微机一个奔腾的芯。

1. 时势造英雄

英特尔公司由戈登•摩尔(Gordon E. Moore)和罗伯特.诺伊斯(Robert Noyce)于 1968 年创立于硅谷。此前,摩尔和诺伊斯在 1956 年还和另外六个人一起创办了仙童(Fairchild)半导体公司。同 IBM、DEC 和惠普等公司相比,英特尔在很长时间内只能算是一个婴儿。说它是婴儿有两方面含义,第一,它是个人数少、生意小的小公司,第二,在八十年代以前,几乎所有的计算机公司如 IBM、DEC 都是自己设计中央处理器,因此这些计算机公司代表了处理器设计和制造的最高水平,而英特尔生产的是性能低的微处理器,是用来补充大计算机公司看不上的低端市场。单纯从性能上讲,英特尔八十年代的处理器还比不上 IBM 七十年代的,但是,它的处理器大家用得起,不是阳春白雪。即使在七十年代末,英特尔公司生产出了著名的十六位 8086 处理器,大家仍然将它看成小弟弟。在很长时间里,英特尔的产品被认为是低性能、低价格。虽然它的性价比很高,但并不是尖端产品。

虽然 8086 是我们今天所有 IBM PC 处理器的祖宗,但是,当时连英特尔自己也没有预测到它的重用性。当时英特尔公司对 8086 并没有一个明确的市场定位,只是想尽可能多地促销。IBM 只不过是英特尔当时众多大大小小的客户之一。1981 年,IBM 为了短平快地搞出 PC,也懒得自己设计处理器,拿来英特尔的 8086 就直接用上了。这一下子,英特尔一举成名。1982 年,英特尔搞出了和 8086 完全兼容的第二代 PC 处理器 80286,用在了 IBM PC/AT 上。由于 IBM 无法阻止别人造兼容机,随着 1985 年康柏(Compaq)造出了世界上第一台 IBM PC 的兼容机,兼容机厂商就像雨后春笋般在全世界冒了出来。这些兼容机硬件不径相同,但是为了和 IBM PC 兼容,处理器都得是英特尔公司的。下图是整个个人电脑工业的生态链。

可以看出在这个生态链中,只有作为操作系统开发商的微软和作为处理器制造商的英特尔处于一个不可替代的地位。因此,英特尔的崛起就成为历史的必然。这正是时势造英雄。

当然,虽然信息革命的浪潮将英特尔推上了前沿,英特尔还必须有能力来领导计算机处理器的技术革命。英特尔的 CEO 安迪•格罗夫在机会和挑战面前,最终证明了英特尔是王者。英特尔起步的八十年代恰恰是日本的十年,当时日本股市的总市值占了全世界的一半,日本东京附近的房地产总值相当于半个美国的房市总值。世界上最大的三个半导体公司都在日本,PC 里面日本芯片一度占到数量的 60%(注意:不是价钱的 60%)。以至于日本有些政治家盲目自大,认为日本到了全面挑战美国的时候,全世界都在怀疑美国在半导体技术上是否会落后于日本。但是冷静地分析一下全世界半导体市场就会发现,日本的半导体工业集中在技术含量低的芯片上,如存储器等芯片(即内存),而全世界高端的芯片工业,如计算机处理器和通信的数字信号处理器全部在美国。八十年代,英特尔果断地停掉了它的内存业务,将这个市场完全让给了日本人,从此专心做处理器。当时日本半导体公司在全市界挣了很多钱,日本一片欢呼,认为它们打败了美国人。其实,这不过是英特尔等美国公司弃子求势的一招棋。1985 年,英特尔公司继摩托罗拉后,第二个研制出 32 位的微处理器 80386,开始扩大它在整个半导体工业的市场份额。这个芯片的研制费用超过三亿美元,虽然远远低于现在英特尔新的处理器芯片的研制成本,但在当时确实是一场豪赌,这笔研制费超过中国当时在一个五年计划中对半导体科研全部投入的好几倍。英特尔靠 80386 完成了对 IBM PC 兼容机机市场一统江湖的伟业。

接下来到了 1989 年,英特尔推出了从 80386 到奔腾处理器的过渡产品 80486,它其实是 80386 加一个浮点处理器 80387 以及缓存(Cache)。靠 80486 的销售,英特尔超过所有的日本半导体公司,坐上了半导体行业的头把交椅。顺便说句题外话,今天日本的股市不到 1990 年 40%的水平,可是美国却涨了五倍。1993 年,英特尔公司推出奔腾处理器。从奔腾起,英特尔公司不再以数字命名它的产品了,但是在工业界和学术界,大家仍然习惯性地把英特尔的处理器称为 x86 系列。

奔腾的诞生,使英特尔甩掉了只会做低性能处理器的帽子。由于奔腾处理器的速度已经达到工作站处理器的水平,高端的微机从那时起,开始取代低性能的图形工作站。到今天,即使是最早生产工作站的太阳公司和世界上最大的计算机公司 IBM 以及以前从不使用英特尔处理器的苹果公司,都开始在自己的计算机中使用英特尔的或者和英特尔兼容的处理器了。现在,英特尔已经垄断了计算机处理器市场。

2. 英特尔摩托罗拉之战

资金密集型的日本半导体公司终究不可能是技术密集型的英特尔公司的对手。英特尔公司迄今唯一遇到的重量级对手只有八十年代的摩托罗拉。正如同罗马帝国的崛起是通过在布匿战争中打败原有的霸主迦太基而完成的,英特尔的崛起是靠击败老牌半导体公司摩托罗拉而实现的。摩托罗拉成立于 1928 年,早在二战期间,它就是美军无线通信的供应商。从六十年代起,它在通信和集成电路方面领先于世界。摩托罗拉比英特尔早两年推出在小数运算性能上五倍好于 8086 的 16 位微处理器 68000。68000 这个名字是以它集成的晶体管数目六万八千个而获得的。而 8086 只有不到三万个晶体管。当时,不少工作站包括惠普、太阳和已经不存在的阿波罗等等都采用的是摩托罗拉的处理器。在英特尔搞出 80286 的同一年(1982 年),摩托罗拉推出了在性能上明显好于 80286 的 68010,继续作为当时主要工作站的处理器。据说英特尔为了和摩托罗拉竞争在型号上耍了个小花招,英特尔公司第二代处理器本来应该命名为 80186,但是英特尔将这个编号留给了一个不重要的输入输出处理芯片,而将它的系列处理器的编号一下跳到 80286,不懂技术的人还以为英特尔的处理器比摩托罗拉高一代。在 32 位微处理器的较量中,摩托罗拉在技术上和推出的时间上完全占了上风,它接下来的 68020 明显好于英特尔的 80386,除了被用于主要的工作站上,68020 还并被苹果选为麦金托什的处理器。

这时,英特尔公司从外部得到了强援。由于 IBM PC 兼容机的逐步普及,技术上相对落后的英特尔反而占了更多的市场份额。虽然,摩托罗拉后来又推出了对应于英特尔 80486 的 68030,但是,这时各个工作站公司都开始开发自己减指指令(RISC)的处理器,摩托罗拉只剩下苹果一个用户便很难和英特尔竞争了。几年后,摩托罗拉干脆自己也加入了 RISC 的行列做起 PowerPC,十年后,随着苹果也开始使用英特尔的处理器,摩托罗拉彻底推出了微机处理器市场。

摩托罗拉并没有败在技术和资金上,八十年代以前,摩托罗拉在资金、技术各方面都明显强于英特尔。在很长时间里,它的处理器从性能上讲要优于英特尔的同类产品。摩托罗拉之败,首先是外界微软的因素,即英特尔有了微软这个没有签约的同盟军。但是,摩托罗拉自己在商业、管理和市场诸方面也有很多失误。如果摩托罗拉自己经营得当,它今天应该能通过精简指令集的处理器守住工作站和苹果的市场。

要分析摩托罗拉之败,我们不妨来比较一下英特尔和摩托罗拉这两个公司。首先,这是两个不同时代的公司。总部在美国中部伊利诺斯州的摩托罗拉虽然也是一个高技术公司,也经历了八十年代的信息革命,但是它的作态完全还是五六十年代的传统的公司。虽然摩托罗拉对雇员在工资和福利上待遇不错,但是公司和员工,基本上还是传统的雇佣关系,公司内部管理层次较多,大部分员工基本上没有多少股票期权。因此,公司的业绩和员工的利益关系不大。英特尔公司则是一的典型的硅谷公司。每个员工的工作强度比摩托罗拉要大很多,但是每个人平均的股票期权也多很多。硅谷几个比较好的学区的房子,不少被英特尔公司的早期员工买走了,而这些房子靠工资是一辈子也买不起的。几年前,美国历史频道(History Channel)在节目中评论了中日甲午战争。美国的历史学家认为,这是两个不同时代军队之间的战争,虽然双方武器相差不多,战争的结果不会有任何悬念,因为一个在专制的农业时代后期的军队很难打赢一个兴起的工业化国家的军队。英特尔和摩托罗拉之间的竞争也是如此。

第二,两个公司的统帅水平相去甚远。英特尔公司八九十年代的 CEO 格罗夫虽然是学者出身,同时也是微机时代最优秀的领导者和管理者,他几次被评为世界上最好的 CEO。摩托罗拉公司由加尔文(Galvin)兄弟创办,公司六十年代传到了儿子手里,八九十年代传到了孙子手里,是个典型的家族公司。俗话说富不过三代,这话果然应验在加尔文家族上,三代人可以说是一代不如一代。孙子辈的克里斯托弗•加尔文虽然是被"选成"CEO的,但是如果他不姓加尔文,他永远不可能是摩托罗拉的 CEO,甚至进不了工业界的高层。

在业务上,半导体只是摩托罗拉的一个部门,而微机处理器又只是其半导体部门的一项业务,可是它对于英特尔来讲却是全部。因此,摩托罗拉即使完全退出微机处理器市场也不过是损失一些地盘,而英特尔一旦失败,则会面临灭顶之灾。一般来讲,华尔街总是希望一个上市公司有尽可能多的而不是单一的收入来源,摩托罗拉确实是这么做的,它曾经在计算机的处理器、通信的数字信号处理器、对讲机、BP 机、手机和电视接收器等很多领域发展。结果每个领域都很难做大。英特尔公司做事情非常专注,直到今天,它一直集中精力于个人微机的处理器上。每一代产品的研发都是集中大量的人力和资金,每一次都是只能成功不能失败。这就像一把散线和一股绳,一把散线很容易被一股绳扯断。因此,专注的英特尔最终把计算机处理器的业务做得很大、很好,而业务多元化的摩托罗拉最后除了在微机处理器上败给了英特尔,在手机上碰到了诺基亚,在信号处理器(DSP)上又败给了德州仪器(TI)。很多人问我雅虎有没有可能在搜索领域赶上谷歌,我明确地回答--没有,因为雅虎不可能专注在这个领域。有时,一个好的公司不能完全按华尔街的意愿办事。

如果时光可以倒流,让摩托罗拉和英特尔当时换个个儿,即 IBM PC 采用摩托罗拉的处理器,而将服务器厂家和苹果交给英特尔。那么二十年发展下来,摩托罗拉也很难成为半导体领域的老大,因为它内部的问题没法解决。

3. 指令集之争

英特尔在微软的帮助下,在商业上打赢了对摩托罗拉一战。在接下来的十年里,它在技术上又和全世界打了一战。

当今的计算机系统结构可以根据指令集合分成复杂指令(CISC)和简单指令(RISC)两种。一个计算机的程序最终要变成一系列指令才能在处理器上运行。每个处理器的指令集不相同。有些处理器在设计时候,尽可能地实现各种各样、功能齐全的指令,这包括早期 IBM 和 DEC 的全部计算机,今天的英特尔和 AMD 的处理器等等。采用复杂指令系统的处理器芯片的好处是它可以实现很复杂的指令,但是它存在主要问题有两个,第一,设计复杂,实现同样的性能需要的集成度高;第二,由于每个指令执行时间不一样长,处理器内部各个部分很难流水作业,处理器会出现不必要的等待。除此之外,还有一个过去不是问题现在是问题的缺陷,就是复杂指令芯片高集成度带来的高功耗。

针对复杂指令的处理器的上述两个不足之处,八十年代,计算机科学家们提出了基于精简指令集的处理器设计思想,其代表人物是现任斯坦福大学校长、美国科学院、工程学院和文理学院三院院士轩尼诗(John Hennessy) 教授和加大伯克利分校著名的计算机教授派特森(David Patterson)院士。精简指令系统只保留很少的常用指令并将一条复杂的指令用几条简单的指令代替。基于精简指令集的设计思想是计算机发展史上的一次革命,它使得计算机处理器的设计得到很大简化,同时由于精简指令集的处理器可以保证每条指令执行时间相同,处理器内各部分可以很好地流水作业,处理器速度可以比同时期的基于复杂指令的处理器要来得快。精简指令集的处理器包括很多工作站的处理器和现在最快的 Sony PS/3 游戏机的微处理器 PS/3-Cell。

虽然复杂指令和精简指令的处理器各有千秋,但是在学术界几乎一边倒地认为复杂指令集的设计过时了,精简指令集是先进的。尤其是美国所有大学计算机原理和计算机系统结构两门课全是用轩尼诗和派特森合写的教科书。在很长时间里,书中以介绍轩尼诗自己设计的 MIPS 精简指令芯片为主。同时,IEEE 和 ACM 系统结构的论文也以精简指令为主。英特尔设计 8086 时还没有精简指令的芯片,否则我想,英特尔很可能会采用这种技术,而不是复杂指令系统。而一旦走上了复杂指令这条不归路,英特尔为了和 8086 完全兼容,在以后的 80286 和 80386 中必须继续使用复杂指令系统。在八十年代中后期,不少精简指令的处理器做出来了,包括轩尼诗设计的 MIPS,后来用于 SGI 工作站,以及派特森设计的 RISC,后来用于 IBM 的工作站。精简指令芯片的速度当时比的复杂指令的要快得多。

到了八十年代末,英特尔面临一个选择,是继续设计和以前 x86 兼容的芯片还是转到精简指令的道路上去。如果转到精简指令的道路上,英特尔的市场优势会荡然无存;如果坚持走复杂指令的道路,它就必须逆着全世界处理器发展潮流前进。在这个问题上,英特尔处理的很理智。首先,英特尔必须维护它通过 x86 系列芯片在微处理器市场上确立的领先地位;但是,万一复杂指令的处理器发展到头了,而精简指令代表了未来的发展方向,它也不能坐以待毙。英特尔在推出过渡型复杂指令集的处理器 80486 的同时,推出了基于精简指令集的 80860。这个产品事实证明不很成功,显然,市场的倾向说明了用户对兼容性的要求比性能更重要。因此,英特尔在精简指令上推出 80960 后,就停止了这方面的工作,而专心做"技术落后"的复杂指令系列。在整个九十年代,工业界只有英特尔一家坚持开发复杂指令集的处理器,对抗着整个处理器工业。

应该讲英特尔在精简指令处理器的工作没有白花,它在奔腾及以后的处理器设计上吸取了 RISC 的长处,使得处理器内部流水线的效率提高很多。由于英特尔每一种 PC 机处理器的销量都超过同时代所有的工作站处理器销量的总和,它可以在每个处理器的开发上投入比任何一种精简指令处理器多的多的研发经费和人力,这样,英特尔通过高强度的投入,保证了它处理器性能提升得比精简指令还要快。而在精简指令阵营,九十年代五大工作站厂家太阳、SGI、IBM、DEC 和 HP 各自为战,每家都生产自己的精简指令处理器,加上摩托罗拉为苹果生产的 PowerPC,六家瓜分一个市场,最后谁也做不大、做不好。到了 2000 年前后,各家的处理器都做不下去了,或者全部或者部分地开始采用英特尔的产品了。而最早的精简指令的 MIPS 处理器现在几乎没有人用了。轩尼诗和派特森作为两个负责任的科学家,将英特尔处理器加入到自己编的教科书中,以免大学生们再去学习 MIPS 这样的恐龙。

英特尔经过十年努力终于打赢了对精简指令集的处理器之战。需要强调的是,英特尔不是靠技术,而是靠市场打赢的此战。英特尔的表现在很多地方很值得圈点。首先,英特尔坚持自己系列产品的兼容性,即保证以往的软件程序肯定能在新的处理器上运行。这样时间一长,用户便积累了很多在英特尔处理器上运行的软件。每次处理器升级,用户原来的软件都能使,非常方便。因此大家就不愿意轻易更换其它厂家的处理器,即使那些处理器更快。而其它处理器生产厂家这点做的都没有英特尔好,它们常常每过几年就重起炉灶,害得用户以前很多软件不能用了,必须花钱买新的。时间一长,用户就换烦了。第二,英特尔利用规模经济的优势,大强度投入研发,让业界普遍看衰的复杂指令集处理器一代代更新。在九十年代初,英特尔的 x86 系列和精简指令集的处理器相比在实数运算上要略逊一筹。但是,英特尔十几年来坚持不懈地努力,后来居上,而其它厂商因为各自市场不够大,每一个单独的处理器芯片的投入远远不如英特尔,因此反倒落在了后面。与其说英特尔战胜其它厂商,不如说它把竞争对手熬死了。第三,英特尔并没有拒绝新技术,它也曾经研制出两个不错的精简指令的处理器,只是看到它们前途不好时,立即停掉了它们。第四,英特尔运气很好,在精简指令处理器阵营中,群龙无首。这一战,看似英特尔单挑诸多处理器领域的老大。但是,这几家做精简指令处理器的公司因为彼此在工作站方面是竞争对手,自然不会用对手的产品,而且各自为战,互相拆台打价格战,最后,太阳公司和 IBM 倒是把其他几家工作站公司全收拾了,但自己也无力和英特尔竞争了,现在这两家自己也用上了英特尔的芯片。本来,摩托罗拉最有可能一统精简指令处理器的天下和英特尔分庭抗礼,因为它本身不做工作站,而各个工作站厂商原本都是用它的 68000 系列处理器,但是摩托罗拉自己不争气。原因我们前面已经分析过了。

4. 英特尔和 AMD 的关系

我们在前面提到摩托罗拉公司时用了"英特尔和摩托罗拉之战"的说法,因为,那对于英特尔来讲确实是一场十分凶险的战争,当时摩托罗拉无论在技术还是财力上都略胜一筹。如果英特尔一步走错,它今天就不会存在了。英特尔和诸多精简指令处理器公司之战,可以说有惊无险,因为英特尔已经是内有实力,外有强援。而今天,英特尔和 AMD 之间争夺市场的竞争我个人认为不是一个重量级对手之间的比赛,因此算不上是战争。我想,如果不是反垄断法的约束,英特尔很可能已经把AMD击垮或者收购了。另外,英特尔和 AMD 的关系基本上是既联合又斗争。

AMD 不同于英特尔以往的对手,它从来没有另起炉灶做一种和英特尔不同的芯片,而是不断推出和英特尔兼容的、更便宜的替代品。AMD 的这种做法和它的基因很有关系。AMD 从血缘来讲应该是英特尔的族弟,因为它也是从仙童半导体分出来的,也在硅谷,只比英特尔晚几年,而且也和英特尔一样,从半导体存储器做起。和其它处理器公司不同,AMD 的创始人是搞销售出身的,而一般技术公司创始人都是技术出身。AMD 的这种基因决定了它不是自己会做什么就做什么,而是市场导向的,市场需要什么就做什么。在 AMD 创建不久,它就成功地解刨了英特尔的一个八位处理器芯片。八十年代,由于 IBM 采购的原则是必须有两个以上的公司参加竞标,所以在很长的时间里,Intel 主动让 AMD 帮它生产芯片卖给 IBM 等公司。

到了 1986 年,英特尔不想让 AMD 生产刚刚问世的 80386,可能是想独占 80386 的利润吧,于是开始毁约。AMD 拿出过去的合同请求仲裁,仲裁的结果是 AMD 可以生产 80386。这下子英特尔不干了,上诉到加州高等法院,这个官司打了好几年,但是法院基本上维持了仲裁的结果。AMD于 是便名正言顺地克隆起英特尔的处理器芯片了。当时微机生产厂家,例如康柏为了同英特尔的压价,开始少量采购 AMD 的芯片。几年后,英特尔再次控告 AMD 公司盗用它花几亿美元买来的多媒体处理的 MMX 技术,AMD 做了让步达成和解。在整个九十年代,英特尔和 AMD 虽然打打闹闹但是,它们在开拓 x86 市场,对抗精简指令集的工作站芯片方面利益是一致的。因此它们在市场上的依存要多于竞争。

两千年后美国经济进入低谷,精简指令的工作站的市场一落千丈,太阳公司的股票跌掉了百分之九十几。放眼处理器市场,全是英特尔和 AMD 的天下了。AMD 这次主动出击,利用它提早开发出 64 位处理器的优势,率先在高端市场挑战英特尔,并一举拿下了服务器市场的不少份额。前几年,因为微软迟迟不能推出新的操作系统 Vista,因此个人用户没有动力去更新微机;而同时,因为互联网的发展,网络服务器市场增长很快,对 64 位高端处理器芯片需求大增。这样在几年里,AMD 的业绩不断上涨,一度占有 40% 左右的处理器市场,并且挑起和英特尔的价格战。AMD 同时在世界各地,状告英特尔的垄断行为。到去年年初,AMD 不仅在业绩达到顶峰,而且在对英特尔的反垄断官司上也颇有收获,欧盟等国开始约束英特尔。这样一来,英特尔就不能太小觑 AMD 这个小兄弟了。它决定给 AMD 一些颜色看看。在接下来的一年里,英特尔千呼万唤始出来的酷睿双核处理器终于面世了,性能高于 AMD 同类产品,英特尔重新恢复了它在产品上的领先地位。同时,英特尔在过去的几年里将生产线移到费用比硅谷低得多的俄罗冈州和亚利桑那州,以降低成本,然后,英特尔开始回应价格战。价格战的结果是,英特尔的利润率受到了一些影响,但是 AMD 则从盈利到大幅度亏损。英特尔重新夺回了处理器市场的主动权。现在,两家都是采用 65 纳米的半导体技术。在未来的几年里,英特尔因为在最新的 45 纳米技术上明显领先于 AMD,并且已经开始研发集成度更高的 32 纳米的芯片,它将对 AMD 保持绝对的优势。

我认为,总的来讲,英特尔并没有想彻底把 AMD 打死。因为留着 AMD 对它利大于弊。首先,它避免了反垄断的很多麻烦。今天 AMD 的股值只有英特尔的 5%,后者靠手中的现金就足以买下前者。但是,英特尔不能这么做,否则会有反垄断的大麻烦。其次,留着 AMD 这个对手对英特尔自身的技术进步有好处。柳宗元在他的“敌戒”一文中指出,“秦有六国,兢兢以强;六国既除,訑訑乃亡”。这条规律对于英特尔也适用。英特尔从 1979 年至今,将处理器速度,(如果以小数运算速度来衡量),提高了二十五万倍。如果没有诸多竞争对手的话,它是做不到这一点的。现在它的主要对手只有 AMD 了,从激励自己的角度讲也许要留着它,毕竟,AMD 在技术上不象当年的摩托罗拉和 IBM 那么让英特尔头疼。流传着这么一个玩笑,英特尔的人一天遇到了 AMD 的同行,便说,你们新的处理器什么时候才能做出来,等你们做出来了,我们才会有新的事做。

5. 天步艰难

谷歌研究院院长、美国“人工智能”教科书的作者彼得.诺威格博士有一句很经典的话在业界广为流传:当一个公司的市场份额超过 50% 以后,就不用再想去将市场份额翻番了。言下之意,这个公司就必须去挖掘新的成长点了。在 2000 年后,英特尔公司就是处于这样一个定位。现在,它已经基本上垄断了通用处理器的市场,今后如何发展是它必须考虑的问题。

虽然英特尔在整个半导体工业中仍然只占了一小块,但是,很多市场,尤其是低端的市场、比如存储器市场英特尔是进不去的,也没有必要进去,因此它的成长空间并没有想象的那么大。英特尔的特长是在处理器和与 PC 相关的芯片制造上,因此它很容易往这两个市场发展。但是,迄今为止,它在微机处理器之外的芯片开发上不很成功。比如,前几年,它花了好几个亿开发 PC 的外围芯片,最后以失败告终,现在不得不采用 Marvell 公司的芯片集(chip set)。除了计算机,现在许多电器和机械产品都需要用到处理器,比如,一辆中高档的奔驰轿车里面有上百个各种有计算功能的芯片,而手机对处理器芯片的需求就更不用说了。英特尔一度进入了高端手机处理器的市场,但是,由于英特尔公司开发费用太高,这个部门一直亏损,不得不于去年卖给了 Marvell 公司。至此,英特尔公司在微机处理器以外的努力全部失败。

英特尔公司的商业模式历来是靠大投入大批量来挣钱,同一代的芯片,英特尔的销量可是太阳公司的十倍甚至更多,因此,它可以花几倍于其他公司的经费来开发一个芯片。但是,当一种芯片市场较小时,英特尔公司很难做到盈利。现在,这是它面临的最大问题。

英特尔公司要做的第二件事就是防止开发精简指令集处理器公司例如 IBM 的死灰复燃。虽然在个人微机的市场上,英特尔 x86 系列的处理器在很长的时间内是不可替代的,因为有微软在操作系统中为它保驾。但是在服务器市场却不一定,因为,现在服务器主要的操作系统是开源的 Linux,而 Linux 在什么处理器上都可以运行,因此只要有一种处理器各方面性能明显优于英特尔的,购买服务器的客户就会考虑采用非英特尔的处理器。在能源紧缺的今天,服务器厂家对处理器最关心的已经不单单是速度,而是单位能耗下的速度。现在,一个酷睿处理器如果昼夜不停使用,一年的耗电量已经等同于它的价格。因此,今后处理器设计必须考虑能耗。虽然这两年英特尔已经开始重视这个问题,但是总的来讲,英特尔复杂指令的芯片不如精简指令的处理器设计简单,相对比较难做到低能耗。这样,精简指令集的处理器,有可能在服务器市场上和英特尔一争,虽然这不会动摇英特尔的根基。我个人认为,在个人微机以外,今后最重要的市场是游戏机市场。现在的游戏机早已不单单是为玩游戏设计的了,它们成为每个家庭的娱乐中心。IBM 等公司至少在目前在这个领域是领先的。IBM 已经垄断了任天堂、Sony 和微软三大游戏机的处理器市场。实际上,现在这些采用精简指令处理器的游戏机无论是从计算速度还是图形功能上讲,都已经超过了基于英特尔处理器的个人电脑。如果下一次技术革命发生在每个家庭的客厅,那么,IBM 无疑已经拔了头筹。

英特尔虽然雄霸个人电脑处理器市场,但随着个人微机市场的饱和,它远景不容乐观。从某种程度上讲,它是反摩尔定理最大的受害者,因为处理器的价格在不断下降。同时,它在新市场的开拓上举步艰难,很难摆脱“诺威格效应”的阴影。好在英特尔同时也是安迪-比尔定理的直接受益者,在可以预见的将来,它的发展很大程度上必须依赖于微软等公司软件的更新。

结束语

在个人微机时代,组装甚至制造微机是一件非常容易的事,连我本人都攒PC机卖过。因此,二十几年来,出现了无数的微机品牌,小到中关村攒出来的自己贴牌子的兼容机,大到占世界绝大部分市场的所谓品牌机,如戴尔、惠普和联想。虽然这些计算机配置和性能大相庭径,但是它们都使用微软的操作系统和英特尔系列的处理器。从这个角度讲,微机时代的领导者只有两个,软件方面的微软和硬件方面的英特尔。有人甚至把 PC 机行业称为英特尔/微软体制。

英特尔对世界最大的贡献在于,它证明了处理器公司可以独立于计算机整机公司而存在。在英特尔以前,所有计算机公司都必须自己设计处理器,这使得计算机成本很高,而且无法普及。英特尔不断地为全世界的各种用户提供廉价的、越来越好的处理器,直接地使个人微机得以普及。它大投入、大批量的做法成为当今半导体工业的典范。它无疑是过去二十年信息革命大潮中最成功的公司之一,但是今后除非它能找到新的成长点,否则它会随着 PC 时代的过去而进入自己平和的中老年期。

发表者:Google(谷歌)研究员 吴军

第四章 计算机工业的生态链(zz)

目录
1. 摩尔定理(Moore’s Law)
2. 安迪-比尔定理 (Andy and Bill’s Law)
3. 反摩尔定理 (Reverse Moore’s Law)

整个信息技术(Information Technologies,简称 IT)产业包括很多领域、很多环节,这些环节之间都是互相关联的。和世界上任何事物同样, IT 产业也是不断变化和发展并且有着它自身发展规律的。这些规律,被 IT 领域的人总结成一些定理,称为 IT 定理(IT Laws)。我们结合一些具体的例子,分几次介绍这些定理。在这一章中,我们将介绍摩尔定理、安迪-比尔定理和反摩尔定理。这三个定理和在一起,描述了 IT 产业中最重要的组成部分 — 计算机行业的发展的规律。


1. 摩尔定理(Moore’s Law)

科技行业流传着很多关于比尔•盖茨的故事,其中一个是他和通用汽车公司老板之间的对话。盖茨说,如果汽车工业能够像计算机领域一样发展,那么今天,买一辆汽车只需要 25 美元,一升汽油能跑四百公里。通用汽车老板反击盖茨的话我们暂且不论,这个故事至少说明计算机和整个 IT 行业的发展比传统工业要快得多。

最早看到这个现象的是英特尔公司的创始人戈登•摩尔(Gordon Moore)博士。早在 1965 年,他就提出,在至少十年内,集成电路的集成度会每两年翻一番。后来,大家把这个周期缩短到十八个月。现在,每十八个月,计算机等 IT 产品的性能会翻一番;或者说相同性能的计算机等 IT 产品,每十八个月价钱会降一半。虽然,这个发展速度令人难以置信,但几十年来 IT 行业的发展始终遵循着摩尔定理预测的速度。

一九四五年,世界上第一台电子计算机 ENIAC 的速度是能在一秒钟完成 5000 次定点的加减法运算。这个三十米长、两米多高的庞然大物,重 27 吨,耗电十五万瓦。今天,使用英特尔酷睿的个人电脑计算速度是每秒 500 亿次浮点运算,至少是 ENIAC 的一千万倍,体积耗电量就更不用比了。而当今(2007 年 6 月)世界上最快的计算机 IBM 的蓝色基因(BlueGene/L),速度高达每秒钟三百六十七万亿次浮点运算,是 ENIAC 的七百三十四亿倍,正好是每二十个月翻一番,和摩尔定理的预测大致相同。尽管计算机的速度如此,存储容量的增长更快,大约每十五个月就翻一番。1976 年,苹果计算机的软盘驱动器容量为 160KB,大约能存下 80 页的中文书。今天,同样价钱的台式个人电脑硬盘容量可以到 500GB,是当时苹果机的三百万倍,可以存得下北京大学图书馆藏书的全部文字部分。不仅如此,这十几年来,网络的传播速度也几乎是按摩尔定理预测的速度在增长。十三年前,我有幸成为中国第一批上网的用户,那时还是通过高能物理所到斯坦福大学线性加速实验室的一根专用线路和互联网相联,当时电话调制解调器的速度是 2.4K,如果下载谷歌拼音输入法需要八个小时。现在,商用的 ADSL 通过同样一根电话线可以做到 10M 的传输率,是十三年前的四千倍,几乎每年翻一番,下载谷歌拼音只要十秒钟左右。在世界经济的前五大行业中,即金融、信息技术(IT)、医疗和制药、能源和日用消费品,只有 IT 一个行业可以以持续翻番的速度进步。

人们多次怀疑摩尔定理还能适用多少年,就连摩尔本人一开始也只认为 IT 领域可以按这么高的速度发展十年。而事实上,从二战后至今,IT 领域的技术进步一直是每一到两年翻一番,至今看不到停下来的迹象。在人类的文明史上,没有任何一个其它行业做到了这一点。因此,IT 行业必然有它的特殊性。

和任何其它商品相比,IT 产品的制造所需的原材料非常少,成本几乎是零。以半导体行业为例,一个英特尔的酷睿双核处理器集成了二点九亿个晶体管,三十年前的英特尔 8086 处理器仅有三万个晶体管。虽然二者的集成度相差近一万倍,但是所消耗的原材料差不太多。IT 行业硬件的制造成本主要是制造设备的成本。据半导体设备制造商 Applied Materials 公司介绍,建一条能生产 65 纳米工艺酷睿双核芯片的生产线,总投资在 20 到 40 亿美元。去年,英特尔公司的研发费用为六十亿美元。当然,我们不能将它全部算到酷睿的头上,但是英特尔平均一年也未必能研制出一个酷睿这样的产品,所以它的研发费用应当和英特尔一年的预算相当。假如我们将这两项成本平摊到前一亿片酷睿处理器中,平均每片要摊上近一百美元。这样,当英特尔公司收回生产线和研发两项主要成本后,酷睿处理器就可以大幅度降价。去年英特尔处理器销量在两亿片左右,因此,一种新的处理器收回成本的时间不会超过一年半。通常,用户可以看到,一般新的处理器发布一年半以后,价格会开始大幅下调。当然,英特尔的新品此时也已经在研发中。

摩尔定理主导着 IT 行业的发展。首先,为了能使摩尔定理成立,IT 公司必须在比较短的时间内完成下一代产品的开发。这就要求,IT 公司在研发上必须投入大量的资金,这使得每个产品的市场不会有太多的竞争者。在美国,主要 IT 市场大都只有一大一小两个主要竞争者。比如,在计算机处理器芯片方面,只有英特尔和 AMD;在高端系统和服务方面,只有 IBM 和太阳;在个人电脑方面,是惠普和戴尔(戴尔这个有点奇怪,它的研发投入大么?)。其次,由于有了强有力的硬件支持,以前想都不敢想的应用会不断涌现。比如,二十年前,将高清晰度电影(1920 x 1080 分辨率)数字化的计算量连 IBM 的大型机也无法胜任;现在,一台笔记本大小的 Sony 游戏机就可以做到。这就为一些新兴公司的诞生创造了条件。比如,在十年前,不会有人去想办一个 YouTube 这样的公司,因为那时候网络的速度无法满足在网上看录像的要求;现在 YouTube 已经融入了老百姓的生活。同样,现在的研发必须针对多年后的市场。我们不妨往后看十年,如果我现在提出十年后每家上网的速度将提高一千倍,也许有人觉得我疯了。事实上,这是一个完全能够达到的目标。如果做到了这一点,我们每个家庭可以同时点播三部高清晰度、环绕立体声的电影,在三个不同的电视机上收看。还可以随时快进和跳跃到下一章节,在任何时候停下来后,下次可以接着看。在看三部电影的同时,我们可以把自己的照片、录像和文件等信息存到一个在线的服务器上,从家里访问起来就如同存在自己本机上一样快。这并不是我自己杜撰出来的幻想,而是思科(Cisco)和微软等公司实施的 IP TV 的计划。再次,现有的 IT 公司必须有办法消除摩尔定理带来的不利因素,即每十八个月价格降一半。这一点,我们在接下来的两节中在讨论。

2. 安迪-比尔定理 (Andy and Bill’s Law)

摩尔定理给所有的计算机消费者带来一个希望,如果我今天嫌计算机太贵买不起,那么我等十八个月就可以用一半的价钱来买。要真是这样简单的话,计算机的销售量就上不去了。需要买计算机的人会多等几个月,已经有计算机的人也没有动力更新计算机。其它的 IT 产品也是如此。

事实上,在过去的二十年里,世界上的个人微机销量在持续增长。2004 年,英特尔公司估计,五年内,即到 2009 年,世界上 PC(包括个人机和小型服务器)的销量会增长 60%,远远高于经济的增长。那么,是什么动力促使人们不断地更新自己的硬件呢?IT 界把它总结成安迪-比尔定理,即比尔要拿走安迪所给的(What
Andy gives, Bill takes away)。

安迪是原英特尔公司 CEO 安迪•格鲁夫(Andy Grove),比尔就是微软的创始人比尔•盖茨。在过去的二十年里,英特尔处理器的速度每十八个月翻一番,计算机内存和硬盘的容量以更快的速度在增长。但是,微软的操作系统等应用软件越来越慢,也越做越大。所以,现在的计算机虽然比十年前快了一百倍,运行软件感觉上还是和以前差不多。而且,过去整个视窗操作系统不过十几兆大小,现在要几千兆,应用软件也是如此。虽然新的软件功能比以前的版本强了一些,但是,增加的功能绝对不是和它的大小成比例的。因此,一台十年前的计算机能装多少应用程序,现在的也不过装这么多,虽然硬盘的容量增加了一千倍。更糟糕的是,用户发现,如果不更新计算机,现在很多新的软件就用不了,连上网也是个问题。而十年前买得起的车却照样可以跑。

这种现象,乍一看来是微软在和大家做对。实际上,盖茨本人和其它厂商也不想把操作系统和应用程序搞得这么大。据了解,盖茨本人多次说,他过去搞得 BASIC 只有几十 K,你们(微软工程师们)搞一个.NET 就要几百兆,其中一定可以优化。当然,我们知道微软现在的.NET 比二十年前的 BASIC 功能要强的多,但是否强了一万倍,恐怕没有人这么认为。这说明,现在软件开发人员不再像二十年前那样精打细算了。我们知道,当年的 BASIC 解释器是用汇编语言写成的,精炼得不能再精炼了,否则在早期的 IBM-PC 上根本运行不了。但是,要求软件工程师使用汇编语言编程,工作效率是极低的,而且写出的程序可读性很差,不符合软件工程的要求。今天,由于有了足够的硬件资源,软件工程师做事情更讲究自己的工作效率,程序的规范化和可读性等等。另外,由于人工成本的提高,为了节省软件工程师写程序和调程序的时间,编程的语言越来越好用,同时效率却越来越低。比如,今天的 Java 就比 C++ 效率低得多,C++ 又比二十年前的 C 效率低。因此,即使是同样功能的软件,今天的比昨天的占用硬件资源多是一件在所难免的事。

虽然用户很是烦恼新的软件把硬件提升所带来的好处几乎全部用光,但是在 IT 领域,各个硬件厂商恰恰是靠软件开发商用光自己提供的硬件资源得以生存。举个例子,到去年上半年为止,因为微软新的操作系统 Vista 迟迟不能面市,从英特尔到惠普、戴尔等整机厂商,再到 Marvell 和 Seagate 等外设厂商,全部销售都受到很大的影响,因为用户没有更新计算机的需求。这些公司的股票不同程度地下跌了 20% 到 40%。去年底,微软千呼万唤始出来的 Vista 终于上市了,当然微软自己的业绩和股票马上得到提升,萧条了一年多的英特尔也在今年初扭转的颓势,当然惠普和戴尔也同时得到增长。今年,这三家公司的股票都有大幅度上涨。接下来不出意外的话,该轮到硬盘、内存和其它计算机芯片的厂商开始复苏了。Vista 相比前一个版本 XP,也许多提供了 20% 的功能,但是它的内存使用几乎要翻两番,CPU 使用要翻一番,这样,除非是新机器,否则无法运行 Vista。当然,用户可以选择使用原来的操作系统 XP,但是很快的,微软和其它软件开发商会逐渐减少对 XP 系统的支持,这样就逼着用户更新机器。

我们可以看出,个人电脑工业整个的生态链是这样的:以微软为首的软件开发商吃掉硬件提升带来的全部好处,迫使用户更新机器让惠普和戴尔等公司收益,而这些整机生产厂再向英特尔这样的半导体厂订货购买新的芯片、同时向 Seagat e等外设厂购买新的外设。在这中间,各家的利润先后得到相应的提升,股票也随着增长。各个硬件半导体和外设公司再将利润投入研发,按照摩尔定理制定的速度,提升硬件性能,为微软下一步更新软件、吃掉硬件性能做准备。华尔街的投资者都知道,如果微软的开发速度比预期的慢,软件的业绩不好,那么就一定不能买英特尔等公司的股票了。

对用户来讲,现在买一台能用的计算机和十年前买一台当时能用的计算机,花出去的钱是差不多的,如果不是“中国制造”效应的影响,还会因为通货膨胀略有提高。(这句话是不是应该是这样:如果不是“中国制造”效应的影响,对用户来讲,现在买一台能用的计算机和十年前买一台当时能用的计算机,花出去的钱是差不多的,甚至还会因为通货膨胀略有提高。原来的意思是现在同原来没有什么差别,因为我自己感觉是便宜了。修改后的结果是因为中国制造,现在比原来便宜了。)当然,微软和其它软件开发商在吃掉大部分硬件提升好处的同时,或多或少地会给用户带来一些新东西。

如果说在美国,始于二十年前的信息革命是基于个人电脑和互联网的,那么在亚洲,主流则是手机和移动通信。今天的手机一般都有两个处理器,一个数字信号处理器(DSP)和一个与微机处理器类似的通用处理器(CPU)今天,一个中档手机的计算性能,超过了五年前的个人微机,而且还按着摩尔定理预计的速度在增长。虽然在手机行业,并没有一个类似微软的通用操作系统公司存在,但是手机制造商自己、运营商和增值服务商加在一起起到了微软的作用。它们在提供新的但是越来越消耗资源的服务,使得用户不得不几年更新一次手机。

就这样,安迪-比尔定理把原本属于耐用消费品的电脑、手机等商品变成了消耗性商品,刺激着整个 IT 领域的发展。

3. 反摩尔定理 (Reverse Moore’s Law)

Google(谷歌)的 CEO 埃里克•施密特在一次采访中指出,如果你反过来看摩尔定理,一个 IT 公司如果今天和十八个月前卖掉同样多的、同样的产品,它的营业额就要降一半。IT 界把它称为反摩尔定理。反摩尔定理对于所有的 IT 公司来讲,都是非常可悲的,因为一个 IT 公司花了同样的劳动,却只得到以前一半的收入。反摩尔定理逼着所有的硬件设备公司必须赶上摩尔定理规定的更新速度。事实上,所有的硬件和设备生产厂活得都是非常辛苦的。下表中列举了各个领域最大的公司今天的股值和他们最高值地比例。

IBM: 82%.
CISCO: 40%
Intel: 33%
AMD: 30%
Marvel: 60%
HP: 70%
Dell: 35%
Sun Microsystems: 10%
Motorola: 33%

这里面,除了 IBM 不单纯是硬件厂商,而有很强的服务和软件收入得以将股票维持在较高的水平,其余的公司和它们最好水平相去甚远。而今天,美国股市几乎是在历史最高点。这说明,以硬件为主的公司因为反摩尔定理的影响,生计之艰难。如果有兴趣读一读这些公司财报的话,就会发现,这些公司的发展波动性很大,一旦不能做到摩尔定理规定的发展速度,它们的盈利情况就会一落千丈。有的公司甚至会有灭顶之灾,比如十年前很红火的 SGI 公司。即使今天它们发展的不错,却不能保证十年以后仍然能拥有翻番进步,因此,投资大师巴菲特从来不投这些 IT 公司。

事实上,反摩尔定理积极的一面更为重要,它促成科技领域质的进步,并为新兴公司提供生存和发展的可能。和所有事物的发展一样,IT 领域的技术进步也有量变和质变两种。比如说,同一种处理器在系统结构(Architecture)没有太大变化,而只是主频提高了,这种进步就是量变的进步。当处理器由十六位上升到三十二位,再到六十四位时,就有了小的质变。如果哪一天能用到纳米技术或者生物技术,那么就做到了质的飞跃,半导体的集成度会有上百倍的提高。为了赶上摩尔定理预测的发展速度,光靠量变是不够的。每一种技术,过不了多少年,量变的潜力就会被挖掘光,这时就必须要有革命性的创造发明诞生。

在科技进步量变的过程中,新的小公司是无法和老的大公司竞争的,因为后者在老的技术方面有无以伦比的优势。比如,木工厂出身的诺基亚在老式的模拟手机上是无法和传统的通信设备老大摩托罗拉竞争的。但是,在抓住质变机遇上,有些小公司会做得比大公司更好而后来居上,因为它们没有包袱,也比大公司灵活。这也是硅谷出现了众多的新技术公司的原因。

十三年前,我最早上网时用的是一个 2.4千 波特率(Kbps)的调制解调器。两年后,我的一个同学,中国最早的互联网公司东方网景的创始人送了我一个当时最新的 14.4 Kbps 的调制解调器,我马上感觉速度快多了。由于我们今天数字电话传输率本身限制在 64Kbps,因此调制解调器的传输率最多到 56Kbps,所以到 1995 年,我的几个同事就预言用电话线上网速度超不过这个极限。如果停留在用传统的方法对调制解调器提速,确实要不了几年摩尔定理就不适用了。但是到了九十年代,出现了 DSL 技术,可以将电话线上的数据传输速度提高近二百倍。DSL 技术虽然最早由贝尔核心实验室发表,但真正把它变为实用技术的是斯坦福大学的约翰•查菲教授。查菲教授三十几岁就成为了 IEEE 的资深会员(Fellow),刚四十岁就成为了美国工程院院士。1991 年,他带着自己的几个学生,办起了一家做 DSL 的小公司 Amati。1997 年,他把 Amati 公司以四亿美元的高价卖给了德州仪器(TI)。这是硅谷新技术公司典型的成功案例。在调制解调器发展的量变阶段,就不会有 Amati 这样的小公司出现,即使出现了,也无法和德州仪器竞争。但是,一旦调制解调器速度接近原有的极限时,能够突破这个极限的新兴公司就有机会登上历史的舞台。

反摩尔定理使得 IT 行业不可能像石油工业或者是飞机制造业那样只追求量变,而必须不断寻找革命性的创造发明。因为任何一个技术发展赶不上摩尔定理要求的公司,用不了几年就会被淘汰。大公司们,除了要保持很高的研发投入,还要时刻注意周围和自己相关的新技术的发展,经常收购有革命性新技术的小公司。它们甚至出钱投资一些有希望的小公司。在这方面,最典型的代表是思科公司,它在过去的二十年里,买回了很多自己投资的小公司。

反摩尔定理同时使得新兴的小公司有可能在发展新技术方面和大公司处在同一个起跑线上。如果小公司办得成功,可以像 Amati 那样被大公司并购(这对创始人、投资者以及所有的员工都是件好事)。甚至它们也有可能取代原有大公司在各自领域中的地位。例如,在通信芯片设计上,广通(BroadCom)和 Marvell 在很大程度上已经取代了原来的朗讯的半导体部门,甚至是英特尔公司在相应领域的业务。

当然,办公司是需要钱的,而且谁也不能保证对一个新兴公司的投资一定能够得到收益。有些愿意冒风险而追求高回报的投资家将钱凑在一起,交给既懂得理财又懂得技术的专业人士打理,投给有希望的公司和个人,这就渐渐形成了美国的风险投资机制。办好一个高科技公司还需要有既志同道合又愿意承担风险的专业人才,他们对部分拥有一个公司比相对高的工资更感兴趣,因此就有了高科技公司员工的期权制度。

IT 行业发展至今,自有它的生存发展之道。它没有因为价格的不断下降而萎缩,而是越来越兴旺。我们以后还会陆续介绍它的发展规律。

发表者:Google(谷歌)研究员 吴军

常用符号的英文读音

English Symbol:
\ backslash
/ slash (solidus)
{ left curly brace
} right curly brace
! exclamation mark
" quotation mark
% percent sign
& ampersand
( left parenthesis
) right parenthesis
* asterisk (star)
, comma
- hyphen
. period (fullstop)
# number sign
$ dollar sign
' apostrophe
: colon
; semicolon
< less than
= equals sign
> greater than
? question mark
@ commercial at
[ left square bracket
] right square bracket
^ caret
_ underscore (horizontal bar)
` acute accent
~ tilde
| vertical bar

Latin characters
α.Α.alpha β.Β.beta γ.Γ.gamma δ.Δ.delta
ε.Ε.epsilon ζ.Ζ.zeta η.Η.eta θ.Θ.theta
ι.Ι.iota κ.Κ.kappa λ.Λ.lambda μ.Μ.mu
ν.Ν.nu ξ.Ξ.xi ο.Ο.omicron π.Π.pi
ρ.Ρ.rho σ.Σ.sigma τ.Τ.tau υ.Υ.upsilon
φ.Φ.phi χ.Χ.chi ψ.Ψ.psi ω.Ω.omega