主流cpu中cpu cache ratio 设置的大小

提问回答都赚钱
> 问题详情
高速缓冲存储器Cache是位于CPU和主存DRAM之间规模或容量较小但速度很快的存储器。下面是关于Cache
悬赏:0&&答案豆&&&&提问人:匿名网友&&&&提问收益:0.00答案豆&&&&&&
高速缓冲存储器Cache是位于CPU和主存DRAM之间规模或容量较小但速度很快的存储器。下面是关于Cache的叙述,其中正确的是( )。A.L1 Cache的工作频率越来越高,但还是低于访问它的CPU的工作频率B.CPU访问Cache,“命中”和“未命中”时,均需插入等待状态,只是“命中”时插入的等待状态数少于“未命中”时插入的等待状态数C.PC中采用的Cache方案兼顾了SRAM的高速特性和DRAM的低成本特性,即达到了既降低成本又提高系统性能的目的D.L1 Cache的工作频率和CPU的工作频率相等,L2 Cache的工作频率越来越高,但不可能等于CPU的工作频率请帮忙给出正确答案和分析,谢谢!
发布时间:&&截止时间:
网友回答&(共0条)
回答悬赏问题预计能赚取&4.00元收益
回答悬赏问题预计能赚取&3.00元收益
回答悬赏问题预计能赚取&1.00元收益
回答悬赏问题预计能赚取&1.00元收益
回答悬赏问题预计能赚取&3.00元收益
回答悬赏问题预计能赚取&2.00元收益
回答悬赏问题预计能赚取&3.00元收益
回答悬赏问题预计能赚取&2.00元收益
回答悬赏问题预计能赚取&3.00元收益
回答悬赏问题预计能赚取&91.00元收益
回答悬赏问题预计能赚取&3.00元收益
回答悬赏问题预计能赚取&91.00元收益
回答悬赏问题预计能赚取&4.00元收益
回答悬赏问题预计能赚取&1.00元收益
回答悬赏问题预计能赚取&1.00元收益
回答悬赏问题预计能赚取&3.00元收益
回答悬赏问题预计能赚取&1.00元收益
回答悬赏问题预计能赚取&1.00元收益
回答悬赏问题预计能赚取&4.00元收益
回答悬赏问题预计能赚取&3.00元收益
回答悬赏问题预计能赚取&3.00元收益
回答悬赏问题预计能赚取&1.00元收益
回答悬赏问题预计能赚取&3.00元收益
回答悬赏问题预计能赚取&91.00元收益
回答悬赏问题预计能赚取&3.00元收益
回答悬赏问题预计能赚取&3.00元收益
回答悬赏问题预计能赚取&51.00元收益
回答悬赏问题预计能赚取&51.00元收益
回答悬赏问题预计能赚取&3.00元收益
回答悬赏问题预计能赚取&3.00元收益
回答悬赏问题预计能赚取&3.00元收益
回答悬赏问题预计能赚取&1.00元收益
回答悬赏问题预计能赚取&1.00元收益
回答悬赏问题预计能赚取&1.00元收益
回答悬赏问题预计能赚取&3.00元收益
回答悬赏问题预计能赚取&22.00元收益
回答悬赏问题预计能赚取&22.00元收益
回答悬赏问题预计能赚取&2.00元收益
回答悬赏问题预计能赚取&1.00元收益
回答悬赏问题预计能赚取&1.00元收益
回答悬赏问题预计能赚取&3.00元收益
回答悬赏问题预计能赚取&1.00元收益
回答悬赏问题预计能赚取&1.00元收益
回答悬赏问题预计能赚取&3.00元收益
回答悬赏问题预计能赚取&1.00元收益
你可能喜欢的
[] [] [] [] [] [] [] [] [] [] [] []
请先输入下方的验证码查看最佳答案
图形验证:函数调用带来的 cache miss 会对 cpu 性能带来多大的影响?
一个应用程序超过几M大小而且还会调用一些动态库,这样会造成函数指针在很广阔的空间飞来飞去。这样 cache 会经常 miss?cache miss 是否是影响 cpu 性能的一大因素?那么这种情况对程序的执行效率产生多大的影响?不同的语言造成这个方面的影响区别有多大呢?是否需要在编程时注意,或有什么成熟的解决方案,或是编译器或 cache 结构已经很好的解决了这个问题?
按投票排序
泻药,这是非常好的一个问题,同时也是比较前沿的。题目描述中的这个现象确实存在,已经有不少实测证明了,现在的L1 instruction miss率是比较差劲的,比较坏的会超过10%,instrcuction miss比较特殊,是乱序执行没办法掩盖的(乱序执行要调度不相干指令上来掩盖数据访问延迟,但是如果指令都取不上来也只能干瞪眼了。分条回答:1. 是的,无可争议。2. 假设一个完全不miss的L1 Intruction Cache,性能在有的benchmark上可以提高10%~50%3. 不清楚,未见相关实测数据4. 解决方案有两种,一种是编译优化时调整代码布局,这一个方向我没有跟进过不敢多说,另一个方向是由微结构负责从已经产生的miss中推断未来miss的位置,提前预取,我所知道的最好的研究结果是微结构研究重镇Umich和ARM联手发表在MICRO'13上的RDIP方案,实现了理想L1 Instruction Cache 70%左右的性能(注:在几个不太常见的benchmark上面得到的结果),代价是需要多开64KB的SRAM,这个额外存储空间比整个L1 Instruction Cache还大,所以没办法实用化,这个问题从研究走向工业实际估计还需要一段时间。
问题不错。已有答案已经回答得不错了,我对你的第3第4个小问题补充一下。同组的师兄正好有这方面的研究(昨晚刚跟submit to Micro2015)一般来说,函数指针这个问题,是程序行为(program behavior) 和架构 (architecture) 不匹配(mismatch)造成的。对于程序员来说,写程序越方便越简单越直白越好,但是这种程序的代价就是编译结果不太好,执行效率低。这种例子很多,比如动态语言里的垃圾回收(garbage collection)要占很多时间。现在函数指针也是一个问题,用函数指针当然很方便,但是编译器完全看不出函数指针指向的规律,分支预测器对这种分支地址预测也是两眼一抹黑,现在成熟的产品里暂时没什么办法,所以要尽量避免用函数指针。这个现象和语言还是很相关的。比如说用node.js之类的事件编程模型,每个事件都是独立的,处理每个事件的代码又完全不一样,程序运行起来完全不知道将来要处理什么事件,指令缓存(I-cache)两眼一抹黑。我看过师兄的paper draft,这东西确实很严重。至于解决方案,楼上提到了RDIP,那是一种prefetching technique (抱歉我实在不知道合适的翻译)。Prefetching的效率、代价还很有争议,比如ls提到的64KB额外空间,还有与之相伴的功率,等等。多了64KB,我不认为很多cache miss rate提升就一定是prefetching算法改进的原因。还有一个办法,就是design hardware for event processing,具体的idea不方便多透露了。总之这是一个很有前景的研究方向. The underlying principle is to combine program behavior with architecture features. 欢迎大家跟进讨论。
大家都往CPU的微架构说了,我都没啥好插嘴的了(虽然我也不是特别懂),为啥还要邀请我。首先,你的问题描述的1)是对的,但是.....这个问题其实已经被部分解决了。RTLD_LAZY
Perform lazy binding.
Only resolve symbols as the code that
references them is executed.
If the symbol is never
referenced, then it is never resolved.
(Lazy binding is
performed only for references to
variables are always immediately bound when the library is
如果在巨大的库被整体加载然后无数的函数跳来跳去,上下文切换,大量的出入栈,并且在超过L1/L2/L3(具体看CPU)的cache大小访问指令和数据。那性能的下降是必然而且是无解的。但是从程序运行的角度来说,它始终都是保持着并且程序员也应该能力让它保持着一定的空间和空间局部性。你不可能说你跑和HTTPD你的CPU密集型任务每次都要横跨128G的服务器内存。那真是不能再糟糕了。也就是说,不管是哪些任务,我们总不可能需要访问所有的函数库,上面的哪个RTLD_LAZY就是为了Linux上防止共享库被加载的时候不需要的数据被加载进cache而引入的一个参数。我相信window也有类似的玩意,估计还能做得更加智能一点。
1:是2:视具体情况各有不同3:很大。例如说各种解释性脚本语言(Python/PHP等),即使代码没有函数调用,但是实际上解释器也会发生多次函数调用。4:没有太好的办法。而且在实际项目中,有太多的需要优化的流程和算法,其影响面都会比这个因素大。所以,在没有确认这个因素为瓶颈前,过于纠结此问题也是一种无用功。
针对第四点,编程中要注意的地方,说一下自己的部分经验。1.cache line 对齐编程时数据结构尽量cache line 对齐,可以自己添加pad,也可以用gcc提供的__attribute__。尽量都适用局部变量,在64位机器上,局部变量会优先放到寄存器中。在些多核程序时,尽量绑定线程到一个cpu上,不同cpu之间L1, L2 cache是独立的。2.避免false sharing。定义数据结构的时候不能这么搞: int num[CPU_NUMS],这样在for循环中对num[i]++的时候就会造成false sharing。这也是为什么结构体定义要cache line对齐。3. cpu affinity。线程创建后立即绑定到具体的core上,然后再 进行分配内存,保证内存分配在自己的领土这边,如果内存分配超过总内存的一半,那么效率会大幅度降低,因为有一部分内存已经到了另一个cpu上了。4. 可以使用prefetch指令。prefetch,gcc也提供了数据的预取指令,不过这个要对程序的执行流程和时间有非常好的把握,要不然预取到cache里用不到就没意思了。之前做多核高性能优化的时候总结过一篇文章:
编程提高cache 命中率 一个简单手段 尽量使用局部变量
避免全局变量滥用 最好函数操作数据都在栈上 像编码解码器CPU密集局部运算
就有一定优化价值 逻辑上
对CPU内存比较密集的
流程分段简单实现
不要一个函数太多分支及功能对于代码段 要求连接器脚本定义
一般应用层应用可操作性不强
首先应该明白什么是cache miss,为什么会发生cache miss,接着就是cache miss有什么特点,然后就是程序应该怎样针对cache miss做优化。一般对CPU有初步了解都知道CPU有cache,数据需要在CPU中计算的话需要先从内存中读取到cache才能被CPU访问,然后cache又分L1和L2/3级cache,L1小,一般32KB,L3大,能达到32MB。那么CPU又是如何从cache读取数据,以及数据如何从内存读取到cache?CPU从cache中读取比较简单,64位CPU 就每次从cache中最多访问64bits数据块,内存中载入数据到内存就比较麻烦了,载入的单位是一个cache line,cache line大小一般是64bytes,也就是64*8 bits,那么很有意思的就是当CPU 需要访问的地址是一个64bits的数据块,而读入到缓存的却只能按照512bits来缓存,那么从内存中读取的时候其他512-64bits 是哪些数据呢?答案是这个数据块周围的数据。比如一个程序是对一个char ss[5120];进行访问,每次访问的是每隔512bits ss[0],ss[512],ss[1024]......,ss[5120] 访问,实际上是这个数组中所有的数据都曾经被载入到cache中了,只是有些载入到cache却没有被访问。cache 是按照LRU 替换数据的,如果一个cache line 被替换,所有的512bits 都被替换那么实际上程序员针对上面所能做的最重要一点就是:增强数据访问的局部性。增强数据访问的局部性也就是减少cache miss 的最直接方法,局部性的数据很可能被载入到cache了,频繁访问局部性数据也会使得对应的cache 数据不被频繁替换出cache。编译器所作的优化很多也只是在不改变程序逻辑的情况下增加数据访问局部性。但编译器这方面能做的很有限,比如这里为什么翻转一个512×512 的矩阵比翻转一个513× 矩阵要慢除了cache miss ,内存的结构也会影响从内存载入到cache 的响应时间,从而影响程序速度(内存是按照4kb一个bank 组织的):当然一个CPU 上是运行不同的程序,如果两个程序一个I/O密集型,一个计算密集型,有可能计算密集型需要载入的数据经常被I/O密集型需要访问的数据替换,从而导致计算密集型的程序有更多的cache miss,导致速度运行慢。这个问题再USENIX Security07 的论文论述过:Memory Performance Attacks: Denial of Memory Service in Multi-Core Systems
gcc对cache有支持,简单的如l1 cache对齐。还有冷热数据,不常用的数据用__cold,经常用的用__hot,这样可以把热数据放一个段,冷数据放一个段。代码也是如此,比如初始化代码可以放一个段,这些代码只运行一次,运行完甚至可以释放掉。inline函数也是减少函数调用的一个方法。
我能请教各位大师一个问题吗?你们是如何测试或是计算L1 cache miss rates? 我使用valgrind --tool=cachegrind ./429.mcf (注:429.mcf 是SPEC CPU2006的一个测试程序)
我测试的SPEC CPU2006里的 429.mcf, 显示的是:done==4772== ==4772== I
40,059,029,376==4772== I1
1,239==4772== L2i misses:
1,238==4772== I1
miss rate:
0.00%==4772== L2i miss rate:
0.00%==4772== ==4772== D
24,404,212,402
(20,728,443,236 rd
+ 3,675,769,166 wr)==4772== D1
268,717,593
243,333,233 rd
25,384,360 wr)==4772== L2d misses:
134,798,076
116,976,297 rd
17,821,779 wr)==4772== D1
miss rate:
)==4772== L2d miss rate:
)==4772== ==4772== L2 refs:
268,718,832
243,334,472 rd
25,384,360 wr)==4772== L2 misses:
134,799,314
116,977,535 rd
17,821,779 wr)==4772== L2 miss rate:
)但是,我用另一个工具测试 opcontrol --event=L1_MISSES:500 测试显示 : 61.05%
[.] primal_bea_mpp18.40%
[.] bea_is_dual_infeasible。。。这说明还是有缺失的。但我个人认为应该不是valgrind所显示的缺失率为1.1%(个人觉得1.1%的缺失可以忽略不计,没有做预取的必要).之前有位大神写:
(文章指出429.mcf的cache miss rates 是100%),我不清楚到能不能得到100%的缺失。因为根据上面所提的valgrind显示L1的缺失 1.1%(注明,我的硬件预取功能已经关闭,相领取的功能也已经关闭)。想请大师们指导一下,不胜感激。
已有帐号?
社交帐号登录
无法登录?
社交帐号登录2576人阅读
Cache一般来说,需要关心以下几个方面
1)Cache hierarchy
Cache的层次,一般有L1, L2, L3 (L是level的意思)的cache。通常来说L1,L2是集成& 在CPU里面的(可以称之为On-chip cache),而L3是放在CPU外面(可以称之为Off-chip cache)。当然这个不是绝对的,不同CPU的做法可能会不太一样。这里面应该还需要加上register,虽然register不是cache,但是把数据放到register里面是能够提高性能的。
2)Cache size
Cache的容量决定了有多少代码和数据可以放到Cache里面,有了Cache才有了竞争,才有了替换,才有了优化的空间。如果一个程序的热点(hotspot)已经完全填充了整Cache,那么再从Cache角度考虑优化就是白费力气了,巧妇难为无米之炊。我们优化程序的目标是把程序尽可能放到Cache里面,但是把程序写到能够占满整个Cache还是有一定难度的,这么大的一个Code path,相应的代码得有多少,代码逻辑肯定是相当的复杂(基本上是不可能,至少我没有见过)。
3)Cache line size
CPU从内存load数据是一次一个cache line;往内存里面写也是一次一个cache line,所以一个cache line里面的数据最好是读写分开,否则就会相互影响。
4)Cache associative
Cache的关联。有全关联(full associative),内存可以映射到任意一个Cache line;也有N-way关联,这个就是一个哈希表的结构,N就是冲突链的长度,超过了N,就需要替换。
5)Cache type
有I-cache(指令cache),D-cache(数据cache),TLB(MMU的cache),每一种又有L1,L2等等,有区分指令和数据的cache,也有不区分指令和数据的cache。
二. 代码层次的优化
1) 字节 alignment (字节对齐)
要理解字节对齐,首先得理解系统内存的组织结构. 把1个内存单元称为1个字节,字节再组成字,在8086时代,16位的机器中1字=2个字节=16bit,而80386以后的32位系统中,1字=4个字节。大多数计算机指令都是对字进行操作,如将两字相加等。也就是说,32位CPU的寄存器为32位,导致指令的操作对象是32位字;16位CPU的寄存器为16位,移动、加、减等指令的操作对象也是16位字。由于指令的原因,内存的寻址也同样是按字进行操作,在16位系统中,如果你访问的只是低8位,内存寻址还是按16位进行,然后再根据A0地址线选择低8位还是高8位,这一过程成为一次内存读(写),在16位系统中,如果读取一个32位数,要花费两个内存读周期(先读低16,再读高16)。同理32位CPU的内存寻址按4个单元进行。
为了达到高效的目的,在16位系统中,变量存储的起始地址是2的倍数,32位系统中,变量存储的起始地址是4的倍数,而这些工作都是由编译器来完成的。下面举个例子来说明这个问题。如下图所示:&
&&& 上图是16位系统的内存布局图,深蓝色表示变量覆盖的内存范围,假设变量的大小为2个字节,变量的起始物理内存地址为0000H时,访问这个变量时,只需要一次内存的读写。然而,当变量的内存起始地址为0001H时,cpu将耗费两次读周期进行变量访问,具体过程如下:为了访问变量的低8位,cpu将通过寻址访问起始地址为0000H所在的字,然后找到当前字的高8位;随后cpu再访问0002H所处字的低8位,此低8位就是变量的高8位,这样经过cpu的拼装变量的访问就结束了,可见,需要经过两次读周期才能正确访问变量的值,效率是前者的1/2。
__attribute__((aligned(n)))表示所定义的变量为n字节对齐;&
字节对齐的细节和编译器实现相关,但一般而言,满足三个准则:
1) (结构体)变量的首地址能够被其(最宽)基本类型成员的大小所整除;
2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。
__attribute__ ((packed)) 的作用就是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐,是GCC特有的语法。这个功能是跟操作系统没关系,跟编译器有关,gcc编译器不是紧凑模式的.例如:
__attribute__ ((packed)) 的作用就是告诉编译器取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐,是GCC特有的语法。
__attribute__((aligned(n)))表示所定义的变量为n字节对齐;&
struct B{&};&(默认4字节对齐)
这时候同样是总共7个字节的变量,但是sizeof(struct B)的值却是12。
下面我们使用预编译指令__attribute__((aligned(n)))来告诉编译器,使用我们指定的对齐值来取代缺省的:
struct C{};
这时候同样是总共7个字节的变量,但是sizeof(struct B)的值却是8
struct D{}; __attribute__ ((packed))
&&&&&& sizeof(struct C)值是8,sizeof(struct D)值为7。
字节对齐的细节和编译器实现相关,但一般而言,满足三个准则:
1) (结构体)变量的首地址能够被其(最宽)基本类型成员的大小所整除;
2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。
2) Cache line alignment (cache对齐)
数据跨越两个cache line,就意味着两次load或者两次store。如果数据结构是cache line对齐的,& 就有可能减少一次读写。数据结构的首地址cache line对齐,意味着可能有内存浪费(特别是& 数组这样连续分配的数据结构),所以需要在空间和时间两方面权衡。
对于普通代码,内存边界对齐也是有好处的,可以降低高速缓存(Cache)和内存交换数据的次数。 主要问题是在于Cache本身是分成很多Cache-Line,每条Cache-Line具有一定的长度,比如一般来说L1 Cache每条Cache Line长度在32个字节或64个字节;而L2的会更大,比如64个字节或128个字节。用户每次访问地址空间中一个变量,如果不在Cache当中,那么就需要从内存中先将数据调入Cache中.
&&&&&& 比如现在有个变量占用4个字节,它的起始地址是0x1234567F;那么它占用的内存范围就在0xx之间。如果现在Cache Line长度为32个字节,那么每次内存同Cache进行数据交换时,都必须取起始地址时32(0x20)倍数的内存位置开始的一段长度为32的内存同Cache Line进行交换. &&比如0x1234567F落在范围0xx1234567F上,但是0xx落在范围 0xx1234569F上,也就是说,为了将4个字节的整数变量0xx装入Cache,我们必
须调入两条Cache Line的数据。但是如果int x的起始地址按4的倍数对齐,比如是 0xx1234567F,那么必然会落在一条Cache Line上,所以每次访问变量x就最多只需要装入一条Cache Line的数据了。比如现在一般的malloc()函数,返回的内存地址会已经是8字节对齐的,这个就是为了能够让大部分程序有更好的性能。
1. &__attribute__((aligned(cache_line)))对齐实现;
&&& struct syn_str { ints_ };__attribute__((aligned(cache_line)));
2. 算法实现
&& &int size = 8;&&& &&& &----& 1000(bin)
&& &计算a以size为倍数的下界数:
&&就让这个数(要计算的这个数)表示成二进制时,最后三位为0就可以达到这个目标。只要下面这个数与a进行&与运算&就可以了:
&&而上面这个数实际下就是 ~(size - 1),可以将该数称为size的对齐掩码size_mask.
&& 计算a以size为倍数的上下界数:
&&#define alignment_down(a, size) (a & () )
&&#define alignment_up(a, size)&& ((a+size-1) & (~ (size-1)))
&&注: 上界数的计算方法,如果要求出比a大的是不是需要加上8就可以了?可是如果a本身就是8的倍数,这样加8不就错了吗,所以在a基础上加上(size - 1), 然后与size的对齐掩码进行与运算.
&&a=0, size=8, 则alignment_down(a,size)=0, alignment_up(a,size)=0.
&&a=6, size=8, 则alignment_down(a,size)=0, alignment_up(a,size)=8.
&&a=8, size=8, 则alignment_down(a,size)=8, alignment_up(a,size)=8.
&&a=14, size=8,则alignment_down(a,size)=8, alignment_up(a,size)=16.
&&注:size应当为2的n次方, 即2, 4, 8, 16, 32, 64, 128, 256, , 4096 ...
&& 实现例子:
&&& struct syn_str { int s_ };
&&& void *p = malloc ( sizeof (struct syn_str) + cache_line );
&&& syn_str *align_p=(syn_str*)((((int)p)+(cache_line-1))&~(cache_line-1);
3) Branch prediction (分支预测)
代码在内存里面是顺序排列的。对于分支程序来说,如果分支语句之后的代码有更大的执行几率,& 那么就可以减少跳转,一般CPU都有指令预取功能,这样可以提高指令预取命中的几率。分支预测& 用的就是likely/unlikely这样的宏,一般需要编译器的支持,这样做是静态的分支预测。现在也有& 很多CPU支持在CPU内部保存执行过的分支指令的结果(分支指令的cache),所以静态的分支预测& 就没有太多的意义。如果分支是有意义的,那么说明任何分支都会执行到,所以在特定情况下,静态 分支预测的结果并没有多好,而且likely/unlikely对代码有很大的侵害(影响可读性),所以一般不&
推荐使用这个方法.
if(likely(value)) 等价于 if(value)
if(unlikely(value)) 也等价于 if(value)
也就是说 likely() 和 unlikely() 从阅读和理解代码的角度来看,是一样的!!!
这两个宏在内核中定义如下:
#define likely(x)&&&&&& __builtin_expect((x),1)
#define unlikely(x)&&&& __builtin_expect((x),0)
__builtin_expect() 是 GCC (version &= 2.96)提供给程序员使用的,目的是将“分支转移”的信息提供给编译器,这样编译器可以对代码进行优化,以减少指令跳转带来的性能下降。
__builtin_expect((x),1) 表示 x 的值为真的可能性更大;
__builtin_expect((x),0) 表示 x 的值为假的可能性更大。
也就是说,使用 likely() ,执行 if 后面的语句 的机会更大,使用unlikely(),执行else 后面的语句的机会更大。
例如下面这段代码,作者就认为 prev 不等于 next 的可能性更大,
if (likely(prev != next)) {
&&&&&& next-&timestamp =
&&&&&&& ...
&&&&&&& ...;
通过这种方式,编译器在编译过程中,会将可能性更大的代码紧跟着起面的代码,从而减少指令跳转带来的性能上的下降。
下面以两个例子来加深这种理解:
第一个例子: example1.c
int testfun(int x)
&&&if(__builtin_expect(x, 0)) {
&&&&&&&&&^^^--- We instruct the compiler, &else& block is more probable
&&&&&x = 5;
&&&&&x = x *
&&&} else {
&&&&&&&x = 6;
在这个例子中,我们认为 x 为0的可能性更大
编译以后,通过 objdump 来观察汇编指令,在我的 2.4 内核机器上,结果如下:
# gcc -O2 -c example1.c
# objdump -d example1.o
Disassembly of section .text:
&testfun&:
&& 0:&& 55&&&&&&&&&&&&&push&& %ebp
&& 1:&& 89 e5&&&&&&&&&&& mov&&& %esp,%ebp
&& 3:&& 8b 45 08&&&&&&&&&&mov&&& 0x8(%ebp),%eax
&& 6:&& 85 c0&&&&&&&&&&& test&& %eax,%eax
&& 8:&& 75 07&&&&&&&&&&& jne&&& 11 &testfun+0x11&
&& a:&& b8 06 00 00 00&&&&&&& mov&&& $0x6,%eax
&& f:&& c9&&&&&&&&&&&&& leave
&&& 10:&& c3&&&&&&&&&&&&& ret
&&& 11:&& b8 19 00 00 00&&&&&&& mov&&& $0x19,%eax
&&& 16:&& eb f7&&&&&&&&&&&&jmp&&& f &testfun+0xf&
可以看到,编译器使用的是 jne (不相等跳转)指令,并且 else block 中的代码紧跟在后面。
8:&& 75 07&&&&&&&&&&&&&&jne&&& 11 &testfun+0x11&
a:&& b8 06 00 00 00&&&&&&&&& mov&&& $0x6,%eax
第二个例子: example2.c
int testfun(int x)
&&if(__builtin_expect(x, 1)) {
&&&&&&&&&&&^^^ --- We instruct the compiler, &if& block is more probable
&&&&&&&x = 5;
&&&&&&&x = x *
&&} else {
&&&&&&&x = 6;
在这个例子中,我们认为 x 不为 0 的可能性更大
编译以后,通过 objdump 来观察汇编指令,在我2.4内核机器上,结果如下:
# gcc -O2 -c example2.c
# objdump -d example2.o
Disassembly of section .text:
&testfun&:
&& 0:&& 55&&&&&&&&&&&&&&&&&push&& %ebp
&& 1:&& 89 e5&&&&&&&&&&&&&&& mov&&& %esp,%ebp
&& 3:&& 8b 45 08&&&&&&&&&&&&&&mov&&& 0x8(%ebp),%eax
&& 6:&& 85 c0&&&&&&&&&&&&&&& test&& %eax,%eax
&& 8:&& 74 07&&&&&&&&&&&&&&& je&&&& 11 &testfun+0x11&
&& a:&& b8 19 00 00 00&&&&&&&&& &&&mov&&& $0x19,%eax
&& f:&& c9&&&&&&&&&&&&&&&&&leave
10:&& c3&&&&&&&&&&&&&&&&&&&ret
11:&& b8 06 00 00 00&&&&&&&&& &&&&&&&mov&&& $0x6,%eax
16:&& eb f7&&&&&&&&&&&&&&&&& jmp&&& f &testfun+0xf&
这次编译器使用的是 je (相等跳转)指令,并且 if block 中的代码紧跟在后面。
&& 8:&& 74 07&&&&&&&&&&&&&&& je&&&& 11 &testfun+0x11&
&& a:&& b8 19 00 00 00&&&&&&&&& &&&mov&&& $0x19,%eax
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:122702次
积分:2043
积分:2043
排名:第11949名
原创:78篇
转载:54篇
评论:13条
(1)(1)(1)(1)(3)(2)(1)(3)(3)(1)(1)(1)(5)(2)(5)(2)(1)(2)(1)(3)(7)(7)(3)(5)(1)(1)(1)(1)(7)(1)(6)(4)(2)(3)(7)(9)(7)(4)(13)(5)}

我要回帖

更多关于 cpu cache 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信