2013-02-26 2 views
0

У меня странное поведение с std :: map (или std :: set, похоже, они ведут себя одинаково в этом сценарии). Возможно, у меня есть серьезное недоразумение о том, как это должно работать. Я использую VS2010 SP1.std :: map странный конфликт ресурсов в многопоточности

Возьмем, к примеру эта функция:

extern time_t g_nElapsed; 
UINT Thread(LPVOID _param) 
{ 
    UINT nRuns = (UINT)_param; 

    for(UINT i=0; i<nRuns; ++i) 
    { 
     time_t _1 = time(NULL); 
     std::set<UINT> cRandomSet; 
     cRandomSet.insert(1); 
     cRandomSet.insert(2); 
     cRandomSet.insert(3); 
     cRandomSet.insert(4); 
     g_nElapsed += (time(NULL) - _1); 
    } 


    return 0; 
} 

Теперь, если я бегу 8 потоков с 100000 итераций каждый, g_nElapsed будет примерно 40 секунд. Если я запустил 1 нить с 800 000 итераций, g_nElapsed будет около 5 секунд. У меня создается впечатление, что g_nElapsed должен быть примерно одинаковым для любого разумного количества потоков. Так сказать, использование процессора увеличивается с количеством потоков, хотя работа остается неизменной. Однако, похоже, что какое-то соперничество ресурсов с множеством приводит к увеличению времени выполнения. Но почему? Это нить локальная ...

Я уверен, что это простое заблуждение и простое исправление, но Я не совсем уверен, в чем проблема.

Следующий код не демонстрирует такое поведение:

extern time_t g_nElapsed; 
UINT Thread(LPVOID _param) 
{ 
    UINT nRuns = (UINT)_param; 

    for(UINT i=0; i<nRuns; ++i) 
    { 
     time_t _1 = time(NULL); 
     UINT n[4]; 
    n[0] = 1; 
     n[1] = 1; 
     n[2] = 1; 
     n[3] = 1; 
     g_nElapsed += (time(NULL) - _1); 
    } 


    return 0; 
} 
+1

Его блокиратор блокировки при создании/уничтожении объекта 'cRandomSet'. Кроме того, каждый 'cRandomSet.insert' может вызывать ** новый **, поэтому его блокировка тоже. – PSIAlt

+0

(не так) красивая сторона распределения памяти ... –

ответ

3

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

Вы можете попробовать другой распределитель, такой как tcmalloc (http://goog-perftools.sourceforge.net/doc/tcmalloc.html). Он специально разработан для решения этой проблемы.

Другим подходом было бы использование пула объектов или другой стратегии распределения, чтобы полностью не использовать стандартный механизм распределения. Для этого потребуются некоторые изменения кода, тогда как использование tcmalloc этого не делает.

+1

Реализация пула объектов распределителя потребует очень мало изменений кода, за исключением написания распределителя. –

+1

Уверен, но очень немногих намного больше, чем нет! Я не говорю, что не использую пулы объектов или что-то еще, они тоже хорошая идея. –

+0

Вы правы. Реализация второго варианта с UINT * nnn = new UINT [5] и удаление результатов в том же поведении, что и карты и векторы. Я не могу поверить, что я никогда не рассматривал новый/delete для рассматриваемого ресурса здесь. Я проверю библиотеку tcmalloc. – namezero

Смежные вопросы