мне кажется, что если ваше интерференционное приложение использовало new / delete (malloc / free), тогда интерференционное приложение больше мешало бы тесту без повторного использования.Но я не знаю, как реализован твой тест помех.
В зависимости от того, как вы перерабатываете (т. Е. Если вы используете мьютексы pthread, не дай бог), ваш код рециркуляции может быть медленным (gcc atomic ops будет в 40 раз быстрее при реализации рецикла).
Malloc, в некоторых вариациях в течение длительного времени, по крайней мере, на некоторых платформах, знал о потоках.Используйте ключи компилятора на gcc, чтобы убедиться, что вы его получили.Более новые алгоритмы поддерживают пулы небольших порций памяти для потока каждого потока , поэтому блокировка отсутствует или незначительна, если в вашем потоке имеется маленький элемент.Я упростил это, и это зависит от того, какой malloc использует ваша система.Плюс, если вы идете и выделяете миллионы предметов для проведения теста ... ну, тогда вы не увидите этого эффекта, потому что небольшие пулы предметов ограничены по размеру.Или, может быть, вы будете.Я не знаю.Если вы освободили элемент сразу после размещения, вы с большей вероятностью увидите его.Освобожденные мелкие элементы возвращаются в списки мелких предметов, а не в общую кучу.Хотя «что происходит, когда поток B освобождает элемент, выделенный потоком A», - это проблема, которая может или не может быть решена в вашей версии malloc и не может быть решена неблокирующим образом.Несомненно, если вы не освободили сразу во время большого теста, то поток должен был бы пополнить свой список небольших элементов много раз.Это может заблокировать, если более одного потока пытается.Наконец, в какой-то момент куча вашего процесса запросит у системы кучную память, которая, очевидно, может блокироваться.
Так вы используете небольшие элементы памяти?Что касается вашего malloc, я не знаю, что будет маленьким, но если вы <1k, это точно маленький.Вы выделяете и освобождаете один за другим или выделяете тысячи узлов, а затем освобождаете тысячи узлов?Ваше приложение помех вмешивалось?Все это повлияет на результаты.</p>
Как перерабатывать с атомарными операциями (CAS = сравнить и поменять местами):
Сначала добавьте pNextFreeNode к вашему объекту узла.Я использовал void *, вы можете использовать свой тип.Этот код предназначен для 32-битных указателей, но работает и для 64-битных.Затем создайте глобальную кучу для переработки.
void *_pRecycleHead; // global head of recycle list.
Добавьте для переработки кучу:
void *Old;
while (1) { // concurrency loop
Old = _pRecycleHead; // copy the state of the world. We operate on the copy
pFreedNode->pNextFreeNode = Old; // chain the new node to the current head of recycled items
if (CAS(&_pRecycleHead, Old, pFreedNode)) // switch head of recycled items to new node
break; // success
}
удалить из кучи:
void *Old;
while (Old = _pRecycleHead) { // concurrency loop, only look for recycled items if the head aint null
if (CAS(&_pRecycleHead, Old, Old->pNextFreeNode)) // switch head to head->next.
break; // success
}
pNodeYoucanUseNow = Old;
Использование CAS означает, что операция будет успешнойтолько если элемент, который вы меняете, - это старое значение, которое вы передаете. Если есть гонка и другой поток, попавший туда первым, то старое значение будет другим.В реальной жизни эта гонка случается очень и очень редко.CAS лишь немного медленнее, чем на самом деле установка значения, по сравнению с мьютексами ... он качается.
При удалении из кучи выше есть условие гонки, если вы быстро добавляете и удаляете тот же элемент.Мы решаем это путем добавления версии # к данным CAS'able.Если вы сделаете версию # в то же время, что и указатель на верхнюю часть стопки, вы выиграете.Используйте союз.Для CAS 64 битов ничего не стоит.
union TRecycle {
struct {
int iVersion;
void *pRecycleHead;
} ; // we can set these. Note, i didn't name this struct. You may have to if you want ANSI
unsigned long long n64; // we cas this
}
Примечание. Вам придется перейти на 128-битную структуру для 64-битной ОС.так что глобальная куча рециркуляции теперь выглядит следующим образом:
TRecycle _RecycleHead;
Добавить в колоду рециркуляции:
while (1) { // concurrency loop
TRecycle New,Old;
Old.n64 = _RecycleHead.n64; // copy state
New.n64 = Old.n64; // new state starts as a copy
pFreedNode->pNextFreeNode = Old.pRecycleHead; // link item to be recycled into recycle pile
New.pRecycleHead = pFreedNode; // make the new state
New.iVersion++; // adding item to list increments the version.
if (CAS(&_RecycleHead.n64, Old.n64, New.n64)) // now if version changed...we fail
break; // success
}
удалить из колоды:
while (1) { // concurrency loop
TRecycle New,Old;
Old.n64 = _RecycleHead.n64; // copy state
New.n64 = Old.n64; // new state starts as a copy
New.pRecycleHead = New.pRecycledHead.pNextFreeNode; // new will skip over first item in recycle list so we can have that item.
New.iVersion++; // taking an item off the list increments the version.
if (CAS(&_RecycleHead.n64, Old.n64, New.n64)) // we fail if version is different.
break; // success
}
pNodeYouCanUseNow = Old.pRecycledHead;
Держу пари, если вырециркулируйте таким образом, вы увидите увеличение производительности.