SpinLock的可扩展性和局限性

时间:2016-05-11 09:35:09

标签: java multithreading locking throughput spinlock

我编写了一个简单的程序来测试CLH锁的吞吐量。 我的代码就像“多核编程艺术”一书中所描述的那样。接下来,我在一个不断变化的线程数上运行一个计数器10秒钟,并将counter / 10.0定义为吞吐量。

我的问题是我得到的结果是否在逻辑范围内,以及它们的原因可能是什么。我问,因为CLH锁的吞吐量下降非常快。 这些是cLH锁定的结果,其中左边指定线程计数,右边是吞吐量(计数器得到的大小,每个线程在CLH锁保护的临界区域中递增一次,除以10)。 / p>

CLH 1 2.89563825E7 2 1.33501436E7 4 5675832.3 8 15868.9 16 11114.4 32 68.4

正如你所看到的那种下降是疯狂的,让我觉得我可能搞砸了别的东西。

这是我的CLH锁代码(就像上面提到的那本书一样):

static class CLHLock implements Lock {
    AtomicReference<QNode> tail;
    ThreadLocal<QNode> myNode, myPred;

    public CLHLock() {
        tail = new AtomicReference<QNode>(new QNode());

        this.myNode = new ThreadLocal<QNode>() {
            protected QNode initialValue() {
                return new QNode();
            }
        };

        this.myPred = new ThreadLocal<QNode>() {
            protected QNode initialValue() {
                return null;
            }
        };
    }

    public void lock() {
        QNode qnode = this.myNode.get();
        qnode.locked.set(true);        

        QNode pred = this.tail.getAndSet(qnode);
        myPred.set(pred);           
        while (pred.locked.get()) {}      
    }

    public void unlock() {
        QNode qnode = this.myNode.get(); 
        qnode.locked.set(false);       
        this.myNode.set(this.myPred.get());   
    }

    static class QNode {  
        public AtomicBoolean locked = new AtomicBoolean(false);
    }
}

运行包括主线程等待10秒,而其他线程尝试锁定,递增,然后解锁,直到挥发性布尔告诉他们时间已到。

1 个答案:

答案 0 :(得分:1)

关于您的CLH锁定实施

除繁忙旋转外,实现看起来相当标准。你可能最好放弃或停车(虽然这需要更多的代码)。

关于您的基准测试结果

从性能测试中判断某些代码的正确性是一项任务,至少需要从正确性测试中判断某些代码的正确性所需的知识。

您可能正在观察与您的代码无直接关系的众多副作用。为了最大限度地减少这些影响,请使用像JMH这样的基准测试工具,否则您正在测量的东西不一定是您的代码。

这是关于您的结果的推测性解释,可能不正确,但完全合情合理:

  • 使用1个线程,您的代码执行速度非常快,因为锁定上几乎没有争用,并且没有缓存抖动。您可能会受益于成功的分支预测以及早期的JIT踢,而不会在以后进行去优化。
  • 使用2个和4个线程,吞吐量会有所下降。它还不错,因为你仍然拥有硬件线程,但现在你遇到了一些缓存抖动(甚至可能是虚假共享),一些一致性流量,以及一些分支错误预测(由于你的基准测试的共享基础架构)。虽然并行执行不会增加吞吐量,但您仍然可以。
  • 使用8和16个线程,您现在已超出计算机上可用硬件线程的限制。您开始体验操作系统调度效果,更重要的缓存抖动以及代码中的重大争用。
  • 使用32个线程,您可以超越某些快速硬件缓存机制(L1缓存,TLB)的限制,并降级到下一个最快的机制。没有必要超过缓存大小限制来体验这一点,您也可以超越关联性限制。