Измерение эффективности ожидания Mutex и Busy

Программа предназначена для создания нескольких streamов, где каждый stream увеличивает общую переменную на 10000, используя цикл for, который увеличивает его на 1 на каждой итерации. Требуются как блокировка блокировки мьютекса, так и блокировка блокировки (ожидание). Согласно тому, что я узнал, версия для мьютексов должна работать быстрее, чем прямая блокировка. Но то, что я реализовал, дало мне противоположный ответ …

Это реализация каждого streamа в версии mutex:

void *incr(void *tid) { int i; for(i = 0; i < 10000; i++) { pthread_mutex_lock(&the_mutex); //Grab the lock sharedVar++; //Increment the shared variable pthread_mutex_unlock(&the_mutex); //Release the lock } pthread_exit(0); } 

И это реализация в версии блокировки спина:

 void *incr(void *tid) { int i; for(i = 0; i < 10000; i++) { enter_region((int)tid); //Grab the lock sharedVar++; //Increment the shared variable leave_region((int)tid); //Release the lock } pthread_exit(0); } void enter_region(int tid) { interested[tid] = true; //Show this thread is interested turn = tid; //Set flag while(turn == tid && other_interested(tid)); //Busy waiting } bool other_interested(int tid) //interested[] is initialized to all false { int i; for(i = 0; i < tNumber; i++) if(i != tid) if(interested[i] == true) //There are other threads that are interested return true; return false; } void leave_region(int tid) { interested[tid] = false; //Depart from critical region } 

Я также повторил процесс создания и запуска streamов в сотни раз, чтобы удостовериться, что время выполнения можно отличить. Например, если tNumber равно 4, и я повторил программу в 1000 раз, мьютекс займет 2,22 секунды, а блокировка блокировки займет 1,35 секунды. Разница возрастает с увеличением числа. Почему это происходит? Является ли мой код неправильным?

Код между enter_region и leave_region не защищен.

Вы можете доказать это, сделав его более сложным, чтобы убедиться, что он подойдет.

Создайте массив bools (check) длиной 10000, настройте false. Сделайте код между входом и выходом:

 if (check[sharedVar]) cout << "ERROR" << endl; else check[sharedVar++] = true; 

«Разница» в скорости заключается в том, что вы используете синхронизацию, используя

 interested[tid] = true; //Show this thread is interested turn = tid; //Set flag while(turn == tid && other_interested(tid)); 

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

Это нужно делать атомарно, реализуя либо compare-and-swap либо compare-and-swap или test-and-set . Эти инструкции обычно предоставляются аппаратным обеспечением.

Например, на x86 у вас есть xchg, cmpxchg/cmpxchg8b, xadd
Ваш тест можно переписать как

 while( compare_and_swap_atomic(myid,id_meaning_it_is_free) == false); 

Проблема в том, что атомарность дорогая .