LCOV - code coverage report
Current view: top level - co - bufferCache.cpp (source / functions) Hit Total Coverage
Test: Collage Lines: 77 104 74.0 %
Date: 2016-12-14 01:26:48 Functions: 14 15 93.3 %

          Line data    Source code
       1             : 
       2             : /* Copyright (c) 2006-2014, Stefan Eilemann <eile@equalizergraphics.com>
       3             :  *                    2012, 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 <lunchbox/atomic.h>
      29             : #include <list>
      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         113 :     explicit BufferCache( const int32_t minFree )
      67         113 :         : _minFree( minFree )
      68             :     {
      69         113 :         LBASSERT( minFree > 1);
      70         113 :         flush();
      71         113 :     }
      72             : 
      73         218 :     ~BufferCache()
      74         218 :     {
      75         109 :         LBASSERT( _cache.size() == 1 );
      76         109 :         LBASSERT( _cache.front()->isFree( ));
      77             : 
      78         109 :         delete _cache.front();
      79         109 :         _cache.clear();
      80         218 :     }
      81             : 
      82         320 :     void flush()
      83             :     {
      84         761 :         for( DataCIter i = _cache.begin(); i != _cache.end(); ++i )
      85             :         {
      86         441 :             co::Buffer* buffer = *i;
      87             :             //LBASSERTINFO( buffer->isFree(), *buffer );
      88         441 :             delete buffer;
      89             :         }
      90         320 :         LBASSERTINFO( size_t( _free ) == _cache.size(),
      91             :                       size_t( _free ) << " != " << _cache.size() );
      92             : 
      93         320 :         _cache.clear();
      94         320 :         _cache.push_back( new co::Buffer( this ));
      95         319 :         _free = 1;
      96         320 :         _maxFree = _minFree;
      97         320 :         _position = _cache.begin();
      98         320 :     }
      99             : 
     100      117978 :     BufferPtr newBuffer()
     101             :     {
     102      117978 :         const uint32_t cacheSize = uint32_t( _cache.size( ));
     103      117978 :         LBASSERTINFO( size_t( _free ) <= cacheSize,
     104             :                       size_t( _free ) << " > " << cacheSize );
     105             : 
     106      117978 :         if( _free > 0 )
     107             :         {
     108      117782 :             LBASSERT( cacheSize > 0 );
     109             : 
     110      117782 :             const DataCIter end = _position;
     111      117782 :             DataCIter& i = _position;
     112             : 
     113      147861 :             for( ++i; i != end; ++i )
     114             :             {
     115      147861 :                 if( i == _cache.end( ))
     116       31633 :                     i = _cache.begin();
     117             : #ifdef PROFILE
     118             :                 ++_lookups;
     119             : #endif
     120      147861 :                 co::Buffer* buffer = *i;
     121      147861 :                 if( !buffer->isFree( ))
     122       30079 :                     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, "
     137             :                            << size / 1024 << "KB" << std::endl;
     138             :                 }
     139             : #endif
     140      117782 :                 --_free;
     141      117782 :                 buffer->setUsed();
     142      235564 :                 return buffer;
     143             :             }
     144             :         }
     145             : 
     146         196 :         const uint32_t add = (cacheSize >> 3) + 1;
     147         432 :         for( size_t j = 0; j < add; ++j )
     148         236 :             _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      144426 :     void compact()
     164             :     {
     165      144426 :         if( _free <= _maxFree )
     166      144426 :             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      117977 :     virtual void notifyFree( co::Buffer* )
     208             :     {
     209      117977 :         ++_free;
     210      117977 :     }
     211             : };
     212             : }
     213             : 
     214         112 : BufferCache::BufferCache( const int32_t minFree )
     215         112 :         : _impl( new detail::BufferCache( minFree ))
     216         113 : {}
     217             : 
     218         218 : BufferCache::~BufferCache()
     219             : {
     220         109 :     flush();
     221         109 :     delete _impl;
     222         109 : }
     223             : 
     224         207 : void BufferCache::flush()
     225             : {
     226         207 :     _impl->flush();
     227         207 : }
     228             : 
     229      117978 : BufferPtr BufferCache::alloc( const uint64_t size )
     230             : {
     231      235956 :     LB_TS_SCOPED( _thread );
     232      117978 :     LBASSERTINFO( size >= COMMAND_ALLOCSIZE, size );
     233      117978 :     LBASSERTINFO( size < LB_BIT48,
     234             :                   "Out-of-sync network stream: buffer size " << size << "?" );
     235             : 
     236      117978 :     BufferPtr buffer = _impl->newBuffer();
     237      117978 :     LBASSERT( buffer->getRefCount() == 1 );
     238             : 
     239      117978 :     buffer->reserve( size );
     240      117978 :     buffer->resize( 0 );
     241      235956 :     return buffer;
     242             : }
     243             : 
     244      144426 : void BufferCache::compact()
     245             : {
     246      144426 :     _impl->compact();
     247      144426 : }
     248             : 
     249           0 : std::ostream& operator << ( std::ostream& os, const BufferCache& cache )
     250             : {
     251           0 :     const Data& buffers = cache._impl->_cache;
     252           0 :     os << lunchbox::disableFlush << "Cache has "
     253           0 :        << buffers.size() - cache._impl->_free << " used buffers:" << std::endl
     254           0 :        << lunchbox::indent << lunchbox::disableHeader;
     255             : 
     256           0 :     for( DataCIter i = buffers.begin(); i != buffers.end(); ++i )
     257             :     {
     258           0 :         Buffer* buffer = *i;
     259           0 :         if( !buffer->isFree( ))
     260           0 :             os << ICommand( 0, 0, buffer, false /*swap*/ ) << std::endl;
     261             :     }
     262           0 :     return os << lunchbox::enableHeader << lunchbox::exdent
     263           0 :               << lunchbox::enableFlush;
     264             : }
     265             : 
     266          66 : }

Generated by: LCOV version 1.11