LCOV - code coverage report
Current view: top level - co - bufferCache.cpp (source / functions) Hit Total Coverage
Test: Collage Lines: 75 102 73.5 %
Date: 2018-01-09 16:37:03 Functions: 14 15 93.3 %

          Line data    Source code
       1             : 
       2             : /* Copyright (c) 2006-2017, Stefan Eilemann <eile@equalizergraphics.com>
       3             :  *                          Daniel Nachbaur <danielnachbaur@gmail.com>
       4             :  *
       5             :  * This file is part of Collage <https://github.com/Eyescale/Collage>
       6             :  *
       7             :  * This library is free software; you can redistribute it and/or modify it under
       8             :  * the terms of the GNU Lesser General Public License version 2.1 as published
       9             :  * by the Free Software Foundation.
      10             :  *
      11             :  * This library is distributed in the hope that it will be useful, but WITHOUT
      12             :  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
      13             :  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
      14             :  * details.
      15             :  *
      16             :  * You should have received a copy of the GNU Lesser General Public License
      17             :  * along with this library; if not, write to the Free Software Foundation, Inc.,
      18             :  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      19             :  */
      20             : 
      21             : #include "bufferCache.h"
      22             : 
      23             : #include "buffer.h"
      24             : #include "bufferListener.h"
      25             : #include "iCommand.h"
      26             : #include "node.h"
      27             : 
      28             : #include <list>
      29             : #include <lunchbox/atomic.h>
      30             : 
      31             : //#define PROFILE
      32             : // 31300 hits, 35 misses, 297640 lookups, 126976b allocated in 31 buffers
      33             : // 31300 hits, 35 misses, 49228 lookups, 135168b allocated in 34 buffers
      34             : //
      35             : // The buffer cache periodically frees allocated buffers to bound memory usage:
      36             : // * 'minFree' buffers (given in ctor) are always kept free
      37             : // * above 'size >> _maxFreeShift' free buffers compaction occurs
      38             : // * compaction tries to reach '(size >> _maxFreeShift) >> _targetShift' buffers
      39             : //
      40             : // In other words, using the values below, if more than half of the buffers are
      41             : // free, the cache is compacted to until one quarter of the buffers is free.
      42             : 
      43             : namespace co
      44             : {
      45             : namespace
      46             : {
      47             : typedef std::list<Buffer*> Data;
      48             : typedef Data::const_iterator DataCIter;
      49             : 
      50             : static const uint32_t _maxFreeShift = 1; // _maxFree = size >> shift
      51             : static const uint32_t _targetShift = 1;  // _targetFree = _maxFree >> shift
      52             : 
      53             : #ifdef PROFILE
      54             : static lunchbox::a_int32_t _hits;
      55             : static lunchbox::a_int32_t _misses;
      56             : static lunchbox::a_int32_t _lookups;
      57             : static lunchbox::a_int32_t _allocs;
      58             : static lunchbox::a_int32_t _frees;
      59             : #endif
      60             : }
      61             : namespace detail
      62             : {
      63             : class BufferCache : public BufferListener
      64             : {
      65             : public:
      66         109 :     explicit BufferCache(const int32_t minFree)
      67         109 :         : _minFree(minFree)
      68             :     {
      69         109 :         LBASSERT(minFree > 1);
      70         109 :         flush();
      71         109 :     }
      72             : 
      73         210 :     ~BufferCache()
      74         210 :     {
      75         105 :         LBASSERT(_cache.size() == 1);
      76         105 :         LBASSERT(_cache.front()->isFree());
      77             : 
      78         105 :         delete _cache.front();
      79         105 :         _cache.clear();
      80         210 :     }
      81             : 
      82         308 :     void flush()
      83             :     {
      84         850 :         for (DataCIter i = _cache.begin(); i != _cache.end(); ++i)
      85             :         {
      86         542 :             co::Buffer* buffer = *i;
      87             :             // LBASSERTINFO( buffer->isFree(), *buffer );
      88         542 :             delete buffer;
      89             :         }
      90         308 :         LBASSERTINFO(size_t(_free) == _cache.size(),
      91             :                      size_t(_free) << " != " << _cache.size());
      92             : 
      93         308 :         _cache.clear();
      94         308 :         _cache.push_back(new co::Buffer(this));
      95         308 :         _free = 1;
      96         308 :         _maxFree = _minFree;
      97         308 :         _position = _cache.begin();
      98         308 :     }
      99             : 
     100      116073 :     BufferPtr newBuffer()
     101             :     {
     102      116073 :         const uint32_t cacheSize = uint32_t(_cache.size());
     103      116073 :         LBASSERTINFO(size_t(_free) <= cacheSize, size_t(_free) << " > "
     104             :                                                                << cacheSize);
     105             : 
     106      116073 :         if (_free > 0)
     107             :         {
     108      115877 :             LBASSERT(cacheSize > 0);
     109             : 
     110      115877 :             const DataCIter end = _position;
     111      115877 :             DataCIter& i = _position;
     112             : 
     113      141750 :             for (++i; i != end; ++i)
     114             :             {
     115      141750 :                 if (i == _cache.end())
     116       26052 :                     i = _cache.begin();
     117             : #ifdef PROFILE
     118             :                 ++_lookups;
     119             : #endif
     120      141750 :                 co::Buffer* buffer = *i;
     121      141750 :                 if (!buffer->isFree())
     122       25873 :                     continue;
     123             : 
     124             : #ifdef PROFILE
     125             :                 const long hits = ++_hits;
     126             :                 if ((hits % 1000) == 0)
     127             :                 {
     128             :                     size_t size = 0;
     129             :                     for (DataCIter j = _cache.begin(); j != _cache.end(); ++j)
     130             :                         size += (*j)->getMaxSize();
     131             : 
     132             :                     LBINFO << _hits << "/" << _hits + _misses << " hits, "
     133             :                            << _lookups << " lookups, " << _free << " of "
     134             :                            << _cache.size() << " buffers free (min " << _minFree
     135             :                            << " max " << _maxFree << "), " << _allocs
     136             :                            << " allocs, " << _frees << " frees, " << size / 1024
     137             :                            << "KB" << std::endl;
     138             :                 }
     139             : #endif
     140      115877 :                 --_free;
     141      115877 :                 buffer->setUsed();
     142      231754 :                 return buffer;
     143             :             }
     144             :         }
     145             : 
     146         196 :         const uint32_t add = (cacheSize >> 3) + 1;
     147         541 :         for (size_t j = 0; j < add; ++j)
     148         345 :             _cache.push_back(new co::Buffer(this));
     149             : 
     150         196 :         _free += add - 1;
     151         196 :         const int32_t num = int32_t(_cache.size() >> _maxFreeShift);
     152         196 :         _maxFree = LB_MAX(_minFree, num);
     153         196 :         _position = _cache.begin();
     154             : 
     155             : #ifdef PROFILE
     156             :         ++_misses;
     157             :         _allocs += add;
     158             : #endif
     159         196 :         _cache.back()->setUsed();
     160         196 :         return _cache.back();
     161             :     }
     162             : 
     163      144274 :     void compact()
     164             :     {
     165      144274 :         if (_free <= _maxFree)
     166      144274 :             return;
     167             : 
     168           0 :         const int32_t tgt = _maxFree >> _targetShift;
     169           0 :         const int32_t target = LB_MAX(tgt, _minFree);
     170           0 :         LBASSERT(target > 0);
     171             : 
     172           0 :         Data::iterator i = _cache.begin();
     173           0 :         while (i != _cache.end())
     174             :         {
     175           0 :             const co::Buffer* buffer = *i;
     176           0 :             if (buffer->isFree())
     177             :             {
     178           0 :                 LBASSERT(_free > 0);
     179             : #ifdef PROFILE
     180             :                 ++_frees;
     181             : #endif
     182           0 :                 i = _cache.erase(i);
     183           0 :                 delete buffer;
     184             : 
     185           0 :                 if (--_free <= target)
     186           0 :                     break;
     187             :             }
     188             :             else
     189           0 :                 ++i;
     190             :         }
     191             : 
     192           0 :         const int32_t num = int32_t(_cache.size() >> _maxFreeShift);
     193           0 :         _maxFree = LB_MAX(_minFree, num);
     194           0 :         _position = (i == _cache.end()) ? _cache.begin() : i;
     195             :     }
     196             : 
     197             : private:
     198             :     friend std::ostream& co::operator<<(std::ostream&, const co::BufferCache&);
     199             : 
     200             :     Data _cache;
     201             :     DataCIter _position;       //!< Last lookup position
     202             :     lunchbox::a_int32_t _free; //!< The current number of free items
     203             : 
     204             :     const int32_t _minFree;
     205             :     int32_t _maxFree; //!< The maximum number of free items
     206             : 
     207      116072 :     virtual void notifyFree(co::Buffer*) { ++_free; }
     208             : };
     209             : }
     210             : 
     211         109 : BufferCache::BufferCache(const int32_t minFree)
     212         109 :     : _impl(new detail::BufferCache(minFree))
     213             : {
     214         109 : }
     215             : 
     216         210 : BufferCache::~BufferCache()
     217             : {
     218         105 :     flush();
     219         105 :     delete _impl;
     220         105 : }
     221             : 
     222         199 : void BufferCache::flush()
     223             : {
     224         199 :     _impl->flush();
     225         199 : }
     226             : 
     227      116073 : BufferPtr BufferCache::alloc(const uint64_t size)
     228             : {
     229      232146 :     LB_TS_SCOPED(_thread);
     230      116073 :     LBASSERTINFO(size >= COMMAND_ALLOCSIZE, size);
     231      116073 :     LBASSERTINFO(size < LB_BIT48, "Out-of-sync network stream: buffer size "
     232             :                                       << size << "?");
     233             : 
     234      116073 :     BufferPtr buffer = _impl->newBuffer();
     235      116073 :     LBASSERT(buffer->getRefCount() == 1);
     236             : 
     237      116073 :     buffer->reserve(size);
     238      116073 :     buffer->resize(0);
     239      232146 :     return buffer;
     240             : }
     241             : 
     242      144274 : void BufferCache::compact()
     243             : {
     244      144274 :     _impl->compact();
     245      144274 : }
     246             : 
     247           0 : std::ostream& operator<<(std::ostream& os, const BufferCache& cache)
     248             : {
     249           0 :     const Data& buffers = cache._impl->_cache;
     250           0 :     os << lunchbox::disableFlush << "Cache has "
     251           0 :        << buffers.size() - cache._impl->_free << " used buffers:" << std::endl
     252           0 :        << lunchbox::indent << lunchbox::disableHeader;
     253             : 
     254           0 :     for (DataCIter i = buffers.begin(); i != buffers.end(); ++i)
     255             :     {
     256           0 :         Buffer* buffer = *i;
     257           0 :         if (!buffer->isFree())
     258           0 :             os << ICommand(0, 0, buffer) << std::endl;
     259             :     }
     260           0 :     return os << lunchbox::enableHeader << lunchbox::exdent
     261           0 :               << lunchbox::enableFlush;
     262             : }
     263          63 : }

Generated by: LCOV version 1.11