C++ 原子操作比锁定时间长(无争用)
我试图在没有争用的情况下测量各种同步选项的开销。我使用以下程序:C++ 原子操作比锁定时间长(无争用),c++,multithreading,locking,atomic,compiler-optimization,C++,Multithreading,Locking,Atomic,Compiler Optimization,我试图在没有争用的情况下测量各种同步选项的开销。我使用以下程序: #include <atomic> #include <chrono> #include <iostream> #include <mutex> void function() { static volatile uint64_t counter = 0; counter++; } void function2() { std::atomic<uin
#include <atomic>
#include <chrono>
#include <iostream>
#include <mutex>
void function() {
static volatile uint64_t counter = 0;
counter++;
}
void function2() {
std::atomic<uint64_t> counter2 = 0;
counter2++;
}
int main() {
// warm up the cache
std::mutex lock;
for( int i=0; i<1'000'000; ++i ) {
std::lock_guard<std::mutex> locker(lock);
function();
function2();
}
std::cout<<"Starting test\n";
auto start = std::chrono::high_resolution_clock::now();
for( int i=0; i<1'000'000; ++i ) {
std::lock_guard<std::mutex> locker(lock);
function();
}
auto end = std::chrono::high_resolution_clock::now();
std::cout<<" With lock took "<<std::chrono::ceil<std::chrono::nanoseconds>(end-start).count()<<"ns\n";
start = std::chrono::high_resolution_clock::now();
for( int i=0; i<1'000'000; ++i ) {
function();
}
end = std::chrono::high_resolution_clock::now();
std::cout<<" No lock took "<<std::chrono::ceil<std::chrono::nanoseconds>(end-start).count()<<"ns\n";
start = std::chrono::high_resolution_clock::now();
for( int i=0; i<1'000'000; ++i ) {
function2();
}
end = std::chrono::high_resolution_clock::now();
std::cout<<"Atomic lock took "<<std::chrono::ceil<std::chrono::nanoseconds>(end-start).count()<<"ns\n";
}
因此,无论是否锁定,结果都相当相似(通常不锁定的速度稍快),但使用原子操作,我得到了x6性能的提升
逻辑上说,如果没有争用,相关变量都将是运行中的CPU缓存的本地变量,并且所有同步选项或多或少都会导致相同的性能
我遗漏了什么?问题在于基准测试程序 第一个是未声明
std::atomic
,导致在每次迭代中使用一个新的。第二个问题是程序没有使用-pthread
编译,导致锁定代码成为no-op(这解释了为什么带锁定的代码与不带锁定的代码运行速度相同)
解决这两个问题会产生:
Starting test
With lock took 21013047ns
No lock took 2125868ns
Atomic lock took 6744567ns
因此,原子方法大约比不锁定慢3倍,而实际锁定大约慢10倍。我想知道。。。
std::atomic::isu lock\u free
是否返回true?我在编译该测试时遇到了非常奇怪的问题。我确实检查过装配。它使用了lock add
,因此无论是无锁的
返回什么,实际上它都是无锁的。我会尝试使用多个线程运行测试:)@MFnx您可以,但这里的重点是测量无争用的性能。您的函数可以做非常不同的事情。一个重复增加相同的uint64\u t
,而另一个在每次迭代中创建一个新的std::atomic
。将其改为比较,原子操作胜出了很多。我认为原子操作在单线程情况下没有得到优化,因为您仍然可以在信号处理程序中使用它们。如果有非信号处理程序选项,则可以对其进行优化。无论如何,像原子对象一样的本地“寄存器”可以被优化掉!
Starting test
With lock took 21013047ns
No lock took 2125868ns
Atomic lock took 6744567ns