by Meng Yan on Nov.15, 2006, under Other
微Y著名的C++大师Herb Sutter?005q初的时候曾l写q一重量的文章:”The Free Lunch Is Over: A Fundamental Turn Toward Concurrency in Software“Q预aOO之后软g开发将要面临的又一ơ重大变?q行计算?/p>
摩尔定律l制下的软g开发时代有一个非常有意思的现象Q?#8221;Andy giveth, and Bill taketh away.”。不CPU的主频有多快Q我们始l有办法来利用它Q而我们也陉在机器升U带来的E序性能提高中?/p>
我记着我大二的时候曾l做q一个五子棋的程序,当时的算法就是预先设计一些棋型(有优先Q,然后扫描盘Q对形势q行分析Q看看当前走哪部对自己最重要。当然下还要堵别hQ这需要互换双方的型再计。如果只一步,很可能被狡猾的对手欺骗,所以ؓ了多惛_步,q需要递归和回朔。在当时的机器上Q算3步就基本上需?U左右的旉了。后来大学毕业收拾东西的时候找到这个程序,试了一下,发现?0步需要的旉也基本上感觉不出来了?/p>
不知道你是否有同Ll历Q我们不知不觉的一直在享受着q样的免费午。可是,随着摩尔定律的提前终l,免费的午终I要q回厅R虽然硬件设计师q在努力QHyper Threading CPUQ多Z套寄存器Q相当于一个逻辑CPUQ得Pipeline可能满负荷Q多个Thread的操作有可能q行Q得多U程E序的性能?%-15%的提升;增加Cache定w也得包括Single-Thread和Multi-ThreadE序都能受益。也许这些还能帮助你一D|_但问题是Q我们必d出改变,面对q个卛_到来的变革,你准备好了么Q?/p>
Concurrency Programming != Multi-Thread Programming。很多h都会说MultiThreading谁不会,问题是,你是Z么?如何使用多线E的Q我从前做过一个类似AcdSee一L囑փ查看/处理E序Q我通常用它来处理我的数码照片。我在里面用了大量的多线E,不过主要目的是在囑փ处理的时候不要Block住UIQ所以将CPU Intensive的计部分用后台U程q行处理。而ƈ没有把对囑փ矩阵的运ƈ行分开?/p>
我觉得Concurrency Programming真正的挑战在于Programming Model的改变,在程序员的脑子里面要对自qE序怎样q行化有很清楚的认识Q更重要的是Q如何去实现Q包括架构、容错、实时监控等{)q种q行化,如何?strong>调试Q如何去试?/p>
在GoogleQ每天有量的数据需要在有限的时间内q行处理Q其实每个互联网公司都会到q样的问题)Q每个程序员都需要进行分布式的程序开发,q其中包括如何分布、调度、监控以及容错等{。Google?a >MapReduce正是把分布式的业务逻辑从这些复杂的l节中抽象出来,使得没有或者很ƈ行开发经验的E序员也能进行ƈ行应用程序的开发?/p>
MapReduce中最重要的两个词是MapQ映)和ReduceQ规U)。初看Map/Reduceq两个词Q熟悉Function Language的h一定感觉很熟悉。FP把这L函数UCؓ”higher order function”Q?#8221;High order function”被成为Function Programming的利器之一哦)Q也是_q些函数是编写来被与其它函数相结合(或者说被其它函数调用的Q。如果说要比的化,可以把它惌成C里面的CallBack函数Q或者STL里面的Functor。比如你要对一个STL的容器进行查找,需要制定每两个元素相比较的FunctorQComparatorQ,q个Comparator在遍历容器的时候就会被调用?/p>
拿前面说q图像处理程序来举例Q其实大多数的图像处理操作都是对囑փ矩阵q行某种q算。这里的q算通常有两U,一U是映射Q一U是规约。拿两种效果来说Q?#8221;老照?#8221;效果通常是强化照片的G/B|然后Ҏ个象素加一些随机的偏移Q这些操作在二维矩阵上的每一个元素都是独立的Q是Map操作。?#8221;雕刻”效果需要提取图像边~,需要元素之间的q算了,是一UReduce操作。再举个单的例子Q一个一l矩阵(数组Q[0,1,2,3,4]可以映射为[0,2,3,6,8]Q乘2Q,也可以映ؓ[1,2,3,4,5]Q加1Q。它可以规约?Q元素求U)也可以规Uؓ10Q元素求和)?/p>
面对复杂问题Q古人教导我们要“?/strong>?strong>?/strong>?#8221;Q英文中对应的词?#8221;Divide and Conquer“。Map/Reduce其实是Divide/Conquer的过E,通过把问题DivideQɘq些Divide后的Mapq算高度q行Q再Map后的l果ReduceQ根据某一个KeyQ,得到最l的l果?/p>
Googler发现q是问题的核心,其它都是共性问题。因此,他们把MapReduce抽象分离出来。这PGoogle的程序员可以只关心应用逻辑Q关心根据哪些Key把问题进行分解,哪些操作是Map操作Q哪些操作是Reduce操作。其它ƈ行计中的复杂问题诸如分布、工作调度、容错、机器间通信都交lMap/Reduce FrameworkdQ很大程度上化了整个~程模型?/p>
MapReduce的另一个特ҎQMap和Reduce?strong>输入和输出都是中间时文?/strong>QMapReduce利用Google文gpȝ来管理和讉Kq些文gQ,而不是不同进E间或者不同机器间的其它通信方式。我觉得Q这是Google一贯的风格Q化JؓQ返璞归真?/p>
接下来就放下其它Q研I一下Map/Reduce操作。(其它比如定w、备份Q务也有很l典的经验和实现Q论文里面都有详qͼ Map的定义: Map, written by the user, takes an input pair and produces a set of intermediate key/value pairs. The MapReduce library groups together all intermediate values associated with the same intermediate key I and passes them to the Reduce function. Reduce的定义: The Reduce function, also written by the user, accepts an intermediate key I and a set of values for that key. It merges together these values to form a possibly smaller set of values. Typically just zero or one output value is produced per Reduce invocation. The intermediate values are supplied to the user’s reduce function via an iterator. This allows us to handle lists of values that are too large to fit in memory. MapReduce论文中给Zq样一个例子:在一个文档集合中l计每个单词出现的次数?/p>
Map操作的输入是每一文档,输入文档中每一个单词的出现输出C间文件中厅R?/p>
map(String key, String value): 比如我们有两文档,内容分别?/p>
A Q?“I love programming” B Q?“I am a blogger, you are also a blogger”?/p>
B文档l过Mapq算后输出的中间文g会是: Reduce操作的输入是单词和出现次数的序列。用上面的例子来_是 (”I”, [1, 1]), (”love”, [1]), (”programming”, [1]), (”am”, [1]), (”a”, [1,1]) {。然后根据每个单词,出ȝ出现ơ数?/p>
reduce(String key, Iterator values): 最后输出的最l结果就会是Q?”I”, 2″), (”a”, 2″)…… 实际的执行顺序是Q?/p>
// key: document name
// value: document contents
for each word w in value:
EmitIntermediate(w, “1″); I,1
am,1
a,1
blogger,1
you,1
are,1
a,1
blogger,1
// key: a word
// values: a list of counts
int result = 0;
for each v in values:
result += ParseInt(v);
Emit(AsString(result));
盘上,q且把文件信息传回给MasterQMaster需要把q些信息发送给Reduce workerQ。这里最重要的一ҎQ?strong>在写盘的时候,需要将中间文g做PartitionQ比如R个)。拿上面的例子来举例Q如果把所有的信息存到一个文ӞReduce worker又会变成瓉。我们只需要保?strong>相同Key能出现在同一个Partition里面可以把q个问题分解?
可见Q这里的分(DivideQ体现在两步Q分别是输入分成M份,以及Map的中间结果分成R份。将输入分开通常很简单,Map的中间结果通常?#8221;hash(key) mod R”q个l果作ؓ标准Q保证相同的Key出现在同一个Partition里面。当Ӟ使用者也可以指定自己的Partition FunctionQ比如,对于Url KeyQ如果希望同一个Host的URL出现在同一个PartitionQ可以用”hash(Hostname(urlkey)) mod R”作ؓPartition Function?/p>
对于上面的例子来_每个文档中都可能会出现成千上万的 (”the”, 1)q样的中间结果,琐碎的中间文件必然导致传输上的损失。因此,MapReduceq支持用h供Combiner Function。这个函数通常与Reduce Function有相同的实现Q不同点在于Reduce函数的输出是最l结果,而Combiner函数的输出是Reduce函数的某一个输入的中间文g?/p>
Tom Whitel出了Nutch[2]中另一个很直观的例子,分布式Grep。我一直觉得,Pipe中的很多操作Q比如More、Grep、Cat都类g一UMap操作Q而Sort、Uniq、wc{都相当于某UReduce操作?/p>
加上前两天Google刚刚发布?a >BigTable论文Q现在Google有了自己的集?- Googel ClusterQ分布式文gpȝ - GFSQ分布式计算环境 - MapReduceQ分布式l构化存?- BigTableQ再加上Lock Service。我真的能感觉的到Google著名的免Ҏ之外的对于E序员的另一U免费的晚餐Q那个由大量的commodity PCl成的large clusters。我觉得q些才真正是Google的核心h值所在?/p>
呵呵Q就像微软老兵Joel SpolskyQ你应该看过他的”Joel on Software”吧?Q曾l说q,对于微Y来说最可怕的是[1]Q微软还在苦苦追赶Google来完善Search功能的时候,Google已经在部|下一代的计算Z?/p>
The very fact that Google invented MapReduce, and Microsoft didn’t, says something about why Microsoft is still playing catch up trying to get basic search features to work, while Google has moved on to the next problem: building Skynet^H^H^H^H^H^H the world’s largest massively parallel supercomputer. I don’t think Microsoft completely understands just how far behind they are on that wave.
?Q其实,微Y也有自己的方?- DryAd。问题是Q大公司里,要想重新部vq样一个底层的InfraStructureQ无论是技术的原因Q还是政ȝ原因Q将是如何的难?/p>
?Q?a >Lucene之父Doug Cutting的又一力作QProject Hadoop - 由Hadoop分布式文件系l和一个Map/Reduce的实现组成,Lucene/Nutch的成产线也够齐全的了?/p>