LCOV - code coverage report
Current view: top level - tests - persistentMap.cpp (source / functions) Hit Total Coverage
Test: Lunchbox Lines: 106 167 63.5 %
Date: 2016-03-29 17:09:06 Functions: 16 17 94.1 %

          Line data    Source code
       1             : 
       2             : /* Copyright (c) 2014-2015, Stefan.Eilemann@epfl.ch
       3             :  *
       4             :  * This library is free software; you can redistribute it and/or modify it under
       5             :  * the terms of the GNU Lesser General Public License version 2.1 as published
       6             :  * by the Free Software Foundation.
       7             :  *
       8             :  * This library is distributed in the hope that it will be useful, but WITHOUT
       9             :  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
      10             :  * FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
      11             :  * details.
      12             :  *
      13             :  * You should have received a copy of the GNU Lesser General Public License
      14             :  * along with this library; if not, write to the Free Software Foundation, Inc.,
      15             :  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
      16             :  */
      17             : 
      18             : #define TEST_RUNTIME 240 //seconds
      19             : #include <test.h>
      20             : #include <lunchbox/clock.h>
      21             : #include <lunchbox/os.h>
      22             : #include <lunchbox/persistentMap.h>
      23             : #include <lunchbox/rng.h>
      24             : #ifdef LUNCHBOX_USE_LEVELDB
      25             : #  include <leveldb/db.h>
      26             : #endif
      27             : #ifdef LUNCHBOX_USE_SKV
      28             : #  include <FxLogger/FxLogger.hpp>
      29             : #endif
      30             : #include <boost/format.hpp>
      31             : #include <stdexcept>
      32             : 
      33             : using lunchbox::PersistentMap;
      34             : 
      35             : const int ints[] = { 17, 53, 42, 65535, 32768 };
      36             : const size_t numInts = sizeof( ints ) / sizeof( int );
      37             : const int64_t loopTime = 1000;
      38             : bool perfTest = false;
      39             : 
      40           6 : template< class T > void insertVector( PersistentMap& map )
      41             : {
      42           6 :     std::vector< T > vector;
      43          36 :     for( size_t i = 0; i < numInts; ++i )
      44          30 :         vector.push_back( T( ints[ i ] ));
      45           6 :     TEST( map.insert( typeid( vector ).name(), vector ));
      46           6 : }
      47             : 
      48          18 : template< class T > void readVector( const PersistentMap& map )
      49             : {
      50             :     const std::vector< T >& vector =
      51          18 :         map.getVector< T >( typeid( vector ).name( ));
      52          18 :     TESTINFO( vector.size() ==  numInts, vector.size() << " != " << numInts );
      53         108 :     for( size_t i = 0; i < numInts; ++i )
      54         108 :         TEST( vector[ i ] == T( ints[i] ));
      55          18 : }
      56             : 
      57           6 : template< class T > void insertVector( PersistentMap& map, const size_t elems )
      58             : {
      59           6 :     std::vector< T > vector;
      60      786438 :     for( size_t i = 0; i < elems; ++i )
      61      786432 :         vector.push_back( i );
      62           6 :     TEST( map.insert( std::string( "bulk" ) + typeid( vector ).name(), vector ));
      63           6 : }
      64             : 
      65           6 : template< class T > void readVector( const PersistentMap& map, const size_t elems )
      66             : {
      67             :     const std::vector< T >& vector =
      68           6 :         map.getVector< T >( std::string( "bulk" ) + typeid( vector ).name());
      69           6 :     TESTINFO( vector.size() ==  elems, vector.size() << " != " << elems );
      70          36 :     for( size_t i = 0; i < numInts; ++i )
      71          36 :         TESTINFO( vector[ i ] == T( i ), vector[ i ] << " != " << i );
      72           6 : }
      73             : 
      74           6 : void read( const PersistentMap& map )
      75             : {
      76             :     const std::set< uint32_t >& bigSet =
      77           6 :         map.getSet< uint32_t >( "std::set< uint32_t >" );
      78           6 :     TEST( bigSet.size() == 1000 );
      79        6006 :     for( uint32_t i = 1; i <= 1000; ++i )
      80        6000 :         TEST( bigSet.find( i ) != bigSet.end( ));
      81             : 
      82           6 :     TEST( map[ "foo" ] == "bar" );
      83           6 :     TEST( map[ "bar" ].empty( ));
      84           6 :     TEST( map.get< bool >( "bValue" ) == true );
      85           6 :     TEST( map.get< int >( "iValue" ) == 42 );
      86             : 
      87           6 :     readVector< int >( map );
      88           6 :     readVector< uint16_t >( map );
      89             : 
      90          12 :     const std::set< int >& set = map.getSet< int >( "std::set< int >" );
      91           6 :     TESTINFO( set.size() ==  numInts, set.size() << " != " << numInts );
      92          36 :     for( size_t i = 0; i < numInts; ++i )
      93          30 :         TESTINFO( set.find( ints[i] ) != set.end(),
      94           6 :                   ints[i] << " not found in set" );
      95           6 : }
      96             : 
      97           3 : void read( const std::string& uri )
      98             : {
      99           3 :     PersistentMap map( uri );
     100           3 :     read( map );
     101           3 : }
     102             : 
     103           5 : void setup( const std::string& uri )
     104             : {
     105           5 :     PersistentMap map( uri );
     106           3 :     TEST( map.insert( "foo", "bar" ));
     107           3 :     TEST( map.contains( "foo" ));
     108           3 :     TESTINFO( map[ "foo" ] == "bar",
     109           0 :               map[ "foo" ] << " length " << map[ "foo" ].length( ));
     110           3 :     TEST( map[ "bar" ].empty( ));
     111             : 
     112           3 :     TEST( map.insert( "the quick brown fox", "jumped over something" ));
     113           3 :     TESTINFO( map[ "the quick brown fox" ] == "jumped over something",
     114           0 :               map[ "the quick brown fox" ] );
     115             : 
     116           3 :     TEST( map.insert( "hans", std::string( "dampf" )));
     117           3 :     TESTINFO( map[ "hans" ] == "dampf", map[ "hans" ] );
     118             : 
     119           3 :     const bool bValue = true;
     120           3 :     TEST( map.insert( "bValue", bValue ));
     121           3 :     TEST( map.get< bool >( "bValue" ) == bValue );
     122             : 
     123           3 :     const int iValue = 42;
     124           3 :     TEST( map.insert( "iValue", iValue ));
     125           3 :     TEST( map.get< int >( "iValue" ) == iValue );
     126             : 
     127           3 :     TEST( map.insert( "coffee", 0xC0FFEE ));
     128           3 :     map.setByteswap( true );
     129           3 :     TEST( map.get< unsigned >( "coffee" ) == 0xEEFFC000u );
     130           3 :     map.setByteswap( false );
     131           3 :     TEST( map.get< int >( "coffee" ) == 0xC0FFEE );
     132             : 
     133           3 :     insertVector< int >( map );
     134           3 :     insertVector< uint16_t >( map );
     135           3 :     readVector< int >( map );
     136           3 :     readVector< uint16_t >( map );
     137             : 
     138           3 :     insertVector< int >( map, LB_128KB );
     139           3 :     insertVector< uint16_t >( map, LB_128KB );
     140           3 :     map.fetch( std::string( "bulk" ) + typeid( std::vector< int > ).name( ));
     141           3 :     map.fetch( std::string( "bulk" ) + typeid( std::vector<uint16_t> ).name( ));
     142           3 :     readVector< int >( map, LB_128KB );
     143           3 :     readVector< uint16_t >( map, LB_128KB );
     144             : 
     145           6 :     std::set< int > set( ints, ints + numInts );
     146           3 :     TEST( map.insert( "std::set< int >", set ));
     147             : 
     148           6 :     std::set< uint32_t > bigSet;
     149        3003 :     for( uint32_t i = 1; i <= 1000; ++i )
     150        3000 :         bigSet.insert( i );
     151           3 :     TEST( map.insert( "std::set< uint32_t >", bigSet ));
     152             : 
     153           6 :     read( map );
     154           3 : }
     155             : 
     156           0 : void benchmark( const std::string& uri, const uint64_t queueDepth,
     157             :                 const size_t valueSize )
     158             : {
     159           0 :     static std::string lastURI;
     160           0 :     if( uri != lastURI )
     161             :     {
     162           0 :         std::cout << uri << std::endl;
     163           0 :         lastURI = uri;
     164             :     }
     165             : 
     166           0 :     PersistentMap map( uri );
     167           0 :     map.setQueueDepth( queueDepth );
     168             : 
     169             :     // Prepare keys and value
     170           0 :     lunchbox::Strings keys;
     171           0 :     keys.resize( queueDepth + 1 );
     172           0 :     for( uint64_t i = 0; i <= queueDepth; ++i )
     173           0 :         keys[i].assign( reinterpret_cast< char* >( &i ), 8 );
     174             : 
     175           0 :     std::string value( valueSize, '*' );
     176           0 :     lunchbox::RNG rng;
     177           0 :     for( size_t i = 0; i < valueSize; ++i )
     178           0 :         value[i] = rng.get<char>();
     179             : 
     180             :     // write performance
     181           0 :     lunchbox::Clock clock;
     182           0 :     uint64_t i = 0;
     183           0 :     while( clock.getTime64() < loopTime )
     184             :     {
     185           0 :         map.insert( keys[ i % (queueDepth+1) ], value );
     186           0 :         ++i;
     187             :     }
     188           0 :     map.flush();
     189           0 :     const float writeTime = clock.getTimef() / 1000.f;
     190           0 :     const uint64_t wOps = i;
     191           0 :     TEST( i > queueDepth );
     192             : 
     193             :     // read performance
     194           0 :     clock.reset();
     195           0 :     if( queueDepth == 0 ) // sync read
     196             :     {
     197           0 :         for( i = 0; i < wOps && clock.getTime64() < loopTime; ++i ) // read keys
     198           0 :             map[ keys[ i % (queueDepth+1) ]];
     199             :     }
     200             :     else // fetch + async read
     201             :     {
     202           0 :         for( i = 0; i < queueDepth; ++i ) // prefetch queueDepth keys
     203           0 :             TEST( map.fetch( keys[ i % (queueDepth+1) ], valueSize ) );
     204             : 
     205           0 :         for( ; i < wOps && clock.getTime64() < loopTime; ++i ) // read keys
     206             :         {
     207           0 :             map[ keys[ (i - queueDepth) % (queueDepth+1) ] ];
     208           0 :             TEST( map.fetch( keys[ i % (queueDepth+1) ], valueSize ));
     209             :         }
     210             : 
     211           0 :         for( uint64_t j = i - queueDepth; j <= i; ++j ) // drain fetched keys
     212           0 :             map[ keys[ j % (queueDepth+1) ]];
     213             :     }
     214             : 
     215           0 :     const float readTime = clock.getTimef() / 1000.f;
     216           0 :     const size_t rOps = i;
     217             : 
     218             :     std::cout << boost::format( "%6i, %6i, %9.2f, %9.2f, %9.2f, %9.2f")
     219             :         // cppcheck-suppress zerodivcond
     220           0 :         % queueDepth % valueSize % (rOps/readTime) % (wOps/writeTime)
     221           0 :         % (rOps/1024.f/1024.f*valueSize/readTime)
     222           0 :         % (wOps/1024.f/1024.f*valueSize/writeTime) << std::endl;
     223             : 
     224             : 
     225           0 :     if( !perfTest )
     226             :     {
     227             :         // check contents of store (not all to save time on bigger tests)
     228           0 :         for( uint64_t j = 0; j < wOps && clock.getTime64() < loopTime; ++j )
     229             :         {
     230           0 :             const std::string& val = map[ keys[ j % (queueDepth+1) ]];
     231           0 :             TESTINFO( val.size() == valueSize,
     232           0 :                       val.size() << " != " << valueSize );
     233           0 :             TEST( val == value );
     234           0 :         }
     235             :     }
     236             : 
     237             :     // try to make sure there's nothing outstanding if we messed up in our test
     238           0 :     map.flush();
     239           0 : }
     240             : 
     241           1 : void testGenericFailures()
     242             : {
     243             :     try
     244             :     {
     245           2 :         setup( "foobar://" );
     246             :     }
     247           2 :     catch( const std::runtime_error& )
     248             :     {
     249           2 :         return;
     250             :     }
     251           0 :     TESTINFO( false, "Missing exception" );
     252             : }
     253             : 
     254           1 : void testLevelDBFailures()
     255             : {
     256             : #ifdef LUNCHBOX_USE_LEVELDB
     257             :     try
     258             :     {
     259           2 :         setup( "leveldb:///doesnotexist/deadbeef/coffee" );
     260             :     }
     261           2 :     catch( const std::runtime_error& )
     262             :     {
     263           2 :         return;
     264             :     }
     265           0 :     TESTINFO( false, "Missing exception" );
     266             : #endif
     267             : }
     268             : 
     269           1 : int main( int, char* argv[] )
     270             : {
     271           1 :     perfTest = std::string( argv[0] ).find( "perf-" ) != std::string::npos;
     272           1 :     if( perfTest )
     273             :         std::cout
     274           0 :             << " async,  value,   reads/s,  writes/s, read MB/s, write MB/s"
     275           0 :             << std::endl;
     276             :     try
     277             :     {
     278             : #ifdef LUNCHBOX_USE_LEVELDB
     279           1 :         setup( "" );
     280           1 :         setup( "leveldb://" );
     281           1 :         setup( "leveldb://persistentMap2.leveldb" );
     282           1 :         read( "" );
     283           1 :         read( "leveldb://" );
     284           1 :         read( "leveldb://persistentMap2.leveldb" );
     285           1 :         if( perfTest )
     286           0 :             for( size_t i=1; i <= 65536; i = i<<2 )
     287           0 :                 benchmark( "leveldb://", 0, i );
     288             : #endif
     289             : #ifdef LUNCHBOX_USE_SKV
     290             :         FxLogger_Init( argv[0] );
     291             :         setup( "skv://" );
     292             :         read( "skv://" );
     293             :         if( perfTest )
     294             :         {
     295             :             benchmark( "skv://", 0, 64 );
     296             :             for( size_t i=1; i <= 65536; i = i<<1 )
     297             :                 benchmark( "skv://", i, 64 );
     298             :             for( size_t i=1; i <= 65536; i = i<<2 )
     299             :                 benchmark( "skv://", 65536, i );
     300             :         }
     301             : #endif
     302             :     }
     303             : #ifdef LUNCHBOX_USE_LEVELDB
     304           0 :     catch( const leveldb::Status& status )
     305             :     {
     306           0 :         TESTINFO( !"exception", status.ToString( ));
     307             :     }
     308             : #endif
     309           0 :     catch( const std::runtime_error& error )
     310             :     {
     311             : #ifdef LUNCHBOX_USE_SKV
     312             :         if( error.what() !=
     313             :             std::string( "skv init failed: SKV_ERRNO_CONN_FAILED" ))
     314             : #endif
     315             :         {
     316           0 :             TESTINFO( !"exception", error.what( ));
     317             :         }
     318             :     }
     319             : 
     320           1 :     testGenericFailures();
     321           1 :     testLevelDBFailures();
     322             : 
     323           1 :     return EXIT_SUCCESS;
     324           3 : }

Generated by: LCOV version 1.11