Ответ 1
мне кажется, если ваше приложение вмешательства использовало новый /delete (malloc/free), тогда приложение с помехами больше помешало бы тестированию без повторного использования. Но я не знаю, как выполняется ваш тест на интерференцию.
В зависимости от того, как вы перерабатываете (т.е. если вы запретите использование муфтексов pthread), ваш код рециркуляции может быть медленным (gcc atom ops будет быстрее на 40 раз при реализации утилизации).
Malloc, в некоторых вариантах в течение длительного времени, по крайней мере на некоторых платформах, знал о потоках. Используйте ключи компилятора на gcc, чтобы убедиться, что вы их получите. Новые алгоритмы поддерживают пулы небольших фрагментов памяти для каждого потока, поэтому нет или мало блокировки, если в вашем потоке имеется небольшой элемент. Я упростил это, и это зависит от того, какой malloc использует ваша система. Кроме того, если вы идете и выделяете миллионы предметов для проведения теста... ну тогда вы не увидите этого эффекта, потому что небольшие пулы элементов ограничены по размеру. Или, может быть, вы это сделаете. Я не знаю. Если вы освободили элемент сразу после выделения, вы, скорее всего, его увидите. Освобожденные мелкие предметы возвращаются в списки мелких предметов, а не в общую кучу. Хотя "то, что происходит, когда поток B освобождает элемент, выделенный потоком A", является проблемой, которая может быть или не быть рассмотрена в вашей версии malloc и не может рассматриваться без блокировки. Конечно, если вы не сразу освобождались во время большого теста, тогда поток должен был бы пополнять свой маленький список предметов много раз. Это может блокироваться, если пытается попробовать несколько потоков. Наконец, в какой-то момент ваша куча процесса будет запрашивать систему памяти кучи, которая, очевидно, может блокироваться.
Так вы используете небольшие элементы памяти? Для вашего malloc я не знаю, что бы это было мало, но если вы - 1k, что точно мало. Вы выделяете и освобождаете один за другим или выделяете тысячи узлов, а затем освобождаете тысячи узлов? Было ли ваше приложение для размещения приложений? Все это повлияет на результаты.
Как перерабатывать с атомарными ops (CAS = сравнивать и свопировать):
Сначала добавьте pNextFreeNode к вашему объекту node. Я использовал 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. Если вы делаете версию # одновременно с указателем на голову переработанной кучи, вы выигрываете. Используйте союз. Не стоит ничего лишнего на 64 бит CAS.
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;
Бьюсь об заклад, если вы переработаете этот путь, вы увидите увеличение уровня.