#ifndef __TBB_tbbmalloc_internal_H
#error tbbmalloc_internal.h must be included at this point
#endif
#ifndef __TBB_large_objects_H
#define __TBB_large_objects_H
enum CacheBinOperationType {
CBOP_INVALID = 0,
CBOP_GET,
CBOP_PUT_LIST,
CBOP_CLEAN_TO_THRESHOLD,
CBOP_CLEAN_ALL,
CBOP_UPDATE_USED_SIZE
};
enum CacheBinOperationStatus {
CBST_WAIT = 0,
CBST_NOWAIT,
CBST_DONE
};
template<size_t MIN_SIZE, size_t MAX_SIZE>
struct LargeBinStructureProps {
public:
static const size_t MinSize = MIN_SIZE, MaxSize = MAX_SIZE;
static const size_t CacheStep = 8 * 1024;
static const unsigned NumBins = (MaxSize - MinSize) / CacheStep;
static size_t alignToBin(size_t size) {
return alignUp(size, CacheStep);
}
static int sizeToIdx(size_t size) {
MALLOC_ASSERT(MinSize <= size && size < MaxSize, ASSERT_TEXT);
MALLOC_ASSERT(size % CacheStep == 0, ASSERT_TEXT);
return (size - MinSize) / CacheStep;
}
};
template<size_t MIN_SIZE, size_t MAX_SIZE>
struct HugeBinStructureProps {
private:
static const int MaxSizeExp = Log2<MAX_SIZE>::value;
static const int MinSizeExp = Log2<MIN_SIZE>::value;
static const int StepFactor = 8;
static const int StepFactorExp = Log2<StepFactor>::value;
public:
static const size_t MinSize = MIN_SIZE, MaxSize = MAX_SIZE;
static const unsigned NumBins = (MaxSizeExp - MinSizeExp) * StepFactor;
static size_t alignToBin(size_t size) {
size_t minorStepExp = BitScanRev(size) - StepFactorExp;
return alignUp(size, 1ULL << minorStepExp);
}
static int sizeToIdx(size_t size) {
MALLOC_ASSERT(MinSize <= size && size <= MaxSize, ASSERT_TEXT);
int sizeExp = (int)BitScanRev(size); size_t majorStepSize = 1ULL << sizeExp;
int minorStepExp = sizeExp - StepFactorExp;
int minorIdx = (size - majorStepSize) >> minorStepExp;
MALLOC_ASSERT(size == majorStepSize + ((size_t)minorIdx << minorStepExp),
"Size is not aligned on the bin");
return StepFactor * (sizeExp - MinSizeExp) + minorIdx;
}
};
template<typename StructureProps, int TOO_LARGE, int ON_MISS, int LONG_WAIT>
struct LargeObjectCacheProps : public StructureProps {
static const int TooLargeFactor = TOO_LARGE, OnMissFactor = ON_MISS, LongWaitFactor = LONG_WAIT;
};
template<typename Props>
class LargeObjectCacheImpl {
private:
class BinsSummary {
size_t usedSz;
size_t cachedSz;
public:
BinsSummary() : usedSz(0), cachedSz(0) {}
bool isLOCTooLarge() const { return cachedSz > Props::TooLargeFactor * usedSz; }
void update(size_t usedSize, size_t cachedSize) {
usedSz += usedSize;
cachedSz += cachedSize;
}
void reset() { usedSz = cachedSz = 0; }
};
public:
static const uint32_t numBins = Props::NumBins;
typedef BitMaskMax<numBins> BinBitMask;
class CacheBin {
private:
LargeMemoryBlock* first;
std::atomic<LargeMemoryBlock*> last;
std::atomic<uintptr_t> oldest;
uintptr_t lastCleanedAge;
std::atomic<intptr_t> ageThreshold;
std::atomic<size_t> usedSize;
std::atomic<size_t> cachedSize;
std::atomic<intptr_t> meanHitRange;
uintptr_t lastGet;
typename MallocAggregator<CacheBinOperation>::type aggregator;
void ExecuteOperation(CacheBinOperation *op, ExtMemoryPool *extMemPool, BinBitMask *bitMask, int idx, bool longLifeTime = true);
CacheBin();
public:
void init() {
memset(this, 0, sizeof(CacheBin));
}
void putList(ExtMemoryPool *extMemPool, LargeMemoryBlock *head, BinBitMask *bitMask, int idx);
LargeMemoryBlock *get(ExtMemoryPool *extMemPool, size_t size, BinBitMask *bitMask, int idx);
bool cleanToThreshold(ExtMemoryPool *extMemPool, BinBitMask *bitMask, uintptr_t currTime, int idx);
bool releaseAllToBackend(ExtMemoryPool *extMemPool, BinBitMask *bitMask, int idx);
void updateUsedSize(ExtMemoryPool *extMemPool, size_t size, BinBitMask *bitMask, int idx);
void decreaseThreshold() {
intptr_t threshold = ageThreshold.load(std::memory_order_relaxed);
if (threshold)
ageThreshold.store((threshold + meanHitRange.load(std::memory_order_relaxed)) / 2, std::memory_order_relaxed);
}
void updateBinsSummary(BinsSummary *binsSummary) const {
binsSummary->update(usedSize.load(std::memory_order_relaxed), cachedSize.load(std::memory_order_relaxed));
}
size_t getSize() const { return cachedSize.load(std::memory_order_relaxed); }
size_t getUsedSize() const { return usedSize.load(std::memory_order_relaxed); }
size_t reportStat(int num, FILE *f);
void forgetOutdatedState(uintptr_t currTime);
LargeMemoryBlock *putList(LargeMemoryBlock *head, LargeMemoryBlock *tail, BinBitMask *bitMask,
int idx, int num, size_t hugeObjectThreshold);
LargeMemoryBlock *get();
LargeMemoryBlock *cleanToThreshold(uintptr_t currTime, BinBitMask *bitMask, int idx);
LargeMemoryBlock *cleanAll(BinBitMask *bitMask, int idx);
void updateUsedSize(size_t size, BinBitMask *bitMask, int idx) {
if (!usedSize.load(std::memory_order_relaxed)) bitMask->set(idx, true);
usedSize.store(usedSize.load(std::memory_order_relaxed) + size, std::memory_order_relaxed);
if (!usedSize.load(std::memory_order_relaxed) && !first) bitMask->set(idx, false);
}
void updateMeanHitRange( intptr_t hitRange ) {
hitRange = hitRange >= 0 ? hitRange : 0;
intptr_t mean = meanHitRange.load(std::memory_order_relaxed);
mean = mean ? (mean + hitRange) / 2 : hitRange;
meanHitRange.store(mean, std::memory_order_relaxed);
}
void updateAgeThreshold( uintptr_t currTime ) {
if (lastCleanedAge)
ageThreshold.store(Props::OnMissFactor * (currTime - lastCleanedAge), std::memory_order_relaxed);
}
void updateCachedSize(size_t size) {
cachedSize.store(cachedSize.load(std::memory_order_relaxed) + size, std::memory_order_relaxed);
}
void setLastGet( uintptr_t newLastGet ) {
lastGet = newLastGet;
}
};
intptr_t hugeSizeThresholdIdx;
private:
std::atomic<intptr_t> tooLargeLOC;
BinBitMask bitMask;
CacheBin bin[numBins];
public:
static size_t alignToBin(size_t size) {
return Props::alignToBin(size);
}
static int sizeToIdx(size_t size) {
return Props::sizeToIdx(size);
}
void putList(ExtMemoryPool *extMemPool, LargeMemoryBlock *largeBlock);
LargeMemoryBlock *get(ExtMemoryPool *extMemPool, size_t size);
bool regularCleanup(ExtMemoryPool *extMemPool, uintptr_t currAge, bool doThreshDecr);
bool cleanAll(ExtMemoryPool *extMemPool);
void updateCacheState(ExtMemoryPool *extMemPool, DecreaseOrIncrease op, size_t size);
void reset();
void reportStat(FILE *f);
#if __TBB_MALLOC_WHITEBOX_TEST
size_t getLOCSize() const;
size_t getUsedSize() const;
#endif
};
class LargeObjectCache {
private:
static const size_t minLargeSize = 8 * 1024,
maxLargeSize = 8 * 1024 * 1024,
maxHugeSize = tbb::detail::select_size_t_constant<2147483648U, 1099511627776ULL>::value;
public:
static const size_t defaultMaxHugeSize = 64UL * 1024UL * 1024UL;
size_t hugeSizeThreshold;
private:
typedef LargeBinStructureProps<minLargeSize, maxLargeSize> LargeBSProps;
typedef LargeObjectCacheProps<LargeBSProps, 2, 2, 16> LargeCacheTypeProps;
typedef HugeBinStructureProps<maxLargeSize, maxHugeSize> HugeBSProps;
typedef LargeObjectCacheProps<HugeBSProps, 1, 1, 4> HugeCacheTypeProps;
typedef LargeObjectCacheImpl< LargeCacheTypeProps > LargeCacheType;
typedef LargeObjectCacheImpl< HugeCacheTypeProps > HugeCacheType;
HugeCacheType hugeCache;
LargeCacheType largeCache;
std::atomic<uintptr_t> cacheCurrTime;
ExtMemoryPool *extMemPool;
static int sizeToIdx(size_t size);
friend class Backend;
public:
void init(ExtMemoryPool *memPool);
void put(LargeMemoryBlock *largeBlock);
void putList(LargeMemoryBlock *head);
LargeMemoryBlock *get(size_t size);
void updateCacheState(DecreaseOrIncrease op, size_t size);
bool isCleanupNeededOnRange(uintptr_t range, uintptr_t currTime);
bool doCleanup(uintptr_t currTime, bool doThreshDecr);
bool decreasingCleanup();
bool regularCleanup();
bool cleanAll();
void reset();
void reportStat(FILE *f);
#if __TBB_MALLOC_WHITEBOX_TEST
size_t getLOCSize() const;
size_t getUsedSize() const;
#endif
static size_t alignToBin(size_t size);
void setHugeSizeThreshold(size_t value);
bool sizeInCacheRange(size_t size);
uintptr_t getCurrTimeRange(uintptr_t range);
void registerRealloc(size_t oldSize, size_t newSize);
};
#endif