Как исправить медленную работу блокировок мьютекса macOS pthread? - PullRequest
2 голосов
/ 11 июля 2019

У меня есть функция, которая изменяет общий ресурс в моей многопоточной программе. Эта функция - единственное место, где потоки касаются общего ресурса, и это лишь небольшая часть общей работы каждого потока.

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 раз медленнее, делая ее даже медленнее, чем однопоточная версия !

После прочтения немного больше, похоже, из-за реализации macOS, которая использует "честные" мьютексы вместо использования spinlocks , и что есть определенные компромиссы между реализациями, но этот случай является одним из случаев, которые работают плохо. Однако причина, по которой я написал код таким образом, заключается в том, что я уже написал программу на Win32 (где блокировка практически не повлияла на производительность), и я планирую также перенести функцию на Linux.

Есть ли способ сделать эту функцию поточно-ориентированной в macOS без создания огромного узкого места, или мне нужно изменить дизайн уровня платформы?

Ответы [ 2 ]

3 голосов
/ 11 июля 2019

Ваш пример точно соответствует std::atomic::fetch_add.Атомарные операции должны быть намного дешевле, чем танцевать «блокируй, модифицируй, разблокируй», и имеют дополнительное преимущество, позволяя указать точную семантику упорядочения памяти .

1 голос
/ 20 июля 2019

Похоже, что std::atomic::fetch_add (как предложил @Botje) скомпилирован с использованием инструкции lock на моей архитектуре, в то время как критическая секция в блокировке / разблокировке мьютекса требует двух фактических обращений к ядру (что, кажется, объясните разницу в производительности).

Меня интересовало, как генерировать те же инструкции, используя API-интерфейс MacOS, а не стандартную библиотеку C ++ (просто для удовольствия). После сканирования их документации я подошел к заголовку 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...