многопоточность — c ++: спин-блокировка или мьютекс-сравнение (простые вычисления)

Спин-блокировка должна иметь лучшую производительность, чем мьютекс для простых задач. Однако в этом простом тесте (8 потоков увеличивают счетчик) результаты отображаются по-разному:

#include <iostream>
#include <thread>
#include <mutex>
#include <atomic>
#include <vector>

using namespace std;

class SpinLock {
private:
atomic_flag lck = ATOMIC_FLAG_INIT;
public:
void lock() { while(lck.test_and_set(memory_order_acquire)) {} }
void unlock() { lck.clear(memory_order_release); }
};

int total = 0;

#ifdef SPINLOCK
SpinLock my_lock;
#else
mutex my_lock;
#endif

void foo(int n)
{
for(int i = 0; i < 10000000; ++i) {
#ifdef SPINLOCK
lock_guard<SpinLock> lck(my_lock);
#else
lock_guard<mutex> lck(my_lock);
#endif
++total;
}
}

int main()
{
vector<thread> v;

for(int i = 0; i < 8; ++i)
v.emplace_back(foo, i);

for(auto& t : v)
t.join();

cout << "total: " << total << endl;
return 0;
}

Чтобы проверить спин-блокировку:

$ g++ -DSPINLOCK -std=c++11 -Wall -pthread test.cc
$ time ./a.out
total: 80000000
real    0m18.206s
user    2m17.792s
sys     0m0.003s

Чтобы проверить мьютекс:

$ g++ -std=c++11 -Wall -pthread test.cc
$ time ./a.out
total: 80000000
real    0m9.483s
user    0m6.451s
sys     1m6.043s

Результаты показывают, что мьютекс почти в два раза быстрее, чем спин-блокировка. Вращающаяся блокировка проводит большую часть времени в «пользовательском процессоре», а мьютекс проводит больше всего времени в «системном процессоре». Как реализован мьютекс и нужно ли использовать мьютекс вместо спиновой блокировки в простых вычислениях, подобных этому? Кто-нибудь может объяснить результаты?

G ++ — 4.8.2, а ОС — Red Hat Enterprise Linux 7.

Благодарю.

0

Решение

некоторые заметки:

  1. время показано в time вывод утилиты — это время процессора, которое используют ваши потоки, а не фактическое время. Spinlock использует процессор даже во время ожидания, в то время как мьютексы ядра будут выполнять другие потоки в других процессах во время ожидания, не выставляя счет вашему процессору за это время процессора, за исключением того, которое используется для фактического планирования (то, которое вы видите в строке sys в случае мьютекса) ).

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

  3. Многопоточность может быть хорошим выбором, если у вас низкий шанс столкновения, то есть время, которое поток проводит в синхронизированной нагрузке, мало по сравнению с нагрузкой, которую он может выполнять асинхронно. Если вся ваша нагрузка защищена мьютексом, то использование потоков вообще накладно, и вам, вероятно, следует сериализовать его.

Спинлок удобен, если у вас низкий шанс столкновения и малое время ожидания в случае столкновения. В вашем случае 8 потоков сталкиваются с одним и тем же ресурсом, а затем требуют, чтобы ресурс был доступен сразу же после его освобождения. Это означает, что в среднем будет работать 1 поток и 7 других спин-блокировок, в результате чего общее время процессора будет использовано в 8 раз больше времени, необходимого для одного потока (если у вас 8-ядерный компьютер и на нем нет другой нагрузки). В случае мьютекса поток приостанавливается и просыпается только тогда, когда ресурс доступен, так что нет никаких накладных расходов на ожидание, однако блокировка мьютекса потребует некоторых накладных расходов, так как работа в ядре отслеживает, какие процессы ожидают мьютекс и даже если он не слишком велик, вы выполняете 160 миллионов операций мьютекса, что составляет sys время, затраченное на ваш процесс

2

Другие решения


По вопросам рекламы [email protected]