为什么atomic比mutex性能更高?

atomic 做的事情:原子指令修改内存,内存栅栏保障修改可见,必要时锁总线。 mutex 大致做的事情:短暂原子 compare and set 自旋如果未成功上锁,futex(&lock, FUTEX_WAIT… ) 退避进入阻塞等待直到 lock 值变化时唤醒。futex 在设计上期望做到如果无争用,则可以不进内核态,不进内核态的 fast path 的开销等价于 atomic 判断。内核里维护按地址维护一张 wait queue 的哈希表,发现锁变量值的变化(解锁)时,唤醒对应的 wait queue 中的一个 task。wait queue 这个哈希表的槽在更新时也会遭遇争用,这时继续通过 spin lock 保护。

参考

https://www.zhihu.com/question/302472384 https://eli.thegreenplace.net/2018/basics-of-futexes/ https://speakerdeck.com/kavya719/lets-talk-locks?slide=58

CppCon 2017: Fedor Pikus “C++ atomics, from basic to advanced. What do they really do?”

https://www.youtube.com/watch?v=ZQFzMfHIxng Atomics:the tool of lock-free programming Lock-free means “fast” Compare performance of two programs ■Both programs perform the same computations and get the same results ■Both programs are correct -No”wait loops”or other tricks ■One program uses std::mutex,the other is wait-free(even better than lock-free!) 原子:无锁编程工具无锁意味着“快速” 比较两个程序的性能 ■两个程序执行相同的计算并获得相同的结果 ■两个程序都是正确的-没有“等待循环”或其他技巧 ■一个程序使用std :: mutex,另一个程序无需等待(甚至比无锁!) Lock-free means”fast” cpp std::atomic<unsigned long>sum; ■Program A: cpp void do work(size t N,unsigned long* a){ for(sizeti=0;i<N;++i)sum +=a[i]; } ■Program B: cpp unsigned long sum(0); std::mutex M; void do_work(size_t N,unsigned long* a){ unsigned long s=0; for(sizeti=0;i<N;++i) s+=a[i]; std::lock guard<std::mutex>L(M); sum +=s; } Is lock-free faster? Algorithm rules supreme ■”Wait-free”has nothing to do with time -Wait-free refers to the number of compute”steps” -Steps do not have to be of the same duration ■Atomic operations do not guarantee good performance ■There is no substitute for understanding what you’re doing -This class is the next best thing aLet’s now understand C++atomics 免锁更快吗? 最高算法规则 ■“免等待”与时间无关-免等待是指计算“步数” -步骤不必持续相同的时间 ■原子操作不能保证良好的性能 ■不能替代理解您正在做的事情-此类是我们现在了解C ++ atomics的下一件好事 What is an atomic operation? aAtomic operation is an operation that is guaranteed to execute as a single transaction: -Other threads will see the state of the system before the operation started or after it finished,but cannot see any intermediate state -At the low level,atomic operations are special hardware instructions (hardware guarantees atomicity) -This is a general concept,not limited to hardware instructions(example:database transactions) 什么是原子操作? aAtomic操作是保证可以作为单个事务执行的操作: -其他线程将在操作开始之前或完成之后看到系统的状态,但是看不到任何中间状态 -在底层,原子操作是特殊的硬件指令(硬件保证原子性) -这是一个一般概念,不是仅限于硬件说明(例如:数据库事务) Data sharing in C++ ■C++03:what’s a thread? ■C++11:std::atomic #include std::atomicx(0);// NOT std::atomicx=0; ■++xis now atomic! -another thread cannot access x during increment C ++中的数据共享 ■C ++ 03:什么是线程? ■C ++ 11:std :: atomic #include std :: atomic x(0); //不是std :: atomic x = 0; ■++现在是原子的! -增量期间另一个线程无法访问x std::atomic ■What C++types can be made atomic? ■What operations can be done on these types? ■Are all operations on atomic types atomic? ■How fast are atomic operations? -Are atomic operations slower than non-atomic? -Are atomic operations faster than locks? ■Is”atomic”same as”lock-free”? If atomic operations avoid locks,there is no waiting,right? std :: atomic ■哪些C ++类型可以设为atomic? ■可以对这些类型执行哪些操作? ■是否对原子类型进行了所有操作? ■原子操作有多快? -原子操作是否比非原子操作慢? -原子操作比锁快吗? ■“原子”是否与“无锁”相同? 如果原子操作避免了锁,那就没有等待了,对吧? What types can be made atomic? ■Any trivially copyable type can be made atomic ■What is trivially copyable? -Continuous chunk of memory -Copying the object means copying all bits(memcpy) -No virtual functions,noexcept constructor std::atomic<int>i;//OK std:atomic<double>x;//OK struct S { long x;long y;}; std::atomic<S>s;//OK! 哪些类型可以做成原子的? ■任何普通可复制类型都可以设为原子 ■什么是普通可复制? -连续的内存块-复制对象意味着复制所有位(memcpy)-没有虚函数,除了构造函数 std::atomic<int>i;//OK std:atomic<double>x;//OK struct S { long x;long y;}; std::atomic<S>s;//OK! What operations can be done on std::atomic? ■Assignment(read and write)-always ■Special atomic operations ■Other operations depend on the type T 可以在std :: atomic 上执行哪些操作? ■始终进行赋值(读和写)■特殊原子操作■其他操作取决于类型T OK,what operations can be done on std::atomic? ■One of these is not the same as the others: std:atomicx{0};//Notx=0!×(0)is OK ++x; X++; X+=1; x|=2; x*=2; inty=x*2; x=y+1; X=X+1; x=x*2; 好,可以在std :: atomic 上执行哪些操作? ■其中之一与其他人不同: std:atomic x {0}; // Notx = 0!×(0)是OK ++ x; X ++; X + = 1; x | = 2; x * = 2; inty = x * 2; x = y + 1; X = X + 1; x = x * 2; std::atomicand overloaded operators std::atomicprovides operator overloads only for atomic operations(incorrect code does not compile③) ■Any expression with atomic variables will not be computed atomically(easy to make mistakes) ■++x;is the same as x+=1;is the same as x=x+1; -Unless x is atomic! std :: atomic 和重载运算符std :: atomic 仅为原子操作提供运算符重载(错误代码无法编译③) ■任何带有原子变量的表达式都不会被原子计算(容易出错) ■+ + x;与x + = 1相同;与x = x + 1相同; -除非x是原子! What operations can be done on std::atomicfor other types? ■Assignment and copy(read and write)for all types -Built-in and user-defined ■Increment and decrement for raw pointers ■ Addition,subtraction,and bitwise logic operations for integers(++,+=,-,-=,|=,&=,^=) astd::atomicis valid,no special operations astd:atomicis valid,no special operations -No atomic increment for floating-point numbers! 对于其他类型,可以在std :: atomic 上执行哪些操作? ■所有类型的赋值和复制(读和写)-内置和用户定义 ■原始指针的增减 ■整数(++,+ =,-,-=,| =,&=,^ =)的加,减和按位逻辑运算astd :: atomic 有效,无特殊运算astd:atomic 有效,无特殊操作-浮点数无原子增量! What “other operations”can be done on std::atomic? Explicit reads and writes: std::atomicx; Ty=x.load();// Same as Ty=x; x.store(y);// Same asx=y; a Atomic exchange: Tz=x.exchange(y);//Atomically:z=x;x=y; Compare-and-swap(conditional exchange): bool success=x.compare_exchange_strong(y,z); //lfx==y,make x=z and return true //Otherwise,set y=x and return false ■Key to most lock-free algorithms 可以在std :: atomic 上执行哪些“其他操作”? 显式读写: std :: atomic x; Ty = x.load(); //与Ty = x相同; x.store(y); // asx = y;原子交换: Tz = x.exchange(y); //原子地:z = x; x = y; 比较交换(条件交换): bool success = x.compare_exchange_strong(y,z); // lfx == y,使x = z并返回true //否则,将y = x并返回false ■大多数无锁算法的键 What is so special about CAS? a Compare-and-swap (CAS)is used in most lock-free algorithms aExample:atomic increment with CAS: std::atomicx{0}; int×0=x; while(!x.compare_exchange_strong(x0,x0+1)){} ■For int,we have atomic increment,but CAS can be used to increment doubles,multiply integers,and many more while(!x.compare_exchange_strong(x0,×0*2)){} CAS有什么特别之处? 大多数无锁算法中都使用比较交换(CAS),例如:CAS的原子增量: std :: atomic x {0}; int×0 = x; while(!x.compare_exchange_strong(x0,x0 + 1)){}■对于int,我们具有原子增量,但是CAS可用于递增双精度,乘以整数,还有更多while(!x.compare_exchange_strong(x0,× 0 * 2)){} What”other operations” can be done on std::atomic? ■For integerT: std:atomicx; x.fetch_add(y);//Same asx+=y; intz=x.fetch_add(y);// Same asz=(x+=y)-y; Also fetch_sub(),fetch and(),fetch_or(),fetch_xor() -Same as +=,-=etc operators More verbose but less error-prone than operators and expressions -Including load()and store()instead of operator=() 可以在std :: atomic 上执行哪些“其他操作”? ■对于integerT: std:atomic x; x.fetch_add(y); //同一个asx + = y; intz = x.fetch_add(y); //相同asz =(x + = y)-y;一个fetch_sub(),fetch和(),fetch_or() ,fetch_xor()-与+ =,-= etc运算符相同,比运算符和表达式更冗长,但更不容易出错-包括load()和store(),而不是operator =() std::atomic and overloaded operators std::atomicprovides operator overloads only for atomic operations(incorrect code does not compile9) Any expression with atomic variables will not be computed atomically(easy to make mistakes) Member functions make atomic operations explicit ■Compilers understand you either way and do exactly what you asked -Not necessarily what you wanted Programmers tend to see what they thought you meant not what you really meant(x=x+1) std :: atomic 和重载运算符std :: atomic 仅为原子操作提供运算符重载(错误代码无法编译9)带有原子变量的任何表达式都不会被原子计算(容易出错)成员函数明确的原子操作■编译器会以两种方式理解您并完全按照您的要求进行操作-不一定是您想要的内容程序员往往会看到他们认为您的意思不是您真正的意思(x = x + 1) How fast are atomic operations? Performance should be measured ■ Caution:measurement results will be hardware and compiler specific and should not be over-generalized! ■Caution:comparing atomic and non-atomic operations may be instructive for understanding of what the hardware does,but is rarely directly useful -Comparing atomic operation with another thread-safe alternative is valid and useful 原子操作有多快? 应测量性能 ■小心:测量结果将取决于硬件和编译器,不应过于笼统! ■警告:比较原子操作和非原子操作可能有助于理解硬件的功能,但很少直接有用-将原子操作与另一种线程安全的替代方法进行比较是有效且有用的 Is atomic the same as lock-free? std::atomic is hiding a huge secret:it’s not always lock-free long x; struct A {longx;} struct B{long x;longy;}; struct C { long x;long y;long z;}; 原子与无锁相同吗? std :: atomic隐藏了一个巨大的秘密:它并不总是无锁long x;结构A {longx;}结构B {long x; longy;}; struct C {long x; long y; long z;}; Strong and weak compare-and-swap ■C++provides two versions of CAS-weak and strong “x.compare_exchange _strong(old_x,new_x): if(x==old_x){x=new_x;return true;} else {oldx=x;return false;} ax.compare exchange_weak(old_x,new_x):same thing but can”spuriously fail”and return false even if x==old_x ■What is the value of old_x if this happens? 强和弱比较和交换 ■C ++提供了两个CAS弱版本和强“ x.compare_exchange _strong(old_x,new_x)版本: if(x == old_x){x = new_x; return true;} else {oldx = x; return false;} ax.compare exchange_weak(old_x,new_x):相同,但即使x可能也会“虚假失败”并返回false == old_x ■如果发生这种情况,old_x的值是什么?