如何修复 macOS pthread 互斥锁性能低下的问题?
How to fix slow performance of macOS pthread mutex locks?
我有一个函数可以修改我的多线程程序中的共享资源。此函数是线程接触共享资源的唯一地方,它只占每个线程整体工作的一小部分。
static int64_t
AddToSharedResource(volatile int64_t* value, int64_t to_add)
{
int64_t result = *value;
*value += to_add;
return result;
}
我想让我的应用程序线程安全,所以我在指令之间添加了一个简单的互斥锁。
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static int64_t
AddToSharedResource(volatile int64_t* value, int64_t to_add)
{
pthread_mutex_lock(&lock);
int64_t result = *value;
*value += to_add;
pthread_mutex_unlock(&lock);
return result;
}
这样做会使我的程序慢 10 倍以上,甚至比 单线程版本!
仔细阅读后,似乎是因为使用 "fair" mutexes instead of using 的 macOS 实现,并且实现之间存在一定的权衡,但这种情况是执行的情况之一很糟糕。然而,我以这种方式编写代码的原因是我已经在 Win32 中编写了程序(锁几乎不会造成任何性能损失),并且我打算将函数移植到 Linux 作为嗯
有没有办法让这个函数在 macOS 中线程安全而不造成巨大的瓶颈,或者我是否需要重新设计平台层?
您的示例与 std::atomic::fetch_add
完全匹配。
原子操作应该比 lock-modify-unlock 舞蹈便宜得多,并且具有允许指定精确 memory ordering semantics.
的额外好处
似乎 std::atomic::fetch_add
(正如@Botje 所建议的那样)是在我的体系结构上使用 lock
指令编译的,同时在互斥体 lock/unlock 中围绕关键部分需要两次实际调用深入到内核(这似乎可以解释性能差异)。
我对如何使用 MacOS 的 API 而不是 C++ 的标准库生成相同的指令感兴趣(只是为了好玩)。扫描他们的文档后,我看到了 header libkern/OSAtomic.h
,它定义了原子操作的函数。
#include <libkern/OSAtomic.h>
static int64_t
AddToSharedResource(volatile int64_t* value, int64_t to_add)
{
int64_t result = OSAtomicAdd64(to_add, value);
return result - to_add; // As we want the previous value.
}
但是,这会产生弃用警告,并建议改用 C++ 的标准库。
'OSAtomicAdd64' is deprecated: deprecated in macOS 10.12 - Use
std::atomic_fetch_add_explicit(std::memory_order_relaxed)
from <atomic> instead.
看来连 MacOS 都希望我们使用 C++ 标准库。 Windows,另一方面,还没有弃用其对应的 InterlockedAdd64
。
我有一个函数可以修改我的多线程程序中的共享资源。此函数是线程接触共享资源的唯一地方,它只占每个线程整体工作的一小部分。
static int64_t
AddToSharedResource(volatile int64_t* value, int64_t to_add)
{
int64_t result = *value;
*value += to_add;
return result;
}
我想让我的应用程序线程安全,所以我在指令之间添加了一个简单的互斥锁。
static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
static int64_t
AddToSharedResource(volatile int64_t* value, int64_t to_add)
{
pthread_mutex_lock(&lock);
int64_t result = *value;
*value += to_add;
pthread_mutex_unlock(&lock);
return result;
}
这样做会使我的程序慢 10 倍以上,甚至比 单线程版本!
仔细阅读后,似乎是因为使用 "fair" mutexes instead of using
有没有办法让这个函数在 macOS 中线程安全而不造成巨大的瓶颈,或者我是否需要重新设计平台层?
您的示例与 std::atomic::fetch_add
完全匹配。
原子操作应该比 lock-modify-unlock 舞蹈便宜得多,并且具有允许指定精确 memory ordering semantics.
似乎 std::atomic::fetch_add
(正如@Botje 所建议的那样)是在我的体系结构上使用 lock
指令编译的,同时在互斥体 lock/unlock 中围绕关键部分需要两次实际调用深入到内核(这似乎可以解释性能差异)。
我对如何使用 MacOS 的 API 而不是 C++ 的标准库生成相同的指令感兴趣(只是为了好玩)。扫描他们的文档后,我看到了 header libkern/OSAtomic.h
,它定义了原子操作的函数。
#include <libkern/OSAtomic.h>
static int64_t
AddToSharedResource(volatile int64_t* value, int64_t to_add)
{
int64_t result = OSAtomicAdd64(to_add, value);
return result - to_add; // As we want the previous value.
}
但是,这会产生弃用警告,并建议改用 C++ 的标准库。
'OSAtomicAdd64' is deprecated: deprecated in macOS 10.12 - Use
std::atomic_fetch_add_explicit(std::memory_order_relaxed)
from <atomic> instead.
看来连 MacOS 都希望我们使用 C++ 标准库。 Windows,另一方面,还没有弃用其对应的 InterlockedAdd64
。