Allopurinol For Sale

微软著名的C++大师Herb Sutter在2005年初的时候曾经写过一篇重量级的文章:"The Free Lunch Is Over: A Fundamental Turn Toward Concurrency in Software",预言OO之后软件开发将要面临的又一次重大变革-并行计算。

Allopurinol For Sale, 摩尔定律统制下的软件开发时代有一个非常有意思的现象:"Andy giveth, and Bill taketh away."。不管CPU的主频有多快,我们始终有办法来利用它,而我们也陶醉在机器升级带来的程序性能提高中。

我记着我大二的时候曾经做过一个五子棋的程序,当时的算法就是预先设计一些棋型(有优先级),然后扫描棋盘,对形势进行分析,看看当前走哪部对自己最重要。当然下棋还要堵别人,这就需要互换双方的棋型再计算。如果只算一步,很可能被狡猾的对手欺骗,所以为了多想几步,还需要递归和回朔。在当时的机器上,算3步就基本上需要3秒左右的时间了。后来大学毕业收拾东西的时候找到这个程序,试了一下,发现算10步需要的时间也基本上感觉不出来了。

不知道你是否有同样的经历,我们不知不觉的一直在享受着这样的免费午餐。可是,随着摩尔定律的提前终结,免费的午餐终究要还回去。虽然硬件设计师还在努力:Hyper Threading CPU(多出一套寄存器,相当于一个逻辑CPU)使得Pipeline尽可能满负荷,使多个Thread的操作有可能并行,使得多线程程序的性能有5%-15%的提升;增加Cache容量也使得包括Single-Thread和Multi-Thread程序都能受益。也许这些还能帮助你一段时间,但问题是,我们必须做出改变,面对这个即将到来的变革,你准备好了么?

Concurrency Programming != Multi-Thread Programming。很多人都会说MultiThreading谁不会,问题是,你是为什么使用/如何使用多线程的?我从前做过一个类似AcdSee一样的图像查看/处理程序,我通常用它来处理我的数码照片。我在里面用了大量的多线程,不过主要目的是在图像处理的时候不要Block住UI,所以将CPU Intensive的计算部分用后台线程进行处理。而并没有把对图像矩阵的运算并行分开。

我觉得Concurrency Programming真正的挑战在于Programming Model的改变,在程序员的脑子里面要对自己的程序怎样并行化有很清楚的认识,更重要的是,如何去实现(包括架构、容错、实时监控等等)这种并行化,如何去调试,如何去测试

在Google,每天有海量的数据需要在有限的时间内进行处理(其实每个互联网公司都会碰到这样的问题),每个程序员都需要进行分布式的程序开发,这其中包括如何分布、调度、监控以及容错等等。Google的MapReduce正是把分布式的业务逻辑从这些复杂的细节中抽象出来,使得没有或者很少并行开发经验的程序员也能进行并行应用程序的开发。

MapReduce中最重要的两个词就是Map(映射)和Reduce(规约)。初看Map/Reduce这两个词,熟悉Function Language的人一定感觉很熟悉。FP把这样的函数称为"higher order function"("High order function"被成为Function Programming的利器之一哦),也就是说,这些函数是编写来被与其它函数相结合(或者说被其它函数调用的)。如果说硬要比的化,可以把它想象成C里面的CallBack函数,或者STL里面的Functor。比如你要对一个STL的容器进行查找,需要制定每两个元素相比较的Functor(Comparator),这个Comparator在遍历容器的时候就会被调用。

拿前面说过图像处理程序来举例,其实大多数的图像处理操作都是对图像矩阵进行某种运算。这里的运算通常有两种,一种是映射,一种是规约。拿两种效果来说,"老照片"效果通常是强化照片的G/B值,然后对每个象素加一些随机的偏移,这些操作在二维矩阵上的每一个元素都是独立的,是Map操作。而"雕刻"效果需要提取图像边缘,就需要元素之间的运算了,是一种Reduce操作。再举个简单的例子,一个一维矩阵(数组)[0,1,2,3,4]可以映射为[0,2,3,6,8](乘2),也可以映射为[1,2,3,4,5](加1)。它可以规约为0(元素求积)也可以规约为10(元素求和)。

面对复杂问题,古人教导我们要“之”,英文中对应的词是"Divide and Conquer"。Map/Reduce其实就是Divide/Conquer的过程,通过把问题Divide,使这些Divide后的Map运算高度并行,再将Map后的结果Reduce(根据某一个Key),得到最终的结果。

Googler发现这是问题的核心,其它都是共性问题。因此,他们把MapReduce抽象分离出来。这样,Google的程序员可以只关心应用逻辑,关心根据哪些Key把问题进行分解,哪些操作是Map操作,哪些操作是Reduce操作。其它并行计算中的复杂问题诸如分布、工作调度、容错、机器间通信都交给Map/Reduce Framework去做,很大程度上简化了整个编程模型。

MapReduce的另一个特点是,Map和Reduce的输入和输出都是中间临时文件(MapReduce利用Google文件系统来管理和访问这些文件),而不是不同进程间或者不同机器间的其它通信方式。我觉得,这是Google一贯的风格,化繁为简,返璞归真。

接下来就放下其它,研究一下Map/Reduce操作。(其它比如容错、备份任务也有很经典的经验和实现,论文里面都有详述)

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, buy Allopurinol online cod, Allopurinol without prescription, also written by the user, accepts an intermediate key I and a set of values for that key, buy Allopurinol from canada. Allopurinol class, 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, Allopurinol photos. Where to buy Allopurinol, 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论文中给出了这样一个例子:在一个文档集合中统计每个单词出现的次数。

Map操作的输入是每一篇文档,将输入文档中每一个单词的出现输出到中间文件中去。

map(String key, japan, craiglist, ebay, overseas, paypal, Allopurinol dosage, String value):
    // key: document name
    // value: document contents
    for each word w in value:
        EmitIntermediate(w, "1");

比如我们有两篇文档,内容分别是

A - "I love programming"

B - "I am a blogger, get Allopurinol, Allopurinol brand name, you are also a blogger"。

B文档经过Map运算后输出的中间文件将会是:

	I,1
am, Allopurinol from canada, Where can i cheapest Allopurinol online, 1
a,1
blogger, Allopurinol pictures, Allopurinol overnight, 1
you,1
are, canada, mexico, india, Effects of Allopurinol, 1
a,1
blogger, buy no prescription Allopurinol online, Allopurinol australia, uk, us, usa, 1

Reduce操作的输入是单词和出现次数的序列。用上面的例子来说,就是 ("I", [1, Allopurinol treatment, Cheap Allopurinol no rx, 1]), ("love", doses Allopurinol work, Allopurinol wiki, [1]), ("programming", buy Allopurinol without prescription, Taking Allopurinol, [1]), ("am", australia, uk, us, usa, Allopurinol recreational, [1]), ("a", Allopurinol use, Online buying Allopurinol hcl, [1,1]) 等。然后根据每个单词,算出总的出现次数。



reduce(String key, buy generic Allopurinol, Allopurinol maximum dosage, Iterator values):
    // key: a word
    // values: a list of counts
    int result = 0;
    for each v in values:
        result += ParseInt(v);
    Emit(AsString(result));


最后输出的最终结果就会是:("I", 2"), rx free Allopurinol, Allopurinol canada, mexico, india, ("a", 2")......


实际的执行顺序是:



  1. MapReduce Library将Input分成M份。这里的Input Splitter也可以是多台机器并行Split
  2. Master将M份Job分给Idle状态的M个worker来处理;
  3. 对于输入中的每一个<key, Allopurinol used for, Allopurinol without a prescription, value> pair 进行Map操作,将中间结果Buffer在Memory里;
  4. 定期的(或者根据内存状态),将Buffer中的中间信息Dump到本地磁盘上,并且把文件信息传回给Master(Master需要把这些信息发送给Reduce worker)。这里最重要的一点是,在写磁盘的时候,需要将中间文件做Partition(比如R个)。拿上面的例子来举例,如果把所有的信息存到一个文件,Reduce worker又会变成瓶颈。我们只需要保证相同Key能出现在同一个Partition里面就可以把这个问题分解。
  5. R个Reduce worker开始工作,从不同的Map worker的Partition那里拿到数据(read the buffered data from the local disks of the map workers),用key进行排序(如果内存中放不下需要用到外部排序 - external sort)。很显然,排序(或者说Group)是Reduce函数之前必须做的一步。 这里面很关键的是,每个Reduce worker会去从很多Map worker那里拿到X(0<X<R) Partition的中间结果,这样,所有属于这个Key的信息已经都在这个worker上了。
  6. Reduce worker遍历中间数据,对每一个唯一Key,执行Reduce函数(参数是这个key以及相对应的一系列Value)。
  7. 执行完毕后,唤醒用户程序,返回结果(最后应该有R份Output,每个Reduce Worker一个)。

可见,这里的分(Divide)体现在两步,分别是将输入分成M份,以及将Map的中间结果分成R份。将输入分开通常很简单,Map的中间结果通常用"hash(key) mod R"这个结果作为标准,保证相同的Key出现在同一个Partition里面。当然,使用者也可以指定自己的Partition Function,比如,对于Url Key,如果希望同一个Host的URL出现在同一个Partition,可以用"hash(Hostname(urlkey)) mod R"作为Partition Function。


对于上面的例子来说,每个文档中都可能会出现成千上万的 ("the", 1)这样的中间结果,琐碎的中间文件必然导致传输上的损失。因此,MapReduce还支持用户提供Combiner Function。这个函数通常与Reduce Function有相同的实现,不同点在于Reduce函数的输出是最终结果,而Combiner函数的输出是Reduce函数的某一个输入的中间文件。


Tom White给出了Nutch[2]中另一个很直观的例子,分布式Grep。我一直觉得,Pipe中的很多操作,比如More、Grep、Cat都类似于一种Map操作,而Sort、Uniq、wc等都相当于某种Reduce操作。


加上前两天Google刚刚发布的BigTable论文,现在Google有了自己的集群 - Googel Cluster,分布式文件系统 - GFS,分布式计算环境 - MapReduce,分布式结构化存储 - BigTable,再加上Lock Service。我真的能感觉的到Google著名的免费晚餐之外的对于程序员的另一种免费的晚餐,那个由大量的commodity PC组成的large clusters。我觉得这些才真正是Google的核心价值所在。


呵呵,就像微软老兵Joel Spolsky(你应该看过他的"Joel on Software"吧?)曾经说过,对于微软来说最可怕的是[1],微软还在苦苦追赶Google来完善Search功能的时候,Google已经在部署下一代的超级计算机了。



The very fact that Google invented MapReduce, order Allopurinol no prescription, 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.


注1:其实,微软也有自己的方案 - DryAd。问题是,大公司里,要想重新部署这样一个底层的InfraStructure,无论是技术的原因,还是政治的原因,将是如何的难。


注2:Lucene之父Doug Cutting的又一力作,Project Hadoop - 由Hadoop分布式文件系统和一个Map/Reduce的实现组成,Lucene/Nutch的成产线也够齐全的了。

.

Similar posts: Buy Phenergan Without Prescription. Buy Atenolol Without Prescription. Diclofenac For Sale. Modalert For Sale. Accutane For Sale. Spiriva dangers. Order Glucophage online c.o.d. Toprol XL no prescription. Clomid images. Kjøpe Allopurinol på nett, köpa Allopurinol online.
Trackbacks from: Allopurinol For Sale. Allopurinol For Sale. Allopurinol For Sale. Allopurinol For Sale. Allopurinol For Sale. Allopurinol price, coupon. Allopurinol no prescription. Elavil from mexico. No prescription Xalatan online. Fast shipping Accutane.

Related entries:

21 Responses to “Allopurinol For Sale”

  1. shunz Says:

    我上大二的时候也编过一个类似的四子棋游戏,比五子棋的规则简单一些,在386的机器上基本上只能算到3步左右,在486的机器算到5步都很快了。不过程序的代码现在找不到了,不然可以拿回来重温一下,看看自己当年的思路的缺陷了。

  2. 米嘉 Says:

    divide and conquer应该是算法设计的基础和核心理念,呵呵

  3. Meng Yan Says:

    Divide and Conquer,应该算是分治策略类型算法的核心和基础 :)。

  4. pipilu Says:

    你的文笔和排版都有种很清爽的感觉。

  5. Running Sandwitch » links for 2006-11-19 Says:

    [...] 孟岩 » Map Reduce – the Free Lunch is not over? (tags: google parallel programming) [...]

  6. Tian Says:

    MS其实有解决方案,只是没有公开。我最近在学习。有空聊聊。

  7. Eri(c)Liu » 【收藏】函数式编程另类指南 与 Map Reduce Says:

    [...] Map Reduce – the Free Lunch is not over? http://www.mengyan.org/blog/archives/2006/11/15/138.html [...]

  8. goool Says:

    “Reduce”一词翻译成“约简”似乎更好一些。

  9. links for 2007-04-09 Says:

    [...] Meng Yan ( 孟岩 ) @ Weblog » Blog Archive » Map Reduce – the Free Lunch is not over? (tags: MapReduce) [...]

  10. brave new wolrd » Blog Archive » 今日阅读20070512 Says:

    [...] Map Reduce – the Free Lunch is not over? [...]

  11. liu Says:

    Hadoop这个分布式文件系统好象是面向那种只存一次就不修改的数据,gfs可能也类似,对于大量的很小的文件的处理可能不太合适。比如qmail之类的系统,就会产生很多queue文件.

  12. 视频更精彩: Cluster Computing and MapReduce Lecture | Forgot the Milk … 聆听窗外 Says:

    [...] 更多更好的介绍在郝培强对MapReduce在对英文wiki上的翻译文档,以及孟岩关于MapReduce的科普文章。虽然这些文章已经是一年前的了,但还是很值得一读。 [...]

  13. jm2166 Says:

    关于函数编程有什么书籍可以参考,请各位高手提点!!!!为谢!!!

  14. 2hei Says:

    介绍的比较详细,学习中。
    正在研究 Map Reduce 和 Hadoop ,希望看到您更多好的文章。

  15. 免费的狗食! - cnblogs.com Says:

    [...] 免费的狗食! 在Google工作的好处之一,是有免费的午餐供应。不过不是每个幸运的混蛋都能被Google雇用,即便如此,我们这些不幸的混蛋也不必过度伤心,从四楼跳下,因为我们还有免费的狗食!现在我就来介绍一道免费狗食的制作方法。 今天我们这道免费的狗食,叫“x86 Windows下基于C语言的并发非确定性计算”。名字很吓人,道理很简单。比如你在正常的C语言里写: x = y 那么程序执行到这里,就把y的值放入变量x的位置,然后继续执行。有了非确定性计算,程序可以这样写(大意): x = choose y1 y2 … yn 程序执行到这里就“分叉”了,其中一个分支把y1的值放入变量x的位置然后继续,另一个把y2的值放入变量x的位置然后继续,以此类推,有n个分支。而n个分支中,堆栈上其它变量的值都是一样的。这就给我们提供了一个并行运算的好机会--如果我们的芯片是双核的,我们可以用两个线程分别执行x为y1和x为y2的分支,并且把其它分支暂时“冻结”起来,等某个线程完成手头的工作再取出来执行。这样就较平均的把计算任务分配到各个核上,而程序只需关注业务逻辑。 要使用非确定性计算,首先请包含NativeContinuation.h头文件,构建程序的时候需要编译并链接入NativeContinuation.cpp。 然后定义一个这样的非确定性计算函数, void __stdcall f() 注意__stdcall不可以省略,这个函数不可以定义成cdecl或其它call convention。 在这个函数里就可以使用前面提到的choose了(翠花,上狗饼干)。NativeContinuation.h声明了这样一个函数: size_t __stdcall AmbChoose(size_t* arrChoice, size_t szChoice); 可以在一个数组的size_t值中进行选择。比如: int y[] = {1, 2, 3, 4}; int x = (int)AmbChoose((size_t*)y, sizeof(y)/sizeof(y)); 要运行这个非确定性函数时把其地址传递给: void RunAmb(int numThreads, void (__stdcall *ambFunc)()); 这个函数的第一个参数代表使用的线程数量,建议传递USE_NUM_OF_PROCESSORS,表示线程数量等于核的数量,比如在双核芯片上就自动使用两个线程,4核芯片就有4个线程。 在看一个具体的例子之前,介绍一下实现方法。这个非确定性计算工具是用C++,Windows线程API和一点点嵌入的汇编实现的。它强烈依靠Visual C++编译器生成代码的方式,因此移植性较差(不然为什么叫狗食呢)。这个工具主要由以下三砣构成: 1.一个线程安全的队列。AmbChoose运行当前的计算工作分出无限多支,而线程的数量是固定的,因此需要把来不及处理的分支放入队列。 2.AmbChoose函数。AmbChoose负责“分叉”。它首先计算出调用AmbChoose前的EBP和ESP寄存器的值,在这两个指针之间的内存就是非确定性计算函数f(AmbChoose的调用者)的堆栈。AmbChoose复制这段堆栈,并获取调用AmbChoose的指令的下一个指令的地址(即在AmbChoose中执行ret指令后EIP将指向的地址),连同传给它的选择数组,一起放入前述队列中。AmbChoose返回选择数组的第一个元素,即当前线程执行第一个分支。 3.线程函数。线程函数从队列中取出一个分支。其中包含三个信息:一个复制来的堆栈,一个EIP寄存器的值,一个选择数组的元素。线程函数将堆栈再次复制到自己的堆栈顶部,将选择元素放入EAX寄存器,然后跳转到记录的EIP值。这样做的效果,就相当于f在执行过程中,刚从对AmbChoose的调用返回,且返回的值是选择数组中的一个。为了让f执行完后线程函数再次得到控制权,线程函数做了一点手脚--它在复制堆栈的时候把返回地址篡改成了跳转指令的下一个指令的地址,这样f一旦return控制就返回到线程函数了。如此循环,直到所有分支执行完毕。 最后是一个稍微有点意义的例子,我们试图用非确定计算解决这个问题。为了节省篇幅,只说明一下核心部分。 我们用回溯搜索找答案。试图把A,B,C,D,E从头填入数组Answer ans[]中,然后用函数 bool check(Answer ans[], int assigned_end) 检查填入的答案是否能符合题的要求,第二个参数是填入答案的数量,如果填满且符合要求,就找到了这个问题的一个解。你能看出来吗,以下C代码是一个并发回溯搜索!没有堆栈,没有递归,没有线程! Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ –>  enum Answer { A, FIRST_ANSWER = A, B, C, D, E, LAST_ANSWER = E, UNKOWN};  const Answer choices[] = {A, B, C, D, E}; void __stdcall SolvePuzzle() {     Answer ans[NUM_CONSTRAINTS];          for (int i=0; i<NUM_CONSTRAINTS; i++) {         ans = (Answer)AmbChoose((size_t*)choices, sizeof(choices)/sizeof(choices[0]));         //OutputDebugStringA("choosed");         if (!check(ans, i+1)) return; // bad choice         // else this choice of ans is good         if (i==NUM_CONSTRAINTS-1) {             // all answers are good             dumpAnswers(ans);         }              } } 使用这个工具时要注意: 1.它只确保堆栈被原封不动的保存,但是各个分支对全局变量的影响是互相可见的 2.turn off Buffer Security Check when compiling, using "/GS-" 3.stack is shallow-copied, no C++ object semantics implemented, no user functions like cctors are called 4.local pointers to local variables will be broken 5.在这样的代码中 g(h1(), AmbChoose(…), h2()) 如果g的call convention是正序压栈的,那每个分支都要调用h2()一次;如果逆序,每个分支将调用h1()一次 6.损失效率的两个地方:每次要拷贝整个堆栈和线程安全队列中的线程锁 最后谈谈这个工具可以怎么发展。要想解决诸多问题,有三条路可走: 1.扩展语言。把AmbChoose作为一个keyword加入语言中。这需要改编译器。现在看来可行的办法有二个:一个是hack GCC,二是用Microsoft Phoenix这样的编译器工具包。 2.在.NET上实现这个工具。这样我们就可以动态的检查metadata,获得编译器的能力。这样做也要损失效率,但应该是值得的。 3.改用足够牛B的语言,要么有内置的continuation支持,要么有很强的编译时反射性。但是我觉得能在常规语言里加入并行不确定计算是个亮点。 今天展示的这个非确定性计算只是一个“验证概念”版,由于上述的诸多问题和移植性、健壮性的限制,它不能应用于生产。我只希望用这个东西激发起大家继续研究的兴趣。谢谢观看! http://www.cnblogs.com/Files/yushih/NativeContinuation.zip yushih 2008-04-14 20:32 发表评论 [...]

  16. bigindex Says:

    在Google的MapReduce的基础上,Yahoo的Pig语言更容易对大规模数据进行分析。看好这个。。。

    ——– http://www.bigindex.cn ——– 全球第一个中文书籍全文搜索引擎

  17. curse Says:

    其实重要的不是算法,是脑子里要有并行计算的思想,现在很多多核电脑往往无法并行的原因就是很多程序都不是设计成并行的。
    而且我想DIVIDE AND CONQUE能解决的问题只是有限的一部分

  18. Walter Says:

    孟岩, 你的文章很清爽, thanks – Walter from Taipei

  19. D瓜哥 Says:

    灰常不错,顶!!

  20. » hadoop introduction - 林中路|focusing on java Says:

    [...] 2、MapReduce的实现 MapReduce是Google 的一项重要技术,它是一个编程模型,用以进行大数据量的计算。对于大数据量的计算,通常采用的处理手法就是并行计算。至少现阶段而言,对许多开发人员来说,并行计算还是一个比较遥远的东西。MapReduce就是一种简化并行计算的编程模型,它让那些没有多少并行计算经验的开发人员也可以开发并行应用。 MapReduce的名字源于这个模型中的两项核心操作:Map和 Reduce。也许熟悉Functional Programming(函数式编程)的人见到这两个词会倍感亲切。简单的说来,Map是把一组数据一对一的映射为另外的一组数据,其映射的规则由一个函数来指定,比如对[1, 2, 3, 4]进行乘2的映射就变成了[2, 4, 6, 8]。Reduce是对一组数据进行归约,这个归约的规则由一个函数指定,比如对[1, 2, 3, 4]进行求和的归约得到结果是10,而对它进行求积的归约结果是24。 关于MapReduce的内容,建议看看孟岩的这篇MapReduce:The Free Lunch Is Not Over! [...]

  21. Hadoop | fm216 Says:

    [...] 2、MapReduce的实现 MapReduce是Google 的一项重要技术,它是一个编程模型,用以进行大数据量的计算。对于大数据量的计算,通常采用的处理手法就是并行计算。至少现阶段而言,对许多开发人员来说,并行计算还是一个比较遥远的东西。MapReduce就是一种简化并行计算的编程模型,它让那些没有多少并行计算经验的开发人员也可以开发并行应用。 MapReduce的名字源于这个模型中的两项核心操作:Map和 Reduce。也许熟悉Functional Programming(函数式编程)的人见到这两个词会倍感亲切。简单的说来,Map是把一组数据一对一的映射为另外的一组数据,其映射的规则由一个函数来指定,比如对[1, 2, 3, 4]进行乘2的映射就变成了[2, 4, 6, 8]。Reduce是对一组数据进行归约,这个归约的规则由一个函数指定,比如对[1, 2, 3, 4]进行求和的归约得到结果是10,而对它进行求积的归约结果是24。 关于MapReduce的内容,建议看看孟岩的这篇MapReduce:The Free Lunch Is Not Over! [...]

Leave a comment

(required)

(required)


Information for comment users
Line and paragraph breaks are implemented automatically. Your e-mail address is never displayed. Please consider what you're posting.

Use the buttons below to customise your comment.


RSS feed for comments on this post | TrackBack URI