原子操作在C++中的使用

前言     

      所谓原子操作,就是“不可中断的一个或一系列操作”。

      在单核心处理器系统中,能够在一条机器指令中完成的操作都可以认为是原子操作,因为中断只能发生于指令之间。这也是某些CPU指令系统中引入了test_and_set、test_and_clear等指令用于临界资源互斥的原因。
在对称多处理器(Symmetric Multi-Processor)结构中就不同了,由于系统中有多个处理器在独立地运行,即使能在单条指令中完成的操作也有可能受到干扰。

CPU对原子操作的影响

intel处理器

      《Intel 64 and IA-32 Architectures Software Developer`s Manual》Volume3 System Programming Guide,8.1.1 Guaranteed Atomic Operations中讲解的原子操作如下:

      Intel486处理器(以及以后生产的处理器)确保以下对基本存储器的操作行为为原子操作:
      Reading or writing a byte
      读写一个byte
      Reading or writing a word aligned on a 16-bit boundary
      读写16bit(2byte)内存对齐的字(word)
      Reading or writing a doubleword aligned on a 32-bit boundary
      读写32bit(4byte)内存对齐的双字(dword)
      The Pentium processor (and newer processors since) guarantees that the following additional memory operationswill always be carried out atomically:
      Pentium系列处理器(以及以后生产的处理器)确保以下对基本存储器的操作行为为原子操作:
      Reading or writing a quadword aligned on a 64-bit boundary
      读写64bit(8byte)内存对齐的四字(quadword)
      16-bit accesses to uncached memory locations that fit within a 32-bit data bus
      使用16bit访问的未缓存的内存,并且这些内存适应32位数据总线(翻译不好)
      The P6 family processors (and newer processors since) guarantee that the following additional memory operationwill always be carried out atomically:
      P6系列处理器(以及以后生产的处理器)确保以下对基本存储器的操作行为为原子操作:
      Unaligned 16-, 32-, and 64-bit accesses to cached memory that fit within a cache line
      对单个cache line中缓存地址的未对齐的16/32/64位访问(非对齐的数据访问非常影响性能)
      Accesses to cacheable memory that are split across cache lines and page boundaries are not guaranteed to beatomic by the Intel Core 2 Duo, Intel®Atom™, Intel Core Duo, Pentium M, Pentium 4, Intel Xeon, P6 family, Pentium, and Intel486 processors. The Intel Core 2 Duo, Intel Atom, Intel Core Duo, Pentium M, Pentium 4, IntelXeon, and P6 family processors provide bus control signals that permit external memory subsystems to make splitaccesses atomic; however, nonaligned data accesses will seriously impact the performance of the processor andshould be avoided.
     那些被总线带宽、cache line以及page大小给分隔开了的内存地址的访问不是原子的,你如果想保证这些操作是原子的,你就得求助于机制Bus Lock,对总线发出相应的控制信号才行。
      An x87 instruction or an SSE instructions that accesses data larger than a quadword may be implemented usingmultiple memory accesses. If such an instruction stores to memory, some of the accesses may complete (writingto memory) while another causes the operation to fault for architectural reasons (e.g. due an page-table entry thatis marked “not present”). In this case, the effects of the completed accesses may be visible to software eventhough the overall instruction caused a fault. If TLB invalidation has been delayed (see Section 4.10.4.4), suchpage faults may occur even if all accesses are to the same page.

     在intel处理器上c++中一些基本的运算那些是基本操作?(本人CPU为Intel Core i5-4590 3.3GHz, x, y均为int型)

 

操作
反汇编代码(VS2010)                    
++i mov         eax,dword ptr [i]  
add         eax,1  
mov         dword ptr [i],eax
i++ mov         eax,dword ptr [i]  
add         eax,1  
mov         dword ptr [i],eax
x = y mov         eax,dword ptr [y]  
mov         dword ptr [x],eax
x = 1 mov         dword ptr [x],1
x + y mov         eax,dword ptr [x]  
add         eax,dword ptr [y]
x + 1 mov         eax,dword ptr [x]  
add         eax,1
x += y mov         eax,dword ptr [x]  
add         eax,dword ptr [y]  
mov         dword ptr [x],eax
x += 1 mov         eax,dword ptr [x]  
add         eax,1  
mov         dword ptr [x],eax 
x - y mov         eax,dword ptr [x]  
sub         eax,dword ptr [y] 
x - 1 mov         eax,dword ptr [x]  
sub         eax,1
x -= y mov         eax,dword ptr [x]  
sub         eax,dword ptr [y]  
mov         dword ptr [x],eax
x -= 1 mov         eax,dword ptr [x]  
sub         eax,1  
mov         dword ptr [x],eax
x * y mov         eax,dword ptr [x]  
imul        eax,dword ptr [y]
x * 10(x*1和x*2会被优化) mov         eax,dword ptr [x]  
imul        eax,eax,0Ah
x *= y mov         eax,dword ptr [x]  
imul        eax,dword ptr [y]  
mov         dword ptr [x],eax
x *= 10 mov         eax,dword ptr [x]  
imul        eax,eax,0Ah  
mov         dword ptr [x],eax
x / y mov         eax,dword ptr [x]  
cdq  
idiv        eax,dword ptr [y]
x / 10 mov         eax,dword ptr [x]  
cdq  
mov         ecx,0Ah  
idiv        eax,ecx
10 / x mov         eax,0Ah  
cdq  
idiv        eax,dword ptr [x]
x /= y mov         eax,dword ptr [x]  
cdq  
idiv        eax,dword ptr [y]  
mov         dword ptr [x],eax
x /= 10 mov         eax,dword ptr [x]  
cdq  
mov         ecx,0Ah  
idiv        eax,ecx  
mov         dword ptr [x],eax
x % y mov         eax,dword ptr [x]  
cdq  
idiv        eax,dword ptr [y]
x % 10 mov         eax,dword ptr [x]  
cdq  
mov         ecx,0Ah  
idiv        eax,ecx
x %= y mov         eax,dword ptr [x]  
cdq  
idiv        eax,dword ptr [y]  
mov         dword ptr [x],edx
x %= 10 mov         eax,dword ptr [x]  
cdq  
mov         ecx,0Ah  
idiv        eax,ecx  
mov         dword ptr [x],edx
x >> y mov         eax,dword ptr [x]  
mov         ecx,dword ptr [y]  
sar         eax,cl
x >> 1 mov         eax,dword ptr [x]  
sar         eax,1
x << y mov         eax,dword ptr [x]  
mov         ecx,dword ptr [y]  
shl         eax,cl
x << 1 mov         eax,dword ptr [x]  
shl         eax,1
x >>= y mov         eax,dword ptr [x]  
mov         ecx,dword ptr [y]  
sar         eax,cl  
mov         dword ptr [x],eax
x >>= 1 mov         eax,dword ptr [x]  
sar         eax,1  
mov         dword ptr [x],eax
x <<= y mov         eax,dword ptr [x]  
mov         ecx,dword ptr [y]  
shl         eax,cl  
mov         dword ptr [x],eax
x <<= 1 mov         eax,dword ptr [x]  
shl         eax,1  
mov         dword ptr [x],eax
x & y mov         eax,dword ptr [x]  
and         eax,dword ptr [y]
x & 10 mov         eax,dword ptr [x]  
and         eax,0Ah
x &= y mov         eax,dword ptr [x]  
and         eax,dword ptr [y]  
mov         dword ptr [x],eax
x &= 10 mov         eax,dword ptr [x]  
and         eax,0Ah  
mov         dword ptr [x],eax
x | y mov         eax,dword ptr [x]  
or          eax,dword ptr [y] 
x | 10 mov         eax,dword ptr [x]  
or          eax,0Ah
x |= y mov         eax,dword ptr [x]  
or          eax,dword ptr [y]  
mov         dword ptr [x],eax
x |= 10 mov         eax,dword ptr [x]  
or          eax,0Ah  
mov         dword ptr [x],eax
x ^ y mov         eax,dword ptr [x]  
xor         eax,dword ptr [y] 
x ^ 10 mov         eax,dword ptr [x]  
xor         eax,0Ah
x ^= y mov         eax,dword ptr [x]  
xor         eax,dword ptr [y]  
mov         dword ptr [x],eax
x ^= 10 mov         eax,dword ptr [x]  
xor         eax,0Ah  
mov         dword ptr [x],eax
!x xor         eax,eax  
cmp         dword ptr [x],0  
sete        al
~x mov         eax,dword ptr [x]  
not         eax

从图表中看出就 x = 1为一条汇编操作指令。那我们再来看下不同类型下的的值为x赋值的结果:

 

类型 反汇编代码(vs2010)
char x = 10; mov         byte ptr [x],0Ah
unsigned char x = 10; mov         byte ptr [x],0Ah
short x = 10; mov         eax,0Ah  
mov         word ptr [x],ax
unsigned short x = 10; mov         eax,0Ah  
mov         word ptr [x],ax
int x = 10; mov         dword ptr [x],0Ah
unsigned int x = 10; mov         dword ptr [x],0Ah
long x = 10; mov         dword ptr [x],0Ah
unsigned long x = 10; mov         dword ptr [x],0Ah
long long x = 0xFFFFFFFFFFFF; mov         dword ptr [x],0FFFFFFFFh  
mov         dword ptr [ebp-8],0FFFFh
unsigned long long x = 0xFFFFFFFFFFFF; mov         dword ptr [x],0FFFFFFFFh  
mov         dword ptr [ebp-8],0FFFFh
float x = 10.20; fld         dword ptr [__real@41233333 (0F6311Ch)]  
fstp        dword ptr [x]
double x = 10.20; fld         qword ptr [__real@4024666666666666 (13D3120h)]  
fstp        qword ptr [x]
__int8 x = 10;
mov         byte ptr [x],0Ah
__int16 x = 10;
mov         eax,0Ah  
mov         word ptr [x],ax
__int32 x = 10;
mov         dword ptr [x],0Ah 
__int64 x = 0xFFFFFFFFFFFF;
mov         dword ptr [x],0FFFFFFFFh  
mov         dword ptr [ebp-8],0FFFFh


C++中线程同步


什么情况需要线程同步

1、有两个或两个以上的线程对同一份数进行操作,而且这些线程中存在写操作。

2、每个线程对该数据的操作完成后才能进行其他线程对该数据的操作,也就是乱序会对数据产生”撕裂“或数据难以预料。

比如说一个结构体重有两个成员变量{int x; int y;}, 如果一个线程对该结构体的实例进行写操作{x = 1, y = 2},另一个也进行写操作{x = 3, y = 4};如果没有进行线程同步,会存在如下几种情况:

 

x = 1 y =2
x = 1 y =4
x = 3 y =2
x = 3 y = 4

这样会造成数据的”撕裂“,从而需要我们进行线程同步保证数据要么是{x = 1, y = 2},要么是{x = 3, y = 4}。

在C++中面对内建类型的多线程同步,非原子操作并不一定需要线程同步


操作 反汇编代码(VS2010)  
说明                                                
同步?
++i mov         eax,dword ptr [i]  
add         eax,1  
mov         dword ptr [i],eax
存在对同一数据的读写, 乱序会造成数据异常 需要
i++ mov         eax,dword ptr [i]  
add         eax,1  
mov         dword ptr [i],eax
同上 需要
x = y mov         eax,dword ptr [y]  
mov         dword ptr [x],eax
非对同一数据的读写,而且乱序不会对数据结果产生影响 不需要
x = 1 mov         dword ptr [x],1 本来就是原子操作 不需要
x + y mov         eax,dword ptr [x]  
add         eax,dword ptr [y]
x + y不赋值给其他变量是没有意义的,不做讨论  
x + 1 mov         eax,dword ptr [x]  
add         eax,1
不赋值给其他变量是没有意义的,不做讨论  
x += y mov         eax,dword ptr [x]  
add         eax,dword ptr [y]  
mov         dword ptr [x],eax
存在对同一数据的读写, 乱序会造成数据异常 需要
x += 1 mov         eax,dword ptr [x]  
add         eax,1  
mov         dword ptr [x],eax 
存在对同一数据的读写, 乱序会造成数据异常 需要
x - y mov         eax,dword ptr [x]  
sub         eax,dword ptr [y] 
不赋值给其他变量是没有意义的,不做讨论  
x - 1 mov         eax,dword ptr [x]  
sub         eax,1
不赋值给其他变量是没有意义的,不做讨论  
x -= y mov         eax,dword ptr [x]  
sub         eax,dword ptr [y]  
mov         dword ptr [x],eax
存在对同一数据的读写, 乱序会造成数据异常 需要
x -= 1 mov         eax,dword ptr [x]  
sub         eax,1  
mov         dword ptr [x],eax
存在对同一数据的读写, 乱序会造成数据异常 需要
x * y mov         eax,dword ptr [x]  
imul        eax,dword ptr [y]
不赋值给其他变量是没有意义的,不做讨论  
x * 10(x*1和x*2会被优化) mov         eax,dword ptr [x]  
imul        eax,eax,0Ah
不赋值给其他变量是没有意义的,不做讨论  
x *= y mov         eax,dword ptr [x]  
imul        eax,dword ptr [y]  
mov         dword ptr [x],eax
存在对同一数据的读写, 乱序会造成数据异常 需要
x *= 10 mov         eax,dword ptr [x]  
imul        eax,eax,0Ah  
mov         dword ptr [x],eax
存在对同一数据的读写, 乱序会造成数据异常 需要
x / y mov         eax,dword ptr [x]  
cdq  
idiv        eax,dword ptr [y]
不赋值给其他变量是没有意义的,不做讨论  
x / 10 mov         eax,dword ptr [x]  
cdq  
mov         ecx,0Ah  
idiv        eax,ecx
不赋值给其他变量是没有意义的,不做讨论  
10 / x mov         eax,0Ah  
cdq  
idiv        eax,dword ptr [x]
不赋值给其他变量是没有意义的,不做讨论  
x /= y mov         eax,dword ptr [x]  
cdq  
idiv        eax,dword ptr [y]  
mov         dword ptr [x],eax
存在对同一数据的读写, 乱序会造成数据异常 需要
x /= 10 mov         eax,dword ptr [x]  
cdq  
mov         ecx,0Ah  
idiv        eax,ecx  
mov         dword ptr [x],eax
存在对同一数据的读写, 乱序会造成数据异常 需要
x % y mov         eax,dword ptr [x]  
cdq  
idiv        eax,dword ptr [y]
不赋值给其他变量是没有意义的,不做讨论  
x % 10 mov         eax,dword ptr [x]  
cdq  
mov         ecx,0Ah  
idiv        eax,ecx
不赋值给其他变量是没有意义的,不做讨论  
x %= y mov         eax,dword ptr [x]  
cdq  
idiv        eax,dword ptr [y]  
mov         dword ptr [x],edx
存在对同一数据的读写, 乱序会造成数据异常 需要
x %= 10 mov         eax,dword ptr [x]  
cdq  
mov         ecx,0Ah  
idiv        eax,ecx  
mov         dword ptr [x],edx
存在对同一数据的读写, 乱序会造成数据异常 需要
x >> y mov         eax,dword ptr [x]  
mov         ecx,dword ptr [y]  
sar         eax,cl
不赋值给其他变量是没有意义的,不做讨论  
x >> 1 mov         eax,dword ptr [x]  
sar         eax,1
不赋值给其他变量是没有意义的,不做讨论  
x << y mov         eax,dword ptr [x]  
mov         ecx,dword ptr [y]  
shl         eax,cl
不赋值给其他变量是没有意义的,不做讨论  
x << 1 mov         eax,dword ptr [x]  
shl         eax,1
不赋值给其他变量是没有意义的,不做讨论  
x >>= y mov         eax,dword ptr [x]  
mov         ecx,dword ptr [y]  
sar         eax,cl  
mov         dword ptr [x],eax
存在对同一数据的读写, 乱序会造成数据异常 需要
x >>= 1 mov         eax,dword ptr [x]  
sar         eax,1  
mov         dword ptr [x],eax
存在对同一数据的读写, 乱序会造成数据异常 需要
x <<= y mov         eax,dword ptr [x]  
mov         ecx,dword ptr [y]  
shl         eax,cl  
mov         dword ptr [x],eax
存在对同一数据的读写, 乱序会造成数据异常 需要
x <<= 1 mov         eax,dword ptr [x]  
shl         eax,1  
mov         dword ptr [x],eax
存在对同一数据的读写, 乱序会造成数据异常 需要
x & y mov         eax,dword ptr [x]  
and         eax,dword ptr [y]
不赋值给其他变量是没有意义的,不做讨论  
x & 10 mov         eax,dword ptr [x]  
and         eax,0Ah
不赋值给其他变量是没有意义的,不做讨论  
x &= y mov         eax,dword ptr [x]  
and         eax,dword ptr [y]  
mov         dword ptr [x],eax
存在对同一数据的读写, 乱序会造成数据异常 需要
x &= 10 mov         eax,dword ptr [x]  
and         eax,0Ah  
mov         dword ptr [x],eax
存在对同一数据的读写, 乱序会造成数据异常 需要
x | y mov         eax,dword ptr [x]  
or          eax,dword ptr [y] 
不赋值给其他变量是没有意义的,不做讨论  
x | 10 mov         eax,dword ptr [x]  
or          eax,0Ah
不赋值给其他变量是没有意义的,不做讨论  
x |= y mov         eax,dword ptr [x]  
or          eax,dword ptr [y]  
mov         dword ptr [x],eax
存在对同一数据的读写, 乱序会造成数据异常 需要
x |= 10 mov         eax,dword ptr [x]  
or          eax,0Ah  
mov         dword ptr [x],eax
存在对同一数据的读写, 乱序会造成数据异常 需要
x ^ y mov         eax,dword ptr [x]  
xor         eax,dword ptr [y] 
不赋值给其他变量是没有意义的,不做讨论  
x ^ 10 mov         eax,dword ptr [x]  
xor         eax,0Ah
不赋值给其他变量是没有意义的,不做讨论  
x ^= y mov         eax,dword ptr [x]  
xor         eax,dword ptr [y]  
mov         dword ptr [x],eax
存在对同一数据的读写, 乱序会造成数据异常 需要
x ^= 10 mov         eax,dword ptr [x]  
xor         eax,0Ah  
mov         dword ptr [x],eax
存在对同一数据的读写, 乱序会造成数据异常 需要
!x xor         eax,eax  
cmp         dword ptr [x],0  
sete        al
不赋值给其他变量是没有意义的,不做讨论  
~x mov         eax,dword ptr [x]  
not         eax
不赋值给其他变量是没有意义的,不做讨论  

对内建类型变量的赋值(数值):

 

类型 反汇编代码(vs2010) 说明                            
同步?       
char x = 10; mov         byte ptr [x],0Ah 原子操作 不需要
unsigned char x = 10; mov         byte ptr [x],0Ah 原子操作 不需要
short x = 10; mov         eax,0Ah  
mov         word ptr [x],ax
只有一个写操作 不需要
unsigned short x = 10; mov         eax,0Ah  
mov         word ptr [x],ax
只有一个写操作 不需要
int x = 10; mov         dword ptr [x],0Ah 原子操作 不需要
unsigned int x = 10; mov         dword ptr [x],0Ah 原子操作 不需要
long x = 10; mov         dword ptr [x],0Ah 原子操作 不需要
unsigned long x = 10; mov         dword ptr [x],0Ah 原子操作 不需要
long long x = 0xFFFFFFFFFFFF; mov         dword ptr [x],0FFFFFFFFh  
mov         dword ptr [ebp-8],0FFFFh
写写操作 需要
unsigned long long x = 0xFFFFFFFFFFFF; mov         dword ptr [x],0FFFFFFFFh  
mov         dword ptr [ebp-8],0FFFFh
写写操作 需要
float x = 10.20; fld         dword ptr [__real@41233333 (0F6311Ch)]  
fstp        dword ptr [x]
只有一个写操作 不需要
double x = 10.20; fld         qword ptr [__real@4024666666666666 (13D3120h)]  
fstp        qword ptr [x]
只有一个写操作 不需要
__int8 x = 10;
mov         byte ptr [x],0Ah
原子操作 不需要
__int16 x = 10;
mov         eax,0Ah  
mov         word ptr [x],ax
只有一个写操作 不需要
__int32 x = 10;
mov         dword ptr [x],0Ah 
原子操作 不需要
__int64 x = 0xFFFFFFFFFFFF;
mov         dword ptr [x],0FFFFFFFFh  
mov         dword ptr [ebp-8],0FFFFh
写写操作 需要


对内建类型变量的赋值(变量):

 

类型 反汇编代码(vs2010) 说明                            
同步?       
char x = y; mov         al,byte ptr [y]  
mov         byte ptr [x],al
非对同一数据的读写,而且乱序不会对数据结果产生影响 不需要
unsigned char x = y; mov         al,byte ptr [y]  
mov         byte ptr [x],al
非对同一数据的读写,而且乱序不会对数据结果产生影响 不需要
short x = y; mov         ax,word ptr [y]  
mov         word ptr [x],ax
非对同一数据的读写,而且乱序不会对数据结果产生影响 不需要
unsigned short x = y; mov         ax,word ptr [y]  
mov         word ptr [x],a
非对同一数据的读写,而且乱序不会对数据结果产生影响 不需要
int x = y; mov         eax,dword ptr [y]  
mov         dword ptr [x],eax
非对同一数据的读写,而且乱序不会对数据结果产生影响 不需要
unsigned int x = y; mov         eax,dword ptr [y]  
mov         dword ptr [x],eax
非对同一数据的读写,而且乱序不会对数据结果产生影响 不需要
long x = y; mov         eax,dword ptr [y]  
mov         dword ptr [x],eax
非对同一数据的读写,而且乱序不会对数据结果产生影响 不需要
unsigned long x = y; mov         eax,dword ptr [y]  
mov         dword ptr [x],eax
非对同一数据的读写,而且乱序不会对数据结果产生影响 不需要
long long x = y; mov         eax,dword ptr [y]  
mov         dword ptr [x],eax  
mov         ecx,dword ptr [ebp-18h]  
mov         dword ptr [ebp-8],ecx
对x存在写写操作,乱序会产出数据”撕裂“ 需要
unsigned long long x = y; mov         eax,dword ptr [y]  
mov         dword ptr [x],eax  
mov         ecx,dword ptr [ebp-18h]  
mov         dword ptr [ebp-8],ecx
对x存在写写操作,乱序会产出数据”撕裂“ 需要
float x = y; fld         dword ptr [y]  
fstp        dword ptr [x]
非对同一数据的读写,而且乱序不会对数据结果产生影响 不需要
double x = y; fld         qword ptr [y]  
fstp        qword ptr [x]
非对同一数据的读写,而且乱序不会对数据结果产生影响 不需要
__int8 x = y;
mov         al,byte ptr [y]  
mov         byte ptr [x],al
非对同一数据的读写,而且乱序不会对数据结果产生影响 不需要
__int16 x =y;
mov         ax,word ptr [y]  
mov         word ptr [x],ax
非对同一数据的读写,而且乱序不会对数据结果产生影响 不需要
__int32 x =y;
mov         eax,dword ptr [y]  
mov         dword ptr [x],eax
非对同一数据的读写,而且乱序不会对数据结果产生影响 不需要
__int64 x = y;
mov         eax,dword ptr [y]  
mov         dword ptr [x],eax  
mov         ecx,dword ptr [ebp-18h]  
mov         dword ptr [ebp-8],ecx
  需要


posted @ 2015-11-13 11:32  ForX  阅读(1678)  评论(0编辑  收藏  举报