gpt4 book ai didi

c++ - 当我使用非 cst 内存模型时,为什么我的自旋锁实现性能最差?

转载 作者:塔克拉玛干 更新时间:2023-11-02 23:31:13 26 4
gpt4 key购买 nike

下面有两个版本的自旋锁。第一个使用默认值 memory_order_cst,而后者使用 memory_order_acquire/memory_order_release。由于后者更轻松,我希望它有更好的表现。然而,情况似乎并非如此。

class SimpleSpinLock
{
public:

inline SimpleSpinLock(): mFlag(ATOMIC_FLAG_INIT) {}

inline void lock()
{
int backoff = 0;
while (mFlag.test_and_set()) { DoWaitBackoff(backoff); }
}

inline void unlock()
{
mFlag.clear();
}

private:

std::atomic_flag mFlag = ATOMIC_FLAG_INIT;
};

class SimpleSpinLock2
{
public:

inline SimpleSpinLock2(): mFlag(ATOMIC_FLAG_INIT) {}

inline void lock()
{
int backoff = 0;
while (mFlag.test_and_set(std::memory_order_acquire)) { DoWaitBackoff(backoff); }
}

inline void unlock()
{
mFlag.clear(std::memory_order_release);
}

private:

std::atomic_flag mFlag = ATOMIC_FLAG_INIT;
};

const int NUM_THREADS = 8;
const int NUM_ITERS = 5000000;

const int EXPECTED_VAL = NUM_THREADS * NUM_ITERS;

int val = 0;
long j = 0;

SimpleSpinLock spinLock;

void ThreadBody()
{
for (int i = 0; i < NUM_ITERS; ++i)
{
spinLock.lock();

++val;

j = i * 3.5 + val;

spinLock.unlock();
}
}

int main()
{
vector<thread> threads;

for (int i = 0; i < NUM_THREADS; ++i)
{
cout << "Creating thread " << i << endl;
threads.push_back(std::move(std::thread(ThreadBody)));
}

for (thread& thr: threads)
{
thr.join();
}

cout << "Final value: " << val << "\t" << j << endl;
assert(val == EXPECTED_VAL);

return 1;
}

我在 Ubuntu 12.04 上运行,gcc 4.8.2 运行优化 O3。

-- 带有 memory_order_cst 的自旋锁:

Run 1:
real 0m1.588s
user 0m4.548s
sys 0m0.052s

Run 2:
real 0m1.577s
user 0m4.580s
sys 0m0.032s

Run 3:
real 0m1.560s
user 0m4.436s
sys 0m0.032s

-- 带有 memory_order_acquire/release 的自旋锁:

Run 1:

real 0m1.797s
user 0m4.608s
sys 0m0.100s

Run 2:

real 0m1.853s
user 0m4.692s
sys 0m0.164s

Run 3:
real 0m1.784s
user 0m4.552s
sys 0m0.124s

Run 4:
real 0m1.475s
user 0m3.596s
sys 0m0.120s

有了更宽松的模型,我看到了更多的可变性。有时会更好。通常情况更糟,有人对此有解释吗?

最佳答案

生成的解锁码不同。 CST 内存模型(使用 g++ 4.9.0)生成:

    movb    %sil, spinLock(%rip)
mfence

解锁。获取/释放生成:

    movb    %sil, spinLock(%rip)

锁码是一样的。其他人可能会说为什么 fence 更好,但如果我不得不猜测,我猜它会减少总线/高速缓存一致性争用,可能是通过减少总线上的干扰。有时越严格越有序,因此速度越快。

附录:根据 this , mfence 花费大约 100 个周期。所以也许你正在减少总线争用,因为当一个线程完成循环体时,它会在尝试重新获取锁之前暂停一下,让另一个线程完成。您可以尝试通过在解锁后放入一个短暂的延迟循环来做同样的事情,但您必须确保它没有得到优化。

附录 2:这似乎是由循环太快引起的总线干扰/争用引起的。我添加了一个短延迟循环,例如:

    spinLock.unlock();
for (int i = 0; i < 5; i++) {
j = i * 3.5 + val;
}

现在,获取/释放执行相同的操作。

关于c++ - 当我使用非 cst 内存模型时,为什么我的自旋锁实现性能最差?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23522034/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com