【我所认知的BIOS】-->Spin Loop

来源:互联网 发布:linux添加路由脚本命令 编辑:程序博客网 时间:2024/05/17 05:06

 

【我所认知的BIOS--> Spin Loop

LightSeed

2009-11-1

前段时间在研究multi-processor的时候在APBSPSMI handler里面有不少关于Spin Loop的语句,后来详细研究了一下,今天写了一个小结和大家分享一下,希望和大家一起探讨,如果您有更好的建议的话不妨也说出来。其实这篇文章是对SMM的补充。不过由于SMM那篇文章实在是感觉太长了,所以就还是另起一章重新来说了。

1、理论描述

1.1多线程(multi-threading)技术带来的问题

多线程并行程序必须使用同步技术才能确保正确的操作。通常,同步操作使用共享的同步变量(shared synchronization variables)和检查这些变量(shared synchronization variables)的值的循环——“spin-wait loops”。从英特尔 P4Xeon™处理器开始,英特尔® IA - 32架构提供了一种新的指令(PAUSE),来处理与spin loops相关问题的性能。

后续的说明两个重要的优化问题:

①多线程计算

②涉及高速处理器的spin loop和共享数据管理。(未写)

具体来说,这些优化包括在了spin-wait loops中新的PAUSE指令的运用,还有就是在不同的128-bytecache lines中共享数据和非共享数据的安排。(这部分还没有写。)因为它兼容早期的所有IA - 32架构,英特尔强烈建议在spin loops中尽量使用新的PAUSE指令。后面将详细介绍一下为什么要用这个新的指令。

1.2 问题的描述和解决办法

当使用多线程系统的时候,为了取得良好的性能,以上两个问题必须解决。首先是对spin-wait loops的方式进行编程,第二个是(实际位置在内存中)同步变量的对齐(原文是alignment,翻译成对齐,我想比较合适。不过大多情况下,还是看E文原版比较好。)。事实上,这种同步变量对齐问题可以推广到所有共享数据的对齐,以及共享数据与非共享数据混合数据的对齐。解决这些问题可以帮助您避免应用程序中严重的性能降低和系统资源的无谓消耗。以下几个部分将和大家探讨一下可以提高性能和降低功耗的方法。

(这里要特别感谢一下kaeshine大哥,他对文章alignment的翻译指出了中肯的建议。以前我翻译成“调整”,确实应该翻译成“对齐”,谢谢这个Phoenix的高手哦!:)

2 Spin-wait Loops的概念

Spin-wait Loops:检查同步变量的值,通常看起来像这样:

wait_loop:    cmp   eax, sync_var

jne     wait_loop

同步变量在内存中的位置sync_var,它的value每循环一次都会被读取。这个内存位置(中的数据)将被检查一遍又一遍,直到被其他线程(或者其他处理器)写入了所需的数据到这个存储单元中。其他线程共享这个内存地址空间,但又独立的执行。(笔者:注意了哦,这就是spin loop的一个实例。)

3 Spin-wait Loops的后果

在目前的微处理器上,像这样的循环(多个线程同时有读取数据的请求)就会导致请求乱序的恶果。当然我们可以这样做,探测被其他处理器(相对而言的)写入到任何地方数据,并且处理器必须保证不会有内存顺序乱套的发生。如果我们这样做了的话,数据是保证安全了,但是处理器却要遭受非常严重的“惩罚”。换言之,像上面的例子,当处理器在退出spin loop的时候就会受到严厉“惩罚”。这种“惩罚”就是处理器将会损失很大一部分的性能。(至于为什么会损失这个性能,请看后文。)举个例子,the Pentium Pro processor, the Pentium II processor and the Pentium III processor这些处理器牺牲的性能保守估计与Pentium 4牺牲的相比大约是后者的25倍甚至更多。(因为CPU有预处理的功能,故占用了很多的资源。)

4 提高整个系统的性能

4.1比较正规点去理解

上面的情况是大家都不愿意看到的,(当然intel也不会让这种现象苟存于世,他们想到了办法来解决。)那就是插入PAUSE这个指令到循环体中来,从而避免上述情况的发生。

wait_loop:    pause

cmp    eax, sync_var

jne     wait_loop

PAUSE指令在循环的过程中略有延误,这样会有效地导致:差不多是在内存系统总线(memory system bus)的最高速度时发出内存的请求,在这个时候,sync_var处的值可以被另一个处理器改变。想想,如果想要比这还快速地发出请求的话肯定是毫无意义的。这种方法的使用可以显著地提高spin-wait的性能。插入暂停指令可以收获显著的效益,它可以大幅降低spin-wait过程中能源的消耗,因为只有较少的系统资源被使用。

看看上面的这段话,似乎违反直觉,这句话值得再说一遍:PAUSE指令虽然是减慢Spin-wait loop,但是却使得整体性能大大增加。Spin loop并不需要很迅速地被执行,因为它去check事件这个过程本身就比较慢。如果检查同步变量太快,那么loop中使用的系统资源就是不必要的浪费,并且这还会导致处理器循环退出后,受到相当严厉的“惩罚”(真的是赔了夫人又折兵呀)。

为了避免PAUSE执行指令时,同步已经完成,用下面的这种循环可以提高性能哦:

cmp   eax, sync_var

je      next_inst

wait_loop:    pause

cmp   eax, sync_var

jne     wait_loop

next_inst:     ...

为什么说是提高了性能呢?应该指出,这些代码片段都与分支预测产生了微妙的相互作用。目前的微处理器上执行指令的速度要比判断情况来的快(笔者:就像MOV的速度要快于CMP的速度一样)。因此,大多数现代微处理器会先预测的分支指令的结果(如'JE''JNE')然后再进行结果比对。处理器然后探索性地执行指令,直到它确定预测是正确的。如果处理器预测错误,那么所有的之前探索性执行的指令都被取消。由于当线程使用在使用共享数据的时候,spin loop往往会保护它。也许有人认为,当一个线程正在使用它的时候,这一共同的数据会有被探索性的访问到的可能性。但是,应该强调的是,如果处理器是探索性的执行程序的话,共享数据永远不会被修改。

4.2直白点来说

关于损失性能我的理解,笔者:如果要说的直白点的话,我想这还和CPU得预测机制有很大的联系。因为现在的CPU再执行判断操作的时候,它会有预测的机制。打个比方,像下面的一段程序:

XOR   AX,AX

MOV  CX,1000

@@

ADD   AX,1

LOOP @B

那么当CPU在执行的时候,在遇到第一个loop的时候,CPU就会预测CX是不等于0的。那么在预取指令的时候就会把后面的loop都这样预测了。那么在执行1000 loop的过程中CPU的预测都是正确的,从而提高了系统的性能。然而恰好是这个原因,在spin-loop中却扮演了降低性能的角色。因为spin-loop必须每次都要去检测同步变量,而CPU同样会去预测这个结果(假如没有加入PAUSE这个指令的话),当在某个时刻这个同步变量已经满足了退出loop的条件(由于这个同步变量本来不是由自己本来能预测出来的,所以就在指令预取的时候早先预取了N多条指令),从而要退出loop,也因此要flush指令预取队列。那么中间就浪费了很多的系统资源。(因为本来执行loop的这个CPU可并不知道是要循环1000次还是1W次哦。)

再说PAUSE这个指令的作用,我猜测,其实CPU的内部在遇到这个指令的时候,就应该不会大量预取指令了,而是先预取后一条或者若干条指令(方便到了内存的总线速度最高的时候去执行。),这样当同步变量满足了条件后推出spin-loop的时候就不会被遭到很严重的“惩罚”——flush大量的预取指令。(因为预取的这些指令占用了大量的系统资源。)

4OS中提高性能的方法

这里还有一些其他技术来避免spin-wait loop里出现的问题。首先,我们可以使用标准的操作系统(OS)的计时服务,这样可以使得一个锁定的服务只能被定期地check。这避免了同步变量经常被check。另一种方法可以(而且经常是)在操作系统本身上使用。该操作系统可以强制执行HALT指令来等待线程,使该线程不消耗任何资源。当操作系统觉得同步条件应该会OK的情况下再下命令, '唤醒'线程恢复处理。这些解决方案都不需要使用PAUSE指令。如果你希望一个线程等待相当长一段时间,通过一个操作系统使用HLAT指令,我想这是首选的解决方案。(不过BIOS中是没有OS的哈,所以这里BIOSER就不用管了)

5、 PAUSE使用指令的技巧

Pause在奔腾4处理器上第一次露面的。从技术上讲,它会对硬件暗示说:“现在正在执行的代码是一个spin-wait loop。”对于这个指令不同的处理器有不同的解释。在奔腾4处理器,指令行为如上所述,这样系统的整体性能会有大大地改善。据intel的相关文档里的介绍证实,奔4处理器之前的所有处理器都会把PAUSE指令上翻译为NOPno operation),并且对于程序还没有任何的影响。甚至在非英特尔x86系列处理器上也会表现为NOP。为此,intel强烈建议在所有spin loop指令代码前插入PAUSE指令。使用PAUSE指令不影响对现有平台程序的正确性,并还提高了奔腾4处理器的平台的整体性能。

 

以上就是我对spin-loop的理解,谢谢。不过这里还有一个东西就是关于CPUcache的,我还了解的不熟,正在研究中。同时也想问问网友们,是否有关于奔腾或者现在比较流行的CPU的架构的书籍哦(如果有的话能否传我一本?我想好好了解一下关于CPUcache的问题,前段时间在解决一个bug的时候问题就出在了cache。)。

 

原创粉丝点击