i++ 原子操作的讨论 转自http://gcoder.blogbus.com/logs/75275569.html

围棋C语言代码要满十字 围棋源码 linux/UNIX 汉字编码GB2312 立即下载

i++ 原子操作的讨论

日期:2010-09-19 | 分类:c & c++

今天在CU上和cjaizss 讨论i++是不是原子操作的问题,收获很多,我之前有很多想法都是错误的,做个总结,记录下来。(习惯i++了,下面讨论的i和x是同一含义)

我对原子操作的理解是需要一条cpu指令的操作,如果i是32位的,并且没有跨页,那么i++是原子的,如果i是64位的,那么不是原子的,它需要两条cpu指令。

i 是32位时的汇编,addl    $1, %eax是 i++ 对应的cpu指令

    movl    x, %eax

    addl    $1, %eax

    movl    %eax, x

i 是64位时的汇编,addl    $1, %eax 和adcl    $0, %edx 是i++对应的cpu指令

    movl    x, %eax

    movl    x+4, %edx                                             

    addl    $1, %eax

    adcl    $0, %edx                                              

    movl    %eax, x

    movl    %edx, x+4 

但是i++是原子的(假设i是32位的),并不能保证多个线程i++,操作同一个i,可以得到正确的结果。因为还有寄存器的因素,多个cpu对应多个寄存器。每次要先把i从内存复制到寄存器,然后++,然后再把i复制到内存中,这需要至少3步。从这个意义上讲,说i++是原子的并不对。

gcc扩展提供了原子的i++,其对应的汇编是

    lock

    addl    $1, x

多个cpu对应的寄存器不同,但是内存是相同的。(我记得cpu不能直接操作内存,这个addl    $1, x,我不知道是啥意思)

 

我之前以为只要加上volatile关键字就可以保证,就不会出现i对应的内容在多个cpu的寄存器中不一致的情况,看来理解是错的,它只是在编译器优化时,提示每次都从内存中读取i的值。

观察优化后的代码(O3)也很有意思,

没有volatile修饰的i对应的汇编

    addl    $100000000, x

略去了循环,一次累加了所有的。有volatile时对应的汇编

    movl    x, %eax

    addl    $1, %edx

    addl    $1, %eax

    cmpl    $100000000, %edx

    movl    %eax, x

和没优化时差别不大。

 

测试代码

  1. #include <stdio.h>
  2. #include <signal.h>
  3. #include <pthread.h>
  4. #define MYNUM 100000000
  5. #ifdef A0
  6.   int x = 0;
  7. # define ADD(x) do { (x)++; } while(0)
  8. #elif defined A1
  9.   volatile sig_atomic_t x = 0;
  10. # define ADD(x) do { (x)++; } while(0)
  11. #elif defined A2
  12.   int x = 0;
  13. # define ADD(x) do { __sync_fetch_and_add(&(x), 1); } while (0)
  14. #elif defined A3
  15.   int x = 0;
  16.   pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
  17. # define ADD(x) do { pthread_mutex_lock(&mutex); (x)++; pthread_mutex_unlock(&mutex); } while (0)
  18. #else
  19. # error "no add method"
  20. #endif
  21. void* func1(void* arg) 
  22. {
  23.     int i;
  24.     for(i = 0; i < MYNUM; i++) 
  25.         ADD(x);
  26. }
  27. int main()
  28. { 
  29.     pthread_t id1,id2;
  30.     pthread_create(&id1, NULL, func1, NULL); 
  31.     pthread_create(&id2, NULL, func1, NULL); 
  32.     pthread_join(id1, NULL);
  33.     pthread_join(id2, NULL);
  34.     printf("x=%d\nMYNUM*2=%d\n", x, MYNUM*2);  
  35.     return 0;
  36. }

编译

gcc -o a0 inc.c -pthread -DA0

gcc -o a1 inc.c -pthread -DA1

gcc -o a2 inc.c -pthread -DA2

gcc -o a3 inc.c -pthread -DA3

 

运行(只少要俩cpu)

A0和A1,无疑运行最快,当然答案是错的。

A2比A1慢8倍左右,可见cpu寄存器的影响之大。

A3比A2慢5倍左右,可见加锁始终是个问题,不得已而为之。

 

这些体会,可能不怎么正确,还有待进一步验证。

我不会汇编,这是个问题。08年就认识到这个问题了,现在感觉如此强烈,是时候学习了。


收藏到: Del.icio.us
Linux下C语言2048游戏代码 Linux下C语言实现2048小游戏 立即下载

相关推荐

攻防世界:Crypto习题之easy_RSA

在非对称加密算法中,单向函数被广泛应用,用于确保从公钥加密后的密文无法轻易推导出私钥,从而保证数据的安全性。而其中最著名的算法便是 RSA 加密算法。首先设φm:=∣1≤k≤mgcdkm1∣为欧拉函数(Euler’sche Phi-function)。pqn:=pqpqφnnpqpqφnp−1q−1e∈12...φn−1gcdφne1ended≡1φn假设我们现在有明文x以及公钥en。

qq_52789488的博客 1572

计算阶乘n!末尾所含0的个数

n! 0的个数

行风 3423

STM32+LoRa模块实战:从环境搭建到数据传输完整指南(附避坑清单)

本文提供STM32与LoRa模块从环境搭建到数据传输的完整实战指南,涵盖硬件连接、开发环境配置、AT指令设置、频段优化及低功耗设计等关键步骤,特别针对常见问题提供解决方案和避坑清单,助力物联网开发者高效实现远距离低功耗通信。

weixin_29168813的博客 290

select函数的第一个参数为什么是最大描述符加1呢

ChinaUnix.net 标题: 一直不明白,select函数的第一个参数为什么是最大描述符加1呢? [打印本页] 作者: iw1210    时间: 2013-01-23 18:51     标题: 一直不明白,select函数的第一个参数为什么是最大描述符加1呢? 本帖最后由 iw1210 于 2013-01-23 18:51 编辑  一直不明白,select函数的第一个

6194

如何用YOLOv5改进无人机小目标检测?MFFSODNet实战教程(附代码)

本文详细介绍了如何利用YOLOv5框架结合MFFSODNet网络结构优化无人机航拍图像中的小目标检测。通过引入微小目标预测头、多尺度特征提取模块(MSFEM)和双向密集特征金字塔网络(BDFPN)等核心技术,显著提升了小目标检测精度。文章提供了完整的代码实现方案,并展示了在农业监测、电力巡检等实际场景中的应用效果。

sre5engineer的博客 739

SAP中MRP运行计划行超限问题的分析与解决

SAP中基于生产性物料的采购特点,通常会采用采购计划协议的方式进行采购。其具体的采购计划通常来自于成品计划独立需求,然后通过物料清单,经由MRP跑出。 在采购计划协议中的计划行中,存在一个计划行号,随着MRP在不断跑出新的计划行,该行号资源会消耗完。当计划行号消耗完后,MRP不会再跑出新的计划行。且在MD04或MD02中看不到类似提示信息。此时可以通过MD05来查看是否出现类似下图的提示信息。 ...

专注SAP应用技术文章写作,致力于拉近用户与系统间的距离; 3845

递归的理解

对于递归问题,一定要明确的一点是,初始调用该函数时的输入是什么样的; 递归执行的顺序是从后往前(直到递归结束的条件发生),迭代是从前到后; 递归:n!=n*(n-1)! ⇒ 0!=1 迭代:n! ⇒ for i = 1:n: res *= i 递归能够奏效的前提是,问题的规模一定是减少的,或者更为严谨地说,问题一定是朝着递归结束的条件执行的; 递归函数的第一...

weixin_30596165的博客 272

从标记基因到功能分析:手把手教你人工注释单细胞亚群的完整流程

本文详细介绍了人工注释单细胞亚群的完整流程,从标记基因筛选到功能分析验证,特别针对肿瘤微环境、发育轨迹等复杂场景提供解决方案。通过差异表达分析、多维数据整合和实验验证,帮助研究者准确识别细胞类型,提升单细胞转录组研究的生物学解读深度。

jenkins8butler的博客 244

sed与状态机

问题1:假设文本中music与realist成对出现,替换所有的realist与music所在行.解决方法1:sed -nr /music/{h;ba;};/realist/{h;bb;};p;d;:a n;/realist/{p;g;s/([^/n]*)/n(.*)//2/n/1/;p;be;};H;ba;:b n;/music/{p;g;s/([^/n]*)/n(.*)//2/n/1

fertiland的专栏 693

【实战指南】DBeaver EE Windows版高效破解与配置全解析

本文详细解析了DBeaver EE Windows版的高效破解与配置方法,涵盖Java环境配置、破解包部署、配置文件修改等关键步骤,并提供了性能优化和数据库连接的最佳实践。适用于需要解锁企业版高级功能的数据库开发人员,帮助提升工作效率。

weixin_28837817的博客 1903

基于python+flask框架的基于微信小程序的CR公司建设项目信息管理系统-前台(开题+程序+论文) 计算机毕设

特别是随着移动互联网技术的普及,企业对便捷、高效的信息管理系统的需求日益迫切。另一方面,该系统集成了仓管、物料、财务、人事、供应商等多个模块,实现了数据的集中管理和信息共享,有助于企业实现精细化管理,降低运营成本,提升决策效率。:深入调研CR公司的业务需求,明确系统应具备的功能模块,包括仓管员管理模块(物料信息、退回记录、申请信息、仓库信息管理)、财务员管理模块(资金信息管理、采购与租赁款项处理)、人事管理模块、供应商管理模块、项目管理模块(项目信息、设备采购、物料采购、设备租赁)、以及考勤打卡模块等。

zhihao503的博客 960

C/C++ 通用 Makefile

http://www.chinaunix.net 作者:whyglinux  发表于:2008-04-26 22:46:39 【发表评论】 【查看原文】 【C/C++讨论区】【关闭】 [size=5]C/C++ 通用 Makefile[/size]  [color=green]Generic Makefile for C/C++ Program[/co

badboyplane的专栏 1197

AutoSAR系列讲解(入门篇)2.1-AppL概述

该章主要是讲解SWC是如何在ECU中分布的

雪云飞星的博客 4万+

++i 和 i++ 是否为原子操作?

2021年02月22日 周一 天气晴 【不悲叹过去,不荒废现在,不惧怕未来】 本文目录1. 操作系统的原子操作2. 从汇编代码看 ++i 和 i++ 是否为原子操作3. 常见问题:进程有一个全局变量i,还有两个线程。i++ 在两个线程里边分别执行100次,能得到的最大值和最小值分别是多少?3.1 多核CPU最小值为2,最大值2003.2 单核CPU最小值为100,最大值200参考文献 1. 操作系统的原子操作 原子操作是不可分割的,在执行完毕前不会被任何其它任务或事件中断: 在单线程中, 能够在单条指令

m0_37433111的博客 1202

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

面试题【1】:i++是否原子操作?并解释为什么?

不是原子操作。理由: 1.i++分为三个阶段: 内存到寄存器 寄存器自增 写回内存 这三个阶段中间都可以被中断分离开.  2.++i首先要看编译器是怎么编译的, 某些编译器比如VC在非优化版本中会编译为以下汇编代码: __asm {         moveax,  dword ptr[i]         inc eax         mov dwordptr[i],

YEYUANGEN的专栏 2万+

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

i++是原子操作吗?怎么保证其原子性

i++是原子操作吗?怎么保证其原子性 i++不是原子操作,因为有多个步骤,为了保证其原子性,可以使用AtomicInteger类的getAndIncrement()方法实现i++ 我们先来看一下getAndIncrement的源代码:          *    public final int getAndIncrement() {          *        f

ustcyy91的博客 4403

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

i++和++i是否为原子操作

i++和++i是原子操作么

u010214092的专栏 7885

GNU C中x++是原子操作吗?

什么是原子操作? 所谓原子操作,就是"不可中断的一个或一系列操作" 。在确认一个操作是原子的情况下,多线程环境里面,我们可以避免仅仅为保护这个操作在外围加上性能开销昂贵的锁,甚至借助于原子操作,我们可以实现互斥锁。 那么,GNU C中x++是原子操作吗?请看下面一段代码:#include #include static int x;void inc(){ x++;

刘爱贵的专栏 4731

关于i++是不是原子操作的问题

i++是不是原子操作?这个看似简单的问题,实则背后有很深的坑,今天就来踩踩这个坑。 之所以要讨论某个操作是不是原子操作,是因为一旦确认某个操作是原子操作的话,就不用为了去保护这个操作而加上昂贵又要耗费性能的锁。而在单核CPU中,中断只会发生在指令间,可以把能够在一个指令间完成的操作,看成是原子操作。 处理器虽然会自动保证基本的内存操作的原子性,但对于多核CPU的复杂的内存操作,处理器是不能保证...

lx_Frolf的博客 1万+

i++是否是原子操作?

i++不是原子操作,原因是: i++操作分为三个阶段(读、改、写): 内存到寄存器 寄存器自增 写回内存 这三个阶段可以被中断分离。 List 什么是操作系统的“原子操作” 原子操作是不可分割的,在执行完毕不会被任何其它任务或事件中断,分为两种情况(两种都应该满足) (1) 在单线程中, 能够在单条指令中完成的操作都可以认为是" 原子操作",因为中断只能发生于指令之间。 (2) 在多线程中,不能被其它进程(线程)打断的操作就叫原子操作。 面试的时候经常问的一道题目是i++在两个线程里边分别执.

思凡 2588

i++ 不是原子操作

1. 什么是操作系统的“原子操作”      原子操作是不可分割的,在执行完毕不会被任何其它任务或事件中断,分为两种情况(两种都应该满足)      (1) 在单线程中, 能够在单条指令中完成的操作都可以认为是" 原子操作",因为中断只能发生于指令之间。       (2) 在多线程中,不能被其它进程(线程)打断的操作就叫原子操作。   2.   面试的时候经

huaweitman的专栏 9613

i++ 或 ++i 是原子操作吗?

前面写了一篇文章,讨论了i++和++i的区别(https://blog.csdn.net/lijianqingfeng/article/details/103474225)。该文的前提都是不存在并发的情况,通过i++ 和++i编译后的代码分析其执行效果。 public class Test { private static volatile int count = 0; pu...

lijianqingfeng的专栏 3314
上一篇: ExecutorService线程池 转自 http://blog.sina.com.cn/s/blog_6151984a0100krj8.html
下一篇: Public Key RSA Encryption in C# .NET
明斯基
博客等级 码龄18年 24粉丝 6原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值