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 : }
|