1+ #include <stdio.h>
2+ #include <stdlib.h>
3+ #include <string.h>
4+ #include <time.h>
5+ #include <sys/stat.h>
6+ #include <lmdb.h>
7+
8+ #define N 10000
9+ #define VAL_SIZE 100
10+ #define MAP_SIZE (256UL * 1024 * 1024)
11+
12+ static double now_ms (void ) {
13+ struct timespec ts ;
14+ clock_gettime (CLOCK_MONOTONIC , & ts );
15+ return ts .tv_sec * 1000.0 + ts .tv_nsec / 1.0e6 ;
16+ }
17+
18+ static void check (int rc , const char * msg ) {
19+ if (rc != 0 ) { fprintf (stderr , "%s: %s\n" , msg , mdb_strerror (rc )); exit (1 ); }
20+ }
21+
22+ int main (void ) {
23+ MDB_env * env ; MDB_dbi dbi ; MDB_txn * txn ; MDB_cursor * cursor ;
24+ MDB_val key , data ;
25+ char keybuf [32 ], valbuf [VAL_SIZE ];
26+ double t0 , t1 ;
27+ int rc ;
28+
29+ memset (valbuf , 'x' , VAL_SIZE );
30+
31+ const char * path = "/tmp/bench_c_lmdb" ;
32+ mkdir (path , 0755 );
33+ check (mdb_env_create (& env ), "env_create" );
34+ check (mdb_env_set_mapsize (env , MAP_SIZE ), "set_mapsize" );
35+ check (mdb_env_open (env , path , MDB_NOSYNC | MDB_NOMETASYNC , 0644 ), "env_open" );
36+ check (mdb_txn_begin (env , NULL , 0 , & txn ), "txn_begin" );
37+ check (mdb_dbi_open (txn , NULL , 0 , & dbi ), "dbi_open" );
38+ check (mdb_txn_commit (txn ), "txn_commit" );
39+
40+ printf ("=== C LMDB Benchmark (%d records, %d-byte values) ===\n\n" , N , VAL_SIZE );
41+
42+ /* 1. N writes, 1 txn each */
43+ t0 = now_ms ();
44+ for (int i = 0 ; i < N ; i ++ ) {
45+ int len = snprintf (keybuf , sizeof (keybuf ), "key:%08d" , i );
46+ key .mv_data = keybuf ; key .mv_size = len ;
47+ data .mv_data = valbuf ; data .mv_size = VAL_SIZE ;
48+ check (mdb_txn_begin (env , NULL , 0 , & txn ), "txn_begin" );
49+ check (mdb_put (txn , dbi , & key , & data , 0 ), "put" );
50+ check (mdb_txn_commit (txn ), "txn_commit" );
51+ }
52+ t1 = now_ms ();
53+ printf ("1. Write (1 txn each): %8.1f ms (%7.0f ops/s)\n" ,
54+ t1 - t0 , N /((t1 - t0 )/1000.0 ));
55+
56+ /* 2. N writes, 1 txn total */
57+ check (mdb_txn_begin (env , NULL , 0 , & txn ), "txn_begin" );
58+ check (mdb_drop (txn , dbi , 0 ), "drop" );
59+ check (mdb_txn_commit (txn ), "txn_commit" );
60+
61+ t0 = now_ms ();
62+ check (mdb_txn_begin (env , NULL , 0 , & txn ), "txn_begin" );
63+ for (int i = 0 ; i < N ; i ++ ) {
64+ int len = snprintf (keybuf , sizeof (keybuf ), "key:%08d" , i );
65+ key .mv_data = keybuf ; key .mv_size = len ;
66+ data .mv_data = valbuf ; data .mv_size = VAL_SIZE ;
67+ check (mdb_put (txn , dbi , & key , & data , 0 ), "put" );
68+ }
69+ check (mdb_txn_commit (txn ), "txn_commit" );
70+ t1 = now_ms ();
71+ printf ("2. Write (1 txn total): %8.1f ms (%7.0f ops/s)\n" ,
72+ t1 - t0 , N /((t1 - t0 )/1000.0 ));
73+
74+ /* 3. N reads, 1 txn each */
75+ t0 = now_ms ();
76+ for (int i = 0 ; i < N ; i ++ ) {
77+ int len = snprintf (keybuf , sizeof (keybuf ), "key:%08d" , i );
78+ key .mv_data = keybuf ; key .mv_size = len ;
79+ check (mdb_txn_begin (env , NULL , MDB_RDONLY , & txn ), "txn_begin" );
80+ mdb_get (txn , dbi , & key , & data );
81+ mdb_txn_abort (txn );
82+ }
83+ t1 = now_ms ();
84+ printf ("3. Read (1 txn each): %8.1f ms (%7.0f ops/s)\n" ,
85+ t1 - t0 , N /((t1 - t0 )/1000.0 ));
86+
87+ /* 4. N reads, 1 txn total (cursor scan) */
88+ int count = 0 ;
89+ t0 = now_ms ();
90+ check (mdb_txn_begin (env , NULL , MDB_RDONLY , & txn ), "txn_begin" );
91+ check (mdb_cursor_open (txn , dbi , & cursor ), "cursor_open" );
92+ while (mdb_cursor_get (cursor , & key , & data , count == 0 ? MDB_FIRST : MDB_NEXT ) == 0 )
93+ count ++ ;
94+ mdb_cursor_close (cursor );
95+ mdb_txn_abort (txn );
96+ t1 = now_ms ();
97+ printf ("4. Read (1 txn total): %8.1f ms (%7.0f ops/s)\n" ,
98+ t1 - t0 , count /((t1 - t0 )/1000.0 ));
99+
100+ /* 5. Prefix scan */
101+ check (mdb_txn_begin (env , NULL , 0 , & txn ), "txn_begin" );
102+ for (int i = 0 ; i < N ; i ++ ) {
103+ int len = snprintf (keybuf , sizeof (keybuf ), "user:%06d" , i );
104+ key .mv_data = keybuf ; key .mv_size = len ;
105+ data .mv_data = valbuf ; data .mv_size = VAL_SIZE ;
106+ mdb_put (txn , dbi , & key , & data , 0 );
107+ }
108+ check (mdb_txn_commit (txn ), "txn_commit" );
109+
110+ const char * prefix = "user:" ;
111+ size_t plen = strlen (prefix );
112+ count = 0 ;
113+ t0 = now_ms ();
114+ check (mdb_txn_begin (env , NULL , MDB_RDONLY , & txn ), "txn_begin" );
115+ check (mdb_cursor_open (txn , dbi , & cursor ), "cursor_open" );
116+ key .mv_data = (void * )prefix ; key .mv_size = plen ;
117+ rc = mdb_cursor_get (cursor , & key , & data , MDB_SET_RANGE );
118+ while (rc == 0 ) {
119+ if (key .mv_size < plen || memcmp (key .mv_data , prefix , plen ) != 0 ) break ;
120+ count ++ ;
121+ rc = mdb_cursor_get (cursor , & key , & data , MDB_NEXT );
122+ }
123+ mdb_cursor_close (cursor );
124+ mdb_txn_abort (txn );
125+ t1 = now_ms ();
126+ printf ("5. Prefix scan (%d): %8.1f ms (%7.0f ops/s)\n" ,
127+ count , t1 - t0 , count /((t1 - t0 )/1000.0 ));
128+
129+ mdb_env_close (env );
130+ system ("rm -rf /tmp/bench_c_lmdb" );
131+ return 0 ;
132+ }
0 commit comments