Работа с кучей памяти - задача для ОС. Ничто не гарантирует, что вы сможете выполнять работу лучше / быстрее, чем ОС.
Но есть некоторые условия, в которых вы можете получить некоторое улучшение, особенно когда вы знаете что-то об использовании вашей памяти, которое неизвестно ОС.
Я пишу здесь свою непроверенную идею, надеюсь, вы получите от нее некоторую прибыль.
Допустим, у вас есть T потоков, все они резервируют и освобождают память. Основная цель - скорость, поэтому я постараюсь не использовать ни TLS, ни критические блокировки, ни атомарные операции.
Если (повторите: если, если, если), приложение может соответствовать нескольким разным размерам блоков памяти (не случайным размерам, чтобы избежать фрагментации и ненужных дыр), тогда начните запрашивать у ОС количество таких дискретных блоков .
Например, у вас есть массив n1
блоков каждого размера size1
, массив n2
блоков каждого размера size2
, массив n3
... и так далее. Каждый массив является двумерным, второе поле просто хранит флаг для используемого / свободного блока. Если ваши массивы очень велики, то лучше использовать выделенный массив для флагов (поскольку непрерывное использование памяти всегда быстрее).
Теперь кто-то просит блок памяти размером sB
. Специализированная функция (или объект или что-то еще) ищет массив блоков размером больше или равным sB
, а затем выбирает блок, просматривая флаг used / free. Непосредственно перед завершением этой задачи правильный флаг блока устанавливается на «используется».
Когда два или более потоков запрашивают блоки одинакового размера, флаг может быть поврежден. Использование TLS решит эту проблему, а также критическую блокировку. Я думаю, вы можете установить флаг bool в начале поиска в flags-array, который заставит другие потоки ждать, пока флаг не изменится, что происходит только после изменения флага блока. С псевдокодом:
MemoryGetter(sB)
{
//select which array depending of 'sB'
for (i=0, i < numOfarrays, i++)
if (sizeOfArr(i) >= sB)
arrMatch = i
break //exit for
//wait if other thread wants a block from the same arrMatch array
while ( searching(arrMatch) == true )
; //wait
//blocks other threads wanting a block from the same arrMatch array
searching(arrMatch) = true
//Get the first free block
for (i=0, i < numOfBlocks, i++)
if ( arrOfUsed(arrMatch, i) != true )
selectedBlock = addressOf(....)
//mark the block as used
arrOfUsed(arrMatch, i) = true
break; //exit for
//Allow other threads
searching(arrMatch) = false
return selectedBlock //NOTE: selectedBlock==NULL means no free block
}
Освободить блок проще, просто отметьте его как свободный, без проблем с параллелизмом потоков.
Работа без свободных блоков зависит от вас (подождите, используйте больший блок, попросите у ОС больше и т. Д.).
Обратите внимание, что вся память резервируется от ОС при запуске приложения, что может быть проблемой.
Если эта идея сделает ваше приложение быстрее, дайте мне знать. Что я могу сказать наверняка, так это то, что используемая память больше, чем если бы вы использовали обычный запрос ОС; но не очень, если вы выбираете «хорошие» размеры, те, которые наиболее часто используются.
Некоторые улучшения могут быть сделаны:
- Кэшировать последний освобожденный блок (по размеру), чтобы избежать поиска.
- Начните с небольшого количества блоков и попросите у ОС только больше памяти
при необходимости. Играть с «количеством блоков» для каждого размера в зависимости от
ваше приложение. Найдите оптимальный случай.