游客 你可以选择到 登录 注册帐号 忘记密码?
  • 服务热线:
    13999268016
高性能Spin_lock介绍
一.以下是我公司的一个高性能spin_lock, 可以挑战目前所有linux下的spin_lock版本
class Spin_lock
{
public:
Spin_lock( void );
void lock( void );
bool try_lock( void );
void unlock( void );
Spin_lock( const Spin_lock& ) = delete;
Spin_lock& operator = ( const Spin_lock& ) = delete;
private:
std::atomic<bool> d_atomic_bool;
};
Spin_lock::Spin_lock()
{
d_atomic_bool.store( false, std::memory_order_relaxed );
return;
}
void Spin_lock::lock( void )
{
while( d_atomic_bool.exchange( true, std::memory_order_acquire ) ) {
while( 1 ) {
_mm_pause();         // pause指令 延迟时间大约是12纳秒
if( !d_atomic_bool.load( std::memory_order_relaxed ) ) break;
std::this_thread::yield();         // 在无其他线程等待执行的情况下,延迟时间113纳秒
 // 在有其他线程等待执行情况下,将切换线程
if( !d_atomic_bool.load( std::memory_order_relaxed ) ) break;
continue;
}
continue;
}
return;
}
bool Spin_lock::try_lock( void )
{
return !d_atomic_bool.exchange( true, std::memory_order_acquire );
}
void Spin_lock::unlock( void )
{
d_atomic_bool.store( false, std::memory_order_release ); // 设置为false
return;
}
我们的核心lock()代码中关键的是第1行
    while( d_atomic_bool.exchange( true, std::memory_order_acquire ) )
    首先是使用了更高效率的exchange, 要比CAS指令有更高的效率,是内存读写模式,每次都要独占cache,并且将其他CPU的cache设置为invalide, 如果只有这1行代码,那么 如果两个以上CPU在等待这把锁,将交替将64字节Cacheline内存反复同步, 数据冲突激烈,影响其他线程的内存操作,降低整体性能, 影响全部CPU的工作效率, 因为产生了内存争用, 产生了总线争用。
因此我们提供了第2行关键检测代码
    if( !d_atomic_flag.load( std::memory_order_relaxed ) ) break
    该指令主要是为了减少CAS指令循环对内存总线带来的不良影响, 这个循环只有在锁被释放后, 才会跳出循环, 但是这个指令消耗的资源极少。这组指令是 只读模式检查, 只需要锁没有被释放, 这里继续循环, 但是这组spin是没有竞争状态的, 不会对内存和cache产生任何影响, 只要锁不释放, 永远不会内存总线的争夺, 因此大大提高了整体的效率。
二.常规数据库瓶颈
    
atomic的exchange/test_and_set/check_and_swap一般都需要serialization,即在执行这条指令前,CPU必须要完成前面所有对memory的访问指令(read and write)。这是非常heavy的操作,使得CPU无法对指令进行reorder,从而优化执行。
    因为每一次atomic的exchange/test_and_set/check_and_swap都必须读-写 state 这个变量。这就要涉及到多个CPU之间cache coherence的问题。
    当CPU1读state的时候,如果这个state没有在CPU1的cache中,就需要从memory中读入,因为CPU又需要写这个变量,所以在把这个变量读入cache的时候,如果其他CPU已经cache了这个变量,就需要invalidate它们。这样在CPU1把lock读入自己的cache中时,这块cacheline所cache的lock就是CPU1所独占的,CPU1就可以更改它的值了。如果多个CPU在竞争这个spinlock的话,每一次test_and_set都需要完成以上的操作,在系统总线上会产生大量的traffic,开销是非常大的,而且unlock的CPU还必须同其它正在竞争spinlock的CPU去竞争cacheline ownership. 随着CPU数目的增多,性能会衰减的非常快。
    常见的 spin_lock 在性能上挖的坑,很多范例里面的spin_lock只是大学生的作业,只能作为作业, 真的不能使用,一旦使用,效率会非常更低。
例如:
class spinlock {
    private:
        typedef enum {Locked, Unlocked} LockState;
        std::atomic<LockState> state_;
    public:
        /// Constructor
        spinlock() noexcept : state_(Unlocked) {}
       /// Blocks until a lock can be obtained for the current execution agent.
    void lock() noexcept {
    //  这里第1行exchange是写模式,每次都要独占cache,并且将其他CPU的cache设置为invalide, 
    //  如果两个以上CPU在等待这把锁,将交替将64字节内存反复同步,整个内存总线被无效的数据读写占用,
    //  数据冲突激烈,影响其他线程的内存操作,降低整体系统的性能
    //  在系统总线上会产生大量的traffic,开销是非常大的,而且unlock的CPU还必须同其它正在竞争spinlock的CPU去竞争cacheline ownership. 随着CPU数目的增多,性能会衰减的非常快。 
    
目前包括C++ 准标准库Boost库在内的spin_lock都存在一些缺陷导致的性能问题。很多版本要么过于简单,出现上面的问题,要么使用了usleep()/nanosleep()/sleep()等代码, 例如:linux下usleep(0)/nanosleep(0)实际测试是50微秒的延迟时间,而我们的锁占用总时长可能只有1-10个微秒
  
咨询热线:13999268016
地址:新疆维吾尔自治区乌鲁木齐市水磨沟区南湖东路77号新疆上海科技合作基地八层801室 固话:6583723
返回首页 | 网站地图 | 联系我们
技术支持:创世网络
乌鲁木齐云山云海信息技术有限责任公司 版权所有 备案号:新ICP备17001390号
新公网安备 65010502000200号
网站访问人数: