- 浏览: 231079 次
- 性别:
- 来自: 南京
最新评论
-
baby8117628:
vc下mp3 IDv1和IDV2的读取 -
gezexu:
你好,我按照你的步骤一步步进行但是安装libvorbis的时候 ...
linux如何搭建强大的FFMPEG环境 -
ini_always:
帅哥,转载也把格式做好点,另外出处也要注明一下吧。。。
MP3文件格式解析
处理器的乱序和并发执行
目前的高级处理器,为了提高内部逻辑元件的利用率以提高运行速度,通常会采用多指令发射、乱序执行等各种措施。现在普遍使用的一些超标量处理器通常能够在一个指令周期内并发执行多条指令。处理器从L1 I-Cache预取了一批指令后,就会分析找出那些互相没有关联可以并发执行的指令,然后送到几个独立的执行单元进行并发执行。比如下面这样的代码(假定编译器不做优化):
z = x + y;
p = m + n;
CPU就有可能将这两行无关代码分别送到两个算术单元去同时执行。像Freescale的MPC8541这种嵌入式处理器一个指令周期能够加载4条指令、发射2条指令到流水线、用5个独立的执行单元来并发执行。
通常来说访存指令(由LSU单元执行)所需要的指令周期可能很多(可能要几十甚至上百个周期),而一般的算术指令通常在一个指令周期就搞定。所以有 可能代码中的访存指令耗费了多个周期完成执行后,其他几个执行单元可能已经把后面有多条逻辑上无关的算术指令都执行完了,这就产生了乱序。
另外访存指令之间也存在乱序的问题。高级的CPU可以根据自己Cache的组织特性,将访存指令重新排序执行。访问一些连续地址的可能会先执行,因为这时候Cache命中率高。有的还允许访存的Non-blocking,即如果前面一条访存指令因为Cache不命中,造成长延时的存储访问时,后面的访存指令可以先执行以便从Cache取数。对写指令的访存乱序有可能造成的错误后果,所以处理器通常有专门的机制(通常是做了个缓冲)保证在出现异常或者错误的时候,可以丢弃异常点后面的写指令的结果不做写入。
处理器的分支预测功能也能引起并发执行。处理器的分支预测单元有可能直接把两条分支的指令都预取来一块并发执行掉。等到分支判断的结果出来以后,再丢弃错误分支的计算结果。这样在很多情况下可以实现0周期跳转。比如这样的代码(假定编译器不做优化):
z = x + y;
if (z > 0) then
p = m + n;
else
p = m - n;
看上去如果z不计算出来是无法继续的。但是实际上CPU有可能先把三个加法都同时进行计算,然后根据z=x+y的结果直接挑选正确的p值。
因此,即使是从汇编上看顺序正确的指令,其执行的顺序也是不可预知的。处理器能够保证并发和乱序执行不会得到错误结果,但是如果是对一些硬件寄存器的操作不能允许乱序的话,程序员就必须把这个情况告诉CPU。告诉的方法就是通过CPU提供的一组同步指令实现,通常在CPU的文档里面有对同步指令的使用说明。系统函数库里面的内存屏障(rmb/wmb/mb)实际上也是通过这些同步指令实现的。因此在C编码的时候,只要设置好内存屏障,就能告诉CPU 哪些代码是不能乱序的。
编译器的乱序优化
受到处理器预取单元的能力限制,处理器每次只能分析一小块指令的并发性,如果指令相隔比较远就无能为力了。但是从编译器的角度来看,编译器能够对很大一个范围的代码进行分析,能够从更大的范围内分辨出可以并发的指令,并将其尽量靠近排列让处理器更容易预取和并发执行,充分利用处理器的乱序并发功能。所以现代的高性能编译器在目标码优化上都具备对指令进行乱序优化的能力。并且可以对访存的指令进行进一步的乱序,减少逻辑上不必要的访存,以及尽量提高 Cache命中率和CPU的LSU(load/store unit)的工作效率。所以在打开编译器优化以后,看到生成的汇编码并不严格按照代码的逻辑顺序是正常的。和处理器一样,如果想要告诉编译器不要去对某些指令乱序优化,也要通过一些方式来告诉编译器。通常可以通过volatile关键字来抑制(注意,不是禁止)编译器对相关变量的访问优化。举个例子:
int *p, *q;
......;
*p = 1;
*p = 2;
*q = *p;
这样,编译器通常会优化掉前面一个对*p的写入(逻辑上冗余),仅对*p写入2。而对*q赋值的时候,编译器认为此时*q的结果就应该是上次*p的值,会优化掉从*p取数的过程,直接把在寄存器中保存的*p的值给*q(PowrPC汇编):
(假设r3=p,r4=q)
li r5, 2 // r5赋值2
stw r5, 0(r3) // 把r5写到*p
stw r5, 0(r4) // 把r5写到*q
但是如果为p指针加上了volatile关键字,情况就不同了:
volatile int *p;
int *q;
......;
*p = 1;
*p = 2;
*q = *p;
在这种情况下,编译器看见*p是volatile的时候,就会:
1.
不对*p操作生成乱序指令(通常如此,具体请看后面的解释)
2.
每次从*p取数据的时候,一定会进行一次访存操作,哪怕前面不久才取过*p的值放在寄存器里。
3.
不合并对*p的写操作(也只是通常如此,解释见后)
所以这回的结果如下(PowrPC汇编):
(假设r3=p,r4=q)
li r5, 1 // r5赋值1
stw r5, 0(r3) // 把r5写到*p
li r5, 2 // r5赋值2
stw r5, 0(r3) // 把r5写到*p
lwz r5, 0(r3) // 从*p取值到r5
stw r5, 0(r4) // 把r5写到*q
这样编译器会在汇编码级别保证指令有序和不优化掉访存操作。通常简单地使用volatile关键字就可以解决编译器的乱序问题,但是这些指令到了处理器执行的时候,仍然可能被乱序。对于处理器乱序执行的避免就需要用到一组内存屏障函数(barrier)了。
重要
绝大多数的编译器,通常不会优化掉对volatile对象的访问,并且通常保持同一个volatile对象的一系列读写操作是有序的(但是不能保证不同的volatile对象之间有序)。
但是,这不是绝对的。因为ANSI C99标准关于对volatile对象访问时编译器是否要绝对保证禁止乱序(reorder)和禁止访问合并(combine access)并没有做任何规定!仅仅是鼓励编译器最好不要去优化对volatile对象的访问,而唯一的强制要求仅仅是要求编译器保证对 volatile对象的访问优化不会跨越“sequence point”即可(所谓sequence point是指一些诸如外部函数调用、条件或循环跳转等关键点,具体定义请查阅C99标准内的详细说明)。
这就是说,如果一个编译器在两个sequence point之间像对待普通变量一样去优化volatile变量,也是完全符合C99标准的!比如:
volatile int a;
if (...) { ... } // sequence point
a = 1;
a = 2;
a = 3;
printk("..."); // sequence point
在两个sequence point之间,要是有编译器对a的赋值操作合并(即仅写入3)或者乱序(如写1和写2对调),都是完全符合C99标准的。所以,我们在使用的时候,不能指望用了volatile以后绝对能生成有序的完整的汇编码,即不要指望volatile来保证访存有序。实质上 volatile最大的作用主要还是在保证每次使用从内存中取值,而并不能保证编译器不做其他任何优化(毕竟volatile从字面上看意思是“易变”而不是“有序”。编译器只保证对volatile对象即时更新但不保证访问有序也不是说不过去的)。
从另一个角度看,即使是编译器生成的汇编码有序,处理器也不一定能保证有序。就算编译器生成了有序的汇编码,到了处理器那里也拿不准是不是会按照代码顺序执行。所以就算编译器保证有序了,程序员也还是要往代码里面加内存屏障才能保证绝对访存有序,这倒不如编译器干脆不管算了,因为内存屏障本身就是一个sequence point,加入后已经能够保证编译器也有序。
因此,对于切实是需要保障访存顺序的代码,就算当前使用的编译器能够编译出有序的目标码来,我们也还是必须通过设置内存屏障的方式来保证有序,否则都是不严谨,有隐患的。
Barrier屏障函数
Barrier函数可以在代码中设置屏障,这个屏障可以阻挡编译器的优化,也可以阻挡处理器的优化。
对于编译器来说,设置任何一个屏障都可以保证:
1.
编译器的乱序优化不会跨越屏障,即屏障前后的代码不会乱序;
2.
在屏障后所有对变量或者地址的操作,都会重新从内存中取值(相当于刷新寄存器中的变量副本)。
而对于处理器来说,根据不同的屏障有不同的表现(以下仅仅列举3种最简单的屏障):
1.
读屏障rmb()
处理器对读屏障前后的取数指令(LOAD)能保证有序,但是不一定能保证其他算术指令或者是写指令的有序。对于读指令的执行完成时间也不能保证,即它不能保证在屏障之前的读指令一定都执行完成,只能保证屏障之前的读指令一定能在屏障之后的读指令之前完成。
2.
写屏障wmb()
处理器对屏障前后的写指令(STORE)能保证有序,但是不一定能保证其他算术指令或者是读指令的有序。对于写指令的执行完成时间也不能保证,即它不能保证在屏障之前的写指令一定都执行完成,只能保证屏障之前的写指令一定能在屏障之后的写指令之前完成。
3.
通用内存屏障mb()
处理器保障只有屏障之前的访存操作(包括读写)都完成以后才会执行屏障之后的访存操作。即可以保障读写之间的有序(但是同样无法保证指令完成的时间)。这种屏障对处理器的执行单元效率产生的负面影响要比单纯用读屏障或者写屏障来的大。比如对于PowerPC来说这种通用屏障通常是使用sync指令实现的,在这种情况下处理器会丢弃所有预取的指令并清空流水线。所以频繁使用内存屏障会降低处理器执行单元的效率。
对于驱动开发者来说,一些对设备寄存器的操作,通常是必须保证有序的。在绝大部分情况下,一般都是写操作。对于有序的写操作,必须设置写屏障(wmb):
例:在驱动中使用写屏障
/* Mask out everything */
im_intctl->ic_simrh = 0x00000000;
im_intctl->ic_simrl = 0x00000000;
wmb();
/* Ack everything */
im_intctl->ic_sipnrh = 0xffffffff;
im_intctl->ic_sipnrl = 0xffffffff;
这是一个对中断控制器操作的例子。在设置两个mask寄存器的值的时候,这两个写操作没有顺序要求,因此可以不加屏障。但是对ack寄存器的设置必须在mask寄存器完成设置以后,所以在中间要加入写屏障wmb()以保证对两组寄存器的写有序。
同样的,对于一系列的只读操作,也可以简单使用rmb()来保证有序。
注意
任何一个rmb()或者wmb()都是可以被替换成mb()的。但是因为上面提到过的mb()的效率问题,所以应该只有在同时需要读屏障和写屏障的时候,才建议使用mb()。否则应该根据实际情况来选择合适的屏障。当然,在设备初始化的时候,即使是使用mb()也不会对性能带来什么影响,因为设备一般只会初始化一次。但是在发生很频繁的设备操作(比如网口的收发帧中断等)时,应该考虑到mb()对性能的影响。
如果驱动不仅仅需要在单纯的读指令或者写指令之间有序,还需要保证读写指令之间有序的时候,就需要设置mb()屏障了。下面将演示一个这样的例子:
例:使用mb()屏障保证读写有序
我们假设有一个设备,在读取设备信息时需要依次对REG1~3这三个寄存器进行写入操作(写入设备读取命令),然后才能依次读取REG4和REG5取得设备返回的信息。
REG1 = a;
wmb(); // 保证REG1和REG2的写有序
REG2 = b;
wmb(); // 保证REG2和REG3的写有序
REG3 = c;
mb(); // 保证在对设备读之前,前面的配置操作都完成(读写之间有序)
*d = REG4;
rmb(); // 保证REG4和REG5的读有序
*e = REG5;
mb(); // 保证与未来对设备的操作有序
return;
*
对于REG1~3的写入,可以通过设置写屏障来保证有序;
*
在进行REG4和5的读取之前,因为得保证前面的寄存器写操作都执行完才能读,所以需要设置一个内存屏障mb()来保证前面对寄存器的写都完成,以保障读写指令之间的有序;
*
后面两个读操作之间就可以通过设置读屏障来保证有序了;
*
最后通常在从设备操作函数返回之前,我们一般需要保证对设备的操作都执行完毕了。这样下次对设备进行操作的时候我们可以保证设备已经完成了上次操作,避免反复调用设备操作函数带来的函数间的乱序问题。所以在最后设置一个内存屏障mb(),保障和未来对设备的其他访问有序。
进一步阅读
目前的高级处理器,为了提高内部逻辑元件的利用率以提高运行速度,通常会采用多指令发射、乱序执行等各种措施。现在普遍使用的一些超标量处理器通常能够在一个指令周期内并发执行多条指令。处理器从L1 I-Cache预取了一批指令后,就会分析找出那些互相没有关联可以并发执行的指令,然后送到几个独立的执行单元进行并发执行。比如下面这样的代码(假定编译器不做优化):
z = x + y;
p = m + n;
CPU就有可能将这两行无关代码分别送到两个算术单元去同时执行。像Freescale的MPC8541这种嵌入式处理器一个指令周期能够加载4条指令、发射2条指令到流水线、用5个独立的执行单元来并发执行。
通常来说访存指令(由LSU单元执行)所需要的指令周期可能很多(可能要几十甚至上百个周期),而一般的算术指令通常在一个指令周期就搞定。所以有 可能代码中的访存指令耗费了多个周期完成执行后,其他几个执行单元可能已经把后面有多条逻辑上无关的算术指令都执行完了,这就产生了乱序。
另外访存指令之间也存在乱序的问题。高级的CPU可以根据自己Cache的组织特性,将访存指令重新排序执行。访问一些连续地址的可能会先执行,因为这时候Cache命中率高。有的还允许访存的Non-blocking,即如果前面一条访存指令因为Cache不命中,造成长延时的存储访问时,后面的访存指令可以先执行以便从Cache取数。对写指令的访存乱序有可能造成的错误后果,所以处理器通常有专门的机制(通常是做了个缓冲)保证在出现异常或者错误的时候,可以丢弃异常点后面的写指令的结果不做写入。
处理器的分支预测功能也能引起并发执行。处理器的分支预测单元有可能直接把两条分支的指令都预取来一块并发执行掉。等到分支判断的结果出来以后,再丢弃错误分支的计算结果。这样在很多情况下可以实现0周期跳转。比如这样的代码(假定编译器不做优化):
z = x + y;
if (z > 0) then
p = m + n;
else
p = m - n;
看上去如果z不计算出来是无法继续的。但是实际上CPU有可能先把三个加法都同时进行计算,然后根据z=x+y的结果直接挑选正确的p值。
因此,即使是从汇编上看顺序正确的指令,其执行的顺序也是不可预知的。处理器能够保证并发和乱序执行不会得到错误结果,但是如果是对一些硬件寄存器的操作不能允许乱序的话,程序员就必须把这个情况告诉CPU。告诉的方法就是通过CPU提供的一组同步指令实现,通常在CPU的文档里面有对同步指令的使用说明。系统函数库里面的内存屏障(rmb/wmb/mb)实际上也是通过这些同步指令实现的。因此在C编码的时候,只要设置好内存屏障,就能告诉CPU 哪些代码是不能乱序的。
编译器的乱序优化
受到处理器预取单元的能力限制,处理器每次只能分析一小块指令的并发性,如果指令相隔比较远就无能为力了。但是从编译器的角度来看,编译器能够对很大一个范围的代码进行分析,能够从更大的范围内分辨出可以并发的指令,并将其尽量靠近排列让处理器更容易预取和并发执行,充分利用处理器的乱序并发功能。所以现代的高性能编译器在目标码优化上都具备对指令进行乱序优化的能力。并且可以对访存的指令进行进一步的乱序,减少逻辑上不必要的访存,以及尽量提高 Cache命中率和CPU的LSU(load/store unit)的工作效率。所以在打开编译器优化以后,看到生成的汇编码并不严格按照代码的逻辑顺序是正常的。和处理器一样,如果想要告诉编译器不要去对某些指令乱序优化,也要通过一些方式来告诉编译器。通常可以通过volatile关键字来抑制(注意,不是禁止)编译器对相关变量的访问优化。举个例子:
int *p, *q;
......;
*p = 1;
*p = 2;
*q = *p;
这样,编译器通常会优化掉前面一个对*p的写入(逻辑上冗余),仅对*p写入2。而对*q赋值的时候,编译器认为此时*q的结果就应该是上次*p的值,会优化掉从*p取数的过程,直接把在寄存器中保存的*p的值给*q(PowrPC汇编):
(假设r3=p,r4=q)
li r5, 2 // r5赋值2
stw r5, 0(r3) // 把r5写到*p
stw r5, 0(r4) // 把r5写到*q
但是如果为p指针加上了volatile关键字,情况就不同了:
volatile int *p;
int *q;
......;
*p = 1;
*p = 2;
*q = *p;
在这种情况下,编译器看见*p是volatile的时候,就会:
1.
不对*p操作生成乱序指令(通常如此,具体请看后面的解释)
2.
每次从*p取数据的时候,一定会进行一次访存操作,哪怕前面不久才取过*p的值放在寄存器里。
3.
不合并对*p的写操作(也只是通常如此,解释见后)
所以这回的结果如下(PowrPC汇编):
(假设r3=p,r4=q)
li r5, 1 // r5赋值1
stw r5, 0(r3) // 把r5写到*p
li r5, 2 // r5赋值2
stw r5, 0(r3) // 把r5写到*p
lwz r5, 0(r3) // 从*p取值到r5
stw r5, 0(r4) // 把r5写到*q
这样编译器会在汇编码级别保证指令有序和不优化掉访存操作。通常简单地使用volatile关键字就可以解决编译器的乱序问题,但是这些指令到了处理器执行的时候,仍然可能被乱序。对于处理器乱序执行的避免就需要用到一组内存屏障函数(barrier)了。
重要
绝大多数的编译器,通常不会优化掉对volatile对象的访问,并且通常保持同一个volatile对象的一系列读写操作是有序的(但是不能保证不同的volatile对象之间有序)。
但是,这不是绝对的。因为ANSI C99标准关于对volatile对象访问时编译器是否要绝对保证禁止乱序(reorder)和禁止访问合并(combine access)并没有做任何规定!仅仅是鼓励编译器最好不要去优化对volatile对象的访问,而唯一的强制要求仅仅是要求编译器保证对 volatile对象的访问优化不会跨越“sequence point”即可(所谓sequence point是指一些诸如外部函数调用、条件或循环跳转等关键点,具体定义请查阅C99标准内的详细说明)。
这就是说,如果一个编译器在两个sequence point之间像对待普通变量一样去优化volatile变量,也是完全符合C99标准的!比如:
volatile int a;
if (...) { ... } // sequence point
a = 1;
a = 2;
a = 3;
printk("..."); // sequence point
在两个sequence point之间,要是有编译器对a的赋值操作合并(即仅写入3)或者乱序(如写1和写2对调),都是完全符合C99标准的。所以,我们在使用的时候,不能指望用了volatile以后绝对能生成有序的完整的汇编码,即不要指望volatile来保证访存有序。实质上 volatile最大的作用主要还是在保证每次使用从内存中取值,而并不能保证编译器不做其他任何优化(毕竟volatile从字面上看意思是“易变”而不是“有序”。编译器只保证对volatile对象即时更新但不保证访问有序也不是说不过去的)。
从另一个角度看,即使是编译器生成的汇编码有序,处理器也不一定能保证有序。就算编译器生成了有序的汇编码,到了处理器那里也拿不准是不是会按照代码顺序执行。所以就算编译器保证有序了,程序员也还是要往代码里面加内存屏障才能保证绝对访存有序,这倒不如编译器干脆不管算了,因为内存屏障本身就是一个sequence point,加入后已经能够保证编译器也有序。
因此,对于切实是需要保障访存顺序的代码,就算当前使用的编译器能够编译出有序的目标码来,我们也还是必须通过设置内存屏障的方式来保证有序,否则都是不严谨,有隐患的。
Barrier屏障函数
Barrier函数可以在代码中设置屏障,这个屏障可以阻挡编译器的优化,也可以阻挡处理器的优化。
对于编译器来说,设置任何一个屏障都可以保证:
1.
编译器的乱序优化不会跨越屏障,即屏障前后的代码不会乱序;
2.
在屏障后所有对变量或者地址的操作,都会重新从内存中取值(相当于刷新寄存器中的变量副本)。
而对于处理器来说,根据不同的屏障有不同的表现(以下仅仅列举3种最简单的屏障):
1.
读屏障rmb()
处理器对读屏障前后的取数指令(LOAD)能保证有序,但是不一定能保证其他算术指令或者是写指令的有序。对于读指令的执行完成时间也不能保证,即它不能保证在屏障之前的读指令一定都执行完成,只能保证屏障之前的读指令一定能在屏障之后的读指令之前完成。
2.
写屏障wmb()
处理器对屏障前后的写指令(STORE)能保证有序,但是不一定能保证其他算术指令或者是读指令的有序。对于写指令的执行完成时间也不能保证,即它不能保证在屏障之前的写指令一定都执行完成,只能保证屏障之前的写指令一定能在屏障之后的写指令之前完成。
3.
通用内存屏障mb()
处理器保障只有屏障之前的访存操作(包括读写)都完成以后才会执行屏障之后的访存操作。即可以保障读写之间的有序(但是同样无法保证指令完成的时间)。这种屏障对处理器的执行单元效率产生的负面影响要比单纯用读屏障或者写屏障来的大。比如对于PowerPC来说这种通用屏障通常是使用sync指令实现的,在这种情况下处理器会丢弃所有预取的指令并清空流水线。所以频繁使用内存屏障会降低处理器执行单元的效率。
对于驱动开发者来说,一些对设备寄存器的操作,通常是必须保证有序的。在绝大部分情况下,一般都是写操作。对于有序的写操作,必须设置写屏障(wmb):
例:在驱动中使用写屏障
/* Mask out everything */
im_intctl->ic_simrh = 0x00000000;
im_intctl->ic_simrl = 0x00000000;
wmb();
/* Ack everything */
im_intctl->ic_sipnrh = 0xffffffff;
im_intctl->ic_sipnrl = 0xffffffff;
这是一个对中断控制器操作的例子。在设置两个mask寄存器的值的时候,这两个写操作没有顺序要求,因此可以不加屏障。但是对ack寄存器的设置必须在mask寄存器完成设置以后,所以在中间要加入写屏障wmb()以保证对两组寄存器的写有序。
同样的,对于一系列的只读操作,也可以简单使用rmb()来保证有序。
注意
任何一个rmb()或者wmb()都是可以被替换成mb()的。但是因为上面提到过的mb()的效率问题,所以应该只有在同时需要读屏障和写屏障的时候,才建议使用mb()。否则应该根据实际情况来选择合适的屏障。当然,在设备初始化的时候,即使是使用mb()也不会对性能带来什么影响,因为设备一般只会初始化一次。但是在发生很频繁的设备操作(比如网口的收发帧中断等)时,应该考虑到mb()对性能的影响。
如果驱动不仅仅需要在单纯的读指令或者写指令之间有序,还需要保证读写指令之间有序的时候,就需要设置mb()屏障了。下面将演示一个这样的例子:
例:使用mb()屏障保证读写有序
我们假设有一个设备,在读取设备信息时需要依次对REG1~3这三个寄存器进行写入操作(写入设备读取命令),然后才能依次读取REG4和REG5取得设备返回的信息。
REG1 = a;
wmb(); // 保证REG1和REG2的写有序
REG2 = b;
wmb(); // 保证REG2和REG3的写有序
REG3 = c;
mb(); // 保证在对设备读之前,前面的配置操作都完成(读写之间有序)
*d = REG4;
rmb(); // 保证REG4和REG5的读有序
*e = REG5;
mb(); // 保证与未来对设备的操作有序
return;
*
对于REG1~3的写入,可以通过设置写屏障来保证有序;
*
在进行REG4和5的读取之前,因为得保证前面的寄存器写操作都执行完才能读,所以需要设置一个内存屏障mb()来保证前面对寄存器的写都完成,以保障读写指令之间的有序;
*
后面两个读操作之间就可以通过设置读屏障来保证有序了;
*
最后通常在从设备操作函数返回之前,我们一般需要保证对设备的操作都执行完毕了。这样下次对设备进行操作的时候我们可以保证设备已经完成了上次操作,避免反复调用设备操作函数带来的函数间的乱序问题。所以在最后设置一个内存屏障mb(),保障和未来对设备的其他访问有序。
进一步阅读
发表评论
-
函数调用堆栈分析
2010-02-26 10:53 1362理解调用栈最重要的两 ... -
mtrace检测内存泄露
2010-02-25 09:50 1055[url] http://math.acadiau.ca/AC ... -
c语言编程之字符串操作
2010-02-25 09:41 8151. //在s串中查找与s1相匹配的字符串,找到后用 ... -
linux C 链接库 so制作及调用[转]
2010-02-24 16:26 2549文章分类:C++编程 [文章作者:陈毓端 若转载请标注原文链 ... -
mtrace的使用
2010-02-24 16:02 1258对于内存溢出之类的麻烦可能大家在编写指针比较多的复杂的程序的时 ... -
单片机的C语言中位操作用法(转
2010-02-24 14:27 2185单片机的C语言中位操作用法 作者:郭天祥 在对单处机进 ... -
Linux下的itoa函数
2010-02-21 17:55 1700上篇文章说到linux需要it ... -
va_list、va_start、va_arg、va_end的原理与使用
2010-02-05 10:34 28611. 概述 由于在C语言中没有函数重载,解 ... -
快速排序(quickSort)
2010-02-04 10:50 8261. #include <stdio.h> ... -
C问题---itoa函数
2010-02-04 10:36 996------------------------------ ... -
itoa函数及atoi函数
2010-02-04 10:35 1269C语言提供了几个标准库函数,可以将任意类型(整型、长整型、浮点 ... -
结构体零长度数组的作用
2010-02-04 10:21 1338在一些 C 语言编写的代码中,有时可以看到如下定义的结构: ... -
优化C代码常用的几招
2010-02-04 10:14 733性能优化方面永远注意8 ... -
我经常去的网站
2010-02-03 17:53 1585MFC相关网站 www.codeproject.com ht ... -
可重入函数与不可重入函数
2010-02-03 16:35 890原文地址:http://blog.chin ... -
linux线程池及其测试
2010-02-03 16:32 2318/*----------------------------- ... -
哈夫曼编码
2010-02-03 16:26 1263本文描述在网上能够找到的最简单,最快速的哈夫曼编码。本方法不使 ... -
优化变成了忧患:String.split引发的“内存泄露”
2010-02-01 17:39 1061一直赞叹Sun对待技术的 ... -
锁无关的(Lock-Free)数据结构——在避免死锁的同时确保线程
2010-01-26 14:47 874http://hi.baidu.com/%5F%E2%64%5 ... -
使用 GNU profiler 来提高代码运行速度
2010-01-26 13:46 758进应用程序的性能是一 ...
相关推荐
内存屏障分析,内存屏障分析,内存屏障分析,内存屏障分析,内存屏障分析
Linux内存屏障知识讲解,彻底了解内存屏障
中国Linux内核开发者大会十周年演讲稿(中兴通讯谢宝友)-Linux内存屏障
内存屏障浅析,多线程编程,由于编译器的优化和缓存的使用,导致对内存的写入操作不能及时的反应出来,也就是说当完成对内存的写入操作之后,读取出来的可能是旧的内容
内核同步机制-优化屏障和内存屏障,linux内核。
内存屏障是为应付内存访问操作的乱序执行而生的. 那么, 内存访问为什么会乱序呢? 这里先简要介绍一下: 现在的CPU一般采用流水线来执行指令. 一个指令的执行被分成: 取指, 译码, 访存, 执行,写回, 等若干个阶段. ...
主要为大家讲解JVM内存模型|内存结构|内存屏障,他们的概念,有什么关联以及各种的功能
国外的文章,比较详细的介绍了各种cpu的内存访问顺序问题
其java内存模型中更重要的,应该是内存屏障,memoryfence较粗犷,代价也提到内存屏障,先应该说到重排序,这强调下,重排序只对于那些在当前线程没有依赖关
乱序执行和内存屏障
java内存屏障与JVM并发详解实用.pdf
Memory Barriers: a Hardware View for Software Hackers 讲解内存屏障的好论文,推荐!
Linux内存屏障,CSDN 2015开发者大会上的优秀演讲
linux内核文档<<LINUX KERNEL MEMORY BARRIERS>>的中文翻译及译注.
volatile是JVM提供的一种最轻量级的同步机制,因为Java内存模型为volatile定义特殊的访问规则,使其可以实现Java内存模型中的两大特性...这篇文章主要介绍了Java多线程之volatile关键字及内存屏障,需要的朋友可以参考下
一文读懂原子操作、内存屏障、锁(偏向锁、轻量级锁、重量级锁、自旋锁)、Disruptor、Go Context之上半部分.doc
3. CPU 0 执行 b = 1,它已经在缓存行中有“b”的值了 (换句话说,缓存行已 4. CPU 0 接收到“读”消息,并且发送缓存行中的最新的“b”的值
写屏障:强制将写缓冲器中的内容写入到高速缓存中,或者将屏障之后的指令全部写到写缓冲器直到之前写缓冲器中的内容全部被刷回缓存中,也就是处理 0 必须等到所有的 i
处理器重排序与内存屏障指令 happens-before 重排序 数据依赖性 as-if-serial 语义 程序顺序规则 重排序对多线程的影响 顺序一致性 数据竞争与顺序一致性保证 顺序一致性内存模型 同步程序的顺序一致性效果 未同步...
SPARC内存一致性检查器Ax是一种根据SPARC内存一致性模型检查内存跟踪(由加载,存储,原子读取-修改-写入和内存屏障指令组成)的工具:顺序一致性(SC),总存储顺序(TSO),部分存储顺序(PSO)和宽松的内存顺序...