Портирование потоков на окна. Критические разделы очень медленные

Я портирую некоторый код на окна и обнаружил, что многопоточность очень медленная. Задача занимает 300 секунд на Windows (с двумя xeon E5-2670 с 8 ядрами и 2,6 ГГц = 16 ядер) и 3,5 секунды на Linux (xeon E5-1607 с 4 ядрами и 3 ГГц). Использование vs2012 express.

У меня 32 потока, каждый из которых вызывает EnterCriticalSection (), извлекает 80-байтную работу std :: stack, LeaveCriticalSection и выполняет некоторую работу (всего 250 тыс. Заданий).

До и после каждого вызова критической секции я печатаю идентификатор потока и текущее время.

  • Время ожидания блокировки одного потока составляет ~ 160 мс
  • Для удаления задания из стека требуется ~ 3 мс
  • Вызов отпуска занимает ~ 3 мс
  • Работа занимает ~ 1 мс

(примерно то же самое для Debug / Release, Debug занимает немного больше времени. Я хотел бы иметь возможность правильно профилировать код: P)

Комментирование вызова задания заставляет весь процесс занимать 2 секунды (все же больше, чем в linux).

Я пробовал и queryperformancecounter, и timeGetTime, оба дают примерно одинаковый результат.

AFAIK задание никогда не делает синхронизирующих вызовов, но я не могу объяснить замедление, если оно не делает.

Я понятия не имею, почему копирование из стека и вызов pop занимает так много времени.
Еще одна очень запутанная вещь — почему вызов метода exit () занимает так много времени.

Кто-нибудь может порассуждать о том, почему он работает так медленно?

Я бы не подумал, что разница в процессоре даст 100-кратную разницу в производительности, но может ли это быть вообще связано с двумя процессорами? (необходимость синхронизации между отдельными процессорами, чем внутренними ядрами).

Кстати, я знаю о std :: thread, но хочу, чтобы мой библиотечный код работал до C ++ 11.

редактировать

//in a while(hasJobs) loop...

EVENT qwe1 = {"lock", timeGetTime(), id};
events.push_back(qwe1);

scene->jobMutex.lock();

EVENT qwe2 = {"getjob", timeGetTime(), id};
events.push_back(qwe2);

hasJobs = !scene->jobs.empty();
if (hasJobs)
{
job = scene->jobs.front();
scene->jobs.pop();
}

EVENT qwe3 = {"gotjob", timeGetTime(), id};
events.push_back(qwe3);

scene->jobMutex.unlock();

EVENT qwe4 = {"unlock", timeGetTime(), id};
events.push_back(qwe4);

if (hasJobs)
scene->performJob(job);

и класс мьютекса, с удаленным материалом linux #ifdef …

CRITICAL_SECTION mutex;

...

Mutex::Mutex()
{
InitializeCriticalSection(&mutex);
}
Mutex::~Mutex()
{
DeleteCriticalSection(&mutex);
}
void Mutex::lock()
{
EnterCriticalSection(&mutex);
}
void Mutex::unlock()
{
LeaveCriticalSection(&mutex);
}

5

Решение

CRITICAL_SECTION окна вращается в тесной петле, когда вы впервые вводите его. Он не приостанавливает поток, вызвавший EnterCriticalSection, если в цикле вращения не прошел существенный период. Таким образом, наличие 32 потоков, конкурирующих за одну и ту же критическую секцию, сожжет и потратит много циклов ЦП. Вместо этого попробуйте мьютекс (см. CreateMutex).

1

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

Кажется, что ваши темы Windows сталкиваются с супер конкуренции. Они кажутся полностью сериализованными. У вас есть около 7 мс общего времени обработки в критическом разделе и 32 потока. Если все потоки поставлены в очередь на блокировке, последний поток в очереди не сможет работать до тех пор, пока он не проработает около 217 мс. Это не слишком далеко от наблюдаемого вами времени ожидания 160 мс.

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

Попытайтесь охарактеризовать поведение профилирования Linux и посмотрите, действительно ли поведение программы и сравнение яблок с яблоками.

0

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector