@@ -362,7 +362,7 @@ CacheAllocator<CacheTrait>::allocate(PoolId poolId,
362362 creationTime = util::getCurrentTimeSec ();
363363 }
364364 return allocateInternal (poolId, key, size, creationTime,
365- ttlSecs == 0 ? 0 : creationTime + ttlSecs);
365+ ttlSecs == 0 ? 0 : creationTime + ttlSecs, false );
366366}
367367
368368template <typename CacheTrait>
@@ -372,7 +372,8 @@ CacheAllocator<CacheTrait>::allocateInternalTier(TierId tid,
372372 typename Item::Key key,
373373 uint32_t size,
374374 uint32_t creationTime,
375- uint32_t expiryTime) {
375+ uint32_t expiryTime,
376+ bool fromEvictorThread) {
376377 util::LatencyTracker tracker{stats ().allocateLatency_ };
377378
378379 SCOPE_FAIL { stats_.invalidAllocs .inc (); };
@@ -387,14 +388,27 @@ CacheAllocator<CacheTrait>::allocateInternalTier(TierId tid,
387388 // TODO: per-tier
388389 (*stats_.allocAttempts )[pid][cid].inc ();
389390
390- void * memory = allocator_[tid]->allocate (pid, requiredSize);
391+ void *memory = nullptr ;
392+
393+ if (tid == 0 && config_.acTopTierEvictionWatermark > 0.0
394+ && getAllocationClassStats (tid, pid, cid)
395+ .approxFreePercent < config_.acTopTierEvictionWatermark ) {
396+ memory = findEviction (tid, pid, cid);
397+ }
398+
399+ if (memory == nullptr ) {
400+ // TODO: should we try allocate item even if this will result in violating
401+ // acTopTierEvictionWatermark?
402+ memory = allocator_[tid]->allocate (pid, requiredSize);
403+ }
404+
391405 // TODO: Today disableEviction means do not evict from memory (DRAM).
392406 // Should we support eviction between memory tiers (e.g. from DRAM to PMEM)?
393407 if (memory == nullptr && !config_.disableEviction ) {
394408 memory = findEviction (tid, pid, cid);
395409 }
396410
397- ItemHandle handle;
411+ WriteHandle handle;
398412 if (memory != nullptr ) {
399413 // At this point, we have a valid memory allocation that is ready for use.
400414 // Ensure that when we abort from here under any circumstances, we free up
@@ -431,18 +445,71 @@ CacheAllocator<CacheTrait>::allocateInternalTier(TierId tid,
431445}
432446
433447template <typename CacheTrait>
434- typename CacheAllocator<CacheTrait>::WriteHandle
435- CacheAllocator<CacheTrait>::allocateInternal (PoolId pid,
448+ TierId
449+ CacheAllocator<CacheTrait>::getTargetTierForItem (PoolId pid,
436450 typename Item::Key key,
437451 uint32_t size,
438452 uint32_t creationTime,
439453 uint32_t expiryTime) {
440- auto tid = 0 ; /* TODO: consult admission policy */
441- for (TierId tid = 0 ; tid < numTiers_; ++tid) {
442- auto handle = allocateInternalTier (tid, pid, key, size, creationTime, expiryTime);
443- if (handle) return handle;
454+ if (numTiers_ == 1 )
455+ return 0 ;
456+
457+ if (config_.forceAllocationTier != UINT64_MAX) {
458+ return config_.forceAllocationTier ;
444459 }
445- return {};
460+
461+ const TierId defaultTargetTier = 0 ;
462+
463+ const auto requiredSize = Item::getRequiredSize (key, size);
464+ const auto cid = allocator_[defaultTargetTier]->getAllocationClassId (pid, requiredSize);
465+
466+ auto freePercentage = getAllocationClassStats (defaultTargetTier, pid, cid).approxFreePercent ;
467+
468+ // TODO: COULD we implement BG worker which would move slabs around
469+ // so that there is similar amount of free space in each pool/ac.
470+ // Should this be responsibility of BG evictor?
471+
472+ if (freePercentage >= config_.maxAcAllocationWatermark )
473+ return defaultTargetTier;
474+
475+ if (freePercentage <= config_.minAcAllocationWatermark )
476+ return defaultTargetTier + 1 ;
477+
478+ // TODO: we can even think about creating different allocation classes for PMEM
479+ // and we could look at possible fragmentation when deciding where to put the item
480+ if (config_.sizeThresholdPolicy )
481+ return requiredSize < config_.sizeThresholdPolicy ? defaultTargetTier : defaultTargetTier + 1 ;
482+
483+ // TODO: (e.g. always put chained items to PMEM)
484+ // if (chainedItemsPolicy)
485+ // return item.isChainedItem() ? defaultTargetTier + 1 : defaultTargetTier;
486+
487+ // TODO:
488+ // if (expiryTimePolicy)
489+ // return (expiryTime - creationTime) < expiryTimePolicy ? defaultTargetTier : defaultTargetTier + 1;
490+
491+ // TODO:
492+ // if (keyPolicy) // this can be based on key length or some other properties
493+ // return getTargetTierForKey(key);
494+
495+ // TODO:
496+ // if (compressabilityPolicy) // if compresses well store in PMEM? latency will be higher anyway
497+ // return TODO;
498+
499+ // TODO: only works for 2 tiers
500+ return (folly::Random::rand32 () % 100 ) < config_.defaultTierChancePercentage ? defaultTargetTier : defaultTargetTier + 1 ;
501+ }
502+
503+ template <typename CacheTrait>
504+ typename CacheAllocator<CacheTrait>::WriteHandle
505+ CacheAllocator<CacheTrait>::allocateInternal(PoolId pid,
506+ typename Item::Key key,
507+ uint32_t size,
508+ uint32_t creationTime,
509+ uint32_t expiryTime,
510+ bool fromEvictorThread) {
511+ auto tid = getTargetTierForItem (pid, key, size, creationTime, expiryTime);
512+ return allocateInternalTier (tid, pid, key, size, creationTime, expiryTime, fromEvictorThread);
446513}
447514
448515template <typename CacheTrait>
@@ -1608,25 +1675,38 @@ bool CacheAllocator<CacheTrait>::shouldWriteToNvmCacheExclusive(
16081675 return true ;
16091676}
16101677
1678+ template <typename CacheTrait>
1679+ bool CacheAllocator<CacheTrait>::shouldEvictToNextMemoryTier(
1680+ TierId sourceTierId, TierId targetTierId, PoolId pid, Item& item)
1681+ {
1682+ if (config_.disableEvictionToMemory )
1683+ return false ;
1684+
1685+ // TODO: implement more advanced admission policies for memory tiers
1686+ return true ;
1687+ }
1688+
16111689template <typename CacheTrait>
16121690typename CacheAllocator<CacheTrait>::WriteHandle
16131691CacheAllocator<CacheTrait>::tryEvictToNextMemoryTier(
1614- TierId tid, PoolId pid, Item& item) {
1615- if (item.isChainedItem ()) return {}; // TODO: We do not support ChainedItem yet
1692+ TierId tid, PoolId pid, Item& item, bool fromEvictorThread) {
16161693 if (item.isExpired ()) return acquire (&item);
16171694
1618- TierId nextTier = tid; // TODO - calculate this based on some admission policy
1695+ TierId nextTier = tid;
16191696 while (++nextTier < numTiers_) { // try to evict down to the next memory tiers
1697+ if (!shouldEvictToNextMemoryTier (tid, nextTier, pid, item))
1698+ continue ;
1699+
16201700 // allocateInternal might trigger another eviction
16211701 auto newItemHdl = allocateInternalTier (nextTier, pid,
16221702 item.getKey (),
16231703 item.getSize (),
16241704 item.getCreationTime (),
1625- item.getExpiryTime ());
1705+ item.getExpiryTime (),
1706+ fromEvictorThread);
16261707
16271708 if (newItemHdl) {
16281709 XDCHECK_EQ (newItemHdl->getSize (), item.getSize ());
1629-
16301710 return moveRegularItemOnEviction (item, newItemHdl);
16311711 }
16321712 }
@@ -1636,10 +1716,10 @@ CacheAllocator<CacheTrait>::tryEvictToNextMemoryTier(
16361716
16371717template <typename CacheTrait>
16381718typename CacheAllocator<CacheTrait>::WriteHandle
1639- CacheAllocator<CacheTrait>::tryEvictToNextMemoryTier(Item& item) {
1719+ CacheAllocator<CacheTrait>::tryEvictToNextMemoryTier(Item& item, bool fromEvictorThread ) {
16401720 auto tid = getTierId (item);
16411721 auto pid = allocator_[tid]->getAllocInfo (item.getMemory ()).poolId ;
1642- return tryEvictToNextMemoryTier (tid, pid, item);
1722+ return tryEvictToNextMemoryTier (tid, pid, item, fromEvictorThread );
16431723}
16441724
16451725template <typename CacheTrait>
@@ -2361,6 +2441,10 @@ void CacheAllocator<CacheTrait>::createMMContainers(const PoolId pid,
23612441 .getAllocsPerSlab ()
23622442 : 0 );
23632443 for (TierId tid = 0 ; tid < numTiers_; tid++) {
2444+ if constexpr (std::is_same_v<MMConfig, MMLru::Config> || std::is_same_v<MMConfig, MM2Q::Config>) {
2445+ config.lruInsertionPointSpec = config_.memoryTierConfigs [tid].lruInsertionPointSpec ;
2446+ config.markUsefulChance = config_.memoryTierConfigs [tid].markUsefulChance ;
2447+ }
23642448 mmContainers_[tid][pid][cid].reset (new MMContainer (config, compressor_));
23652449 }
23662450 }
@@ -2415,7 +2499,7 @@ std::set<PoolId> CacheAllocator<CacheTrait>::getRegularPoolIds() const {
24152499 folly::SharedMutex::ReadHolder r (poolsResizeAndRebalanceLock_);
24162500 // TODO - get rid of the duplication - right now, each tier
24172501 // holds pool objects with mostly the same info
2418- return filterCompactCachePools (allocator_[0 ]->getPoolIds ());
2502+ return filterCompactCachePools (allocator_[currentTier () ]->getPoolIds ());
24192503}
24202504
24212505template <typename CacheTrait>
@@ -2828,7 +2912,8 @@ CacheAllocator<CacheTrait>::allocateNewItemForOldItem(const Item& oldItem) {
28282912 oldItem.getKey (),
28292913 oldItem.getSize (),
28302914 oldItem.getCreationTime (),
2831- oldItem.getExpiryTime ());
2915+ oldItem.getExpiryTime (),
2916+ false );
28322917 if (!newItemHdl) {
28332918 return {};
28342919 }
@@ -2961,14 +3046,14 @@ void CacheAllocator<CacheTrait>::evictForSlabRelease(
29613046template <typename CacheTrait>
29623047typename CacheAllocator<CacheTrait>::ItemHandle
29633048CacheAllocator<CacheTrait>::evictNormalItem(Item& item,
2964- bool skipIfTokenInvalid) {
3049+ bool skipIfTokenInvalid, bool fromEvictorThread ) {
29653050 XDCHECK (item.isMoving ());
29663051
29673052 if (item.isOnlyMoving ()) {
29683053 return ItemHandle{};
29693054 }
29703055
2971- auto evictHandle = tryEvictToNextMemoryTier (item);
3056+ auto evictHandle = tryEvictToNextMemoryTier (item, fromEvictorThread );
29723057 if (evictHandle) return evictHandle;
29733058
29743059 auto predicate = [](const Item& it) { return it.getRefCount () == 0 ; };
0 commit comments