Line data Source code
1 : #include "CacheResource.h" 2 : 3 : namespace elsa::mr 4 : { 5 : void CacheResource::releaseCache() 6 13 : { 7 10058 : for (auto& entry : _cache) { 8 10058 : _upstream->deallocate(entry.ptr, entry.size, entry.alignment); 9 10058 : } 10 13 : _cache.clear(); 11 13 : _sizeToCacheElement.clear(); 12 13 : } 13 : 14 : CacheResource::CacheResource(MemoryResource upstream, const CacheResourceConfig& config) 15 : : _upstream{upstream}, _config{config} 16 10 : { 17 10 : if (config.maxCachedCount != std::numeric_limits<size_t>::max()) { 18 : // The + 1 is necessary, because elements are evicted after inserting, 19 : // so the cache is briefly larger than the maxCachedCount. 20 9 : _sizeToCacheElement.reserve(config.maxCachedCount + 1); 21 9 : } 22 10 : } 23 : 24 : CacheResource::~CacheResource() 25 10 : { 26 10 : releaseCache(); 27 10 : } 28 : 29 : MemoryResource CacheResource::make(MemoryResource upstream, const CacheResourceConfig& config) 30 10 : { 31 10 : return std::shared_ptr<MemResInterface>(new CacheResource(upstream, config), 32 10 : [](CacheResource* p) { delete p; }); 33 10 : } 34 : 35 : void* CacheResource::allocate(size_t size, size_t alignment) 36 30139 : { 37 30139 : auto mapIt = _sizeToCacheElement.find({size, alignment}); 38 30139 : if (mapIt == _sizeToCacheElement.end()) { 39 29584 : try { 40 29584 : return _upstream->allocate(size, alignment); 41 29584 : } catch (std::bad_alloc& e) { 42 3 : releaseCache(); 43 : // try again after hopefully returning enough memory to the upstream allocator 44 3 : return _upstream->allocate(size, alignment); 45 3 : } 46 555 : } else { 47 555 : void* ptr = mapIt->second->ptr; 48 555 : _cachedSize -= mapIt->second->size; 49 555 : _cache.erase(mapIt->second); 50 555 : _sizeToCacheElement.erase(mapIt); 51 555 : return ptr; 52 555 : } 53 30139 : } 54 : 55 : void CacheResource::deallocate(void* ptr, size_t size, size_t alignment) noexcept 56 30136 : { 57 30136 : if (size > _config.maxCacheSize) { 58 0 : _upstream->deallocate(ptr, size, alignment); 59 0 : return; 60 0 : } 61 : 62 30136 : if (!ptr) { 63 0 : return; 64 0 : } 65 : 66 30136 : try { 67 30136 : _cache.push_back({ptr, size, alignment}); 68 30136 : } catch (std::bad_alloc& e) { 69 0 : _upstream->deallocate(ptr, size, alignment); 70 0 : return; 71 0 : } 72 : 73 30136 : try { 74 30136 : _sizeToCacheElement.insert({{size, alignment}, --_cache.end()}); 75 30136 : } catch (std::bad_alloc& e) { 76 0 : _cache.pop_back(); 77 0 : _upstream->deallocate(ptr, size, alignment); 78 0 : return; 79 0 : } 80 : 81 30136 : _cachedSize += size; 82 49659 : while (_cache.size() > _config.maxCachedCount || _cachedSize > _config.maxCacheSize) { 83 19523 : cache_resource::CacheElement& poppedElement = _cache.front(); 84 19523 : auto poppedIt = _sizeToCacheElement.find({poppedElement.size, poppedElement.alignment}); 85 : // If this throws, internal invariants are violated (the element is not in the map). 86 : // This would be a serious bug, thus termination seems justified. 87 35255 : while (&*poppedIt->second != &poppedElement) 88 15732 : poppedIt++; 89 19523 : _sizeToCacheElement.erase(poppedIt); 90 19523 : _cachedSize -= poppedElement.size; 91 19523 : _upstream->deallocate(poppedElement.ptr, poppedElement.size, poppedElement.alignment); 92 19523 : _cache.pop_front(); 93 19523 : } 94 30136 : } 95 : 96 : bool CacheResource::tryResize(void* ptr, size_t size, size_t alignment, size_t newSize) noexcept 97 0 : { 98 0 : return _upstream->tryResize(ptr, size, alignment, newSize); 99 0 : } 100 : } // namespace elsa::mr