1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2001,2008 Oracle.  All rights reserved.
5 *
6 * $Id: bench_001.c,v 12.9 2008/01/08 20:58:23 bostic Exp $
7 */
8
9/*
10 * bench_001 - time bulk fetch interface.
11 *	Without -R builds a btree acording to the arguments.
12 *	With -R runs and times bulk fetches.  If -d is specified
13 *	during reads the DB_MULTIPLE interface is used
14 *	otherwise the DB_MULTIPLE_KEY interface is used.
15 *
16 * ARGUMENTS:
17 *	-c	cachesize [1000 * pagesize]
18 *	-d	number of duplicates [none]
19 *	-E	don't use environment
20 *	-I	Just initialize the environment
21 *	-i	number of read iterations [1000000]
22 *	-l	length of data item [20]
23 *	-n	number of keys [1000000]
24 *	-p	pagesize [65536]
25 *	-R	perform read test.
26 *	-T	incorporate transactions.
27 *
28 * COMPILE:
29 *	cc -I /usr/local/BerkeleyDB/include \
30 *	    -o bench_001 -O2 bench_001.c /usr/local/BerkeleyDB/lib/libdb.so
31 */
32#include <sys/types.h>
33#include <sys/time.h>
34
35#include <stdlib.h>
36#include <string.h>
37
38#ifdef _WIN32
39extern int getopt(int, char * const *, const char *);
40#else
41#include <unistd.h>
42#endif
43
44#include <db.h>
45
46#define	DATABASE	"bench_001.db"
47
48int	compare_int(DB *, const DBT *, const DBT *);
49DB_ENV *db_init(char *, char *, u_int, int);
50int	fill(DB_ENV *, DB *, int, u_int, int, int);
51int	get(DB *, int, u_int, int, int, int, int *);
52int	main(int, char *[]);
53void	usage(void);
54
55const char
56	*progname = "bench_001";		/* Program name. */
57/*
58 * db_init --
59 *	Initialize the environment.
60 */
61DB_ENV *
62db_init(home, prefix, cachesize, txn)
63	char *home, *prefix;
64	u_int cachesize;
65	int txn;
66{
67	DB_ENV *dbenv;
68	u_int32_t flags;
69	int ret;
70
71	if ((ret = db_env_create(&dbenv, 0)) != 0) {
72		dbenv->err(dbenv, ret, "db_env_create");
73		return (NULL);
74	}
75	dbenv->set_errfile(dbenv, stderr);
76	dbenv->set_errpfx(dbenv, prefix);
77	(void)dbenv->set_cachesize(dbenv, 0,
78	    cachesize == 0 ? 50 * 1024 * 1024 : (u_int32_t)cachesize, 0);
79
80	flags = DB_CREATE | DB_INIT_MPOOL;
81	if (txn)
82		flags |= DB_INIT_TXN | DB_INIT_LOCK;
83	if ((ret = dbenv->open(dbenv, home, flags, 0)) != 0) {
84		dbenv->err(dbenv, ret, "DB_ENV->open: %s", home);
85		(void)dbenv->close(dbenv, 0);
86		return (NULL);
87	}
88	return (dbenv);
89}
90
91/*
92 * get -- loop getting batches of records.
93 *
94 */
95int
96get(dbp, txn, datalen, num, dups, iter, countp)
97	DB *dbp;
98	u_int datalen;
99	int txn, num, dups, iter, *countp;
100{
101	DBC *dbcp;
102	DBT key, data;
103	DB_ENV *dbenv;
104	DB_TXN *txnp;
105	u_int32_t flags, len, klen;
106	int count, i, j, ret;
107	void *pointer, *dp, *kp;
108
109	dbenv = dbp->dbenv;
110
111	klen = 0;				/* Lint. */
112	klen = klen;
113
114	memset(&key, 0, sizeof(key));
115	key.data = &j;
116	key.size = sizeof(j);
117	memset(&data, 0, sizeof(data));
118	data.flags = DB_DBT_USERMEM;
119	data.data = malloc(datalen*1024*1024);
120	data.ulen = data.size = datalen*1024*1024;
121
122	count = 0;
123	flags = DB_SET;
124	if (!dups)
125		flags |= DB_MULTIPLE_KEY;
126	else
127		flags |= DB_MULTIPLE;
128	for (i = 0; i < iter; i++) {
129		txnp = NULL;
130		if (txn)
131			if ((ret =
132			    dbenv->txn_begin(dbenv, NULL, &txnp, 0)) != 0)
133				goto err;
134		if ((ret = dbp->cursor(dbp, txnp, &dbcp, 0)) != 0)
135			goto err;
136
137		j = random() % num;
138		if ((ret = dbcp->get(dbcp, &key, &data, flags)) != 0)
139			goto err;
140		DB_MULTIPLE_INIT(pointer, &data);
141		if (dups)
142			while (pointer != NULL) {
143				DB_MULTIPLE_NEXT(pointer, &data, dp, len);
144				if (dp != NULL)
145					count++;
146			}
147		else
148			while (pointer != NULL) {
149				DB_MULTIPLE_KEY_NEXT(pointer,
150				    &data, kp, klen, dp, len);
151				if (kp != NULL)
152					count++;
153			}
154		if ((ret = dbcp->close(dbcp)) != 0)
155			goto err;
156		if (txn)
157			if ((ret = txnp->commit(txnp, 0)) != 0)
158				goto err;
159	}
160
161	*countp = count;
162	return (0);
163
164err:	dbp->err(dbp, ret, "get");
165	return (ret);
166}
167
168/*
169 * fill - fill a db
170 *	Since we open/created the db with transactions (potentially),
171 * we need to populate it with transactions.  We'll bundle the puts
172 * 10 to a transaction.
173 */
174#define	PUTS_PER_TXN	10
175int
176fill(dbenv, dbp, txn, datalen, num, dups)
177	DB_ENV *dbenv;
178	DB *dbp;
179	u_int datalen;
180	int txn, num, dups;
181{
182	DBT key, data;
183	DB_TXN *txnp;
184	struct data {
185		int id;
186		char str[1];
187	} *data_val;
188	int count, i, ret;
189
190	/*
191	 * Insert records into the database, where the key is the user
192	 * input and the data is the user input in reverse order.
193	 */
194	txnp = NULL;
195	ret = 0;
196	count = 0;
197	memset(&key, 0, sizeof(DBT));
198	memset(&data, 0, sizeof(DBT));
199	key.data = &i;
200	key.size = sizeof(i);
201	data.data = data_val = malloc(datalen);
202	memcpy(data_val->str, "0123456789012345678901234567890123456789",
203	    datalen - sizeof(data_val->id));
204	data.size = datalen;
205	data.flags = DB_DBT_USERMEM;
206
207	for (i = 0; i < num; i++) {
208		if (txn != 0 && i % PUTS_PER_TXN == 0) {
209			if (txnp != NULL) {
210				ret = txnp->commit(txnp, 0);
211				txnp = NULL;
212				if (ret != 0)
213					goto err;
214			}
215			if ((ret =
216			    dbenv->txn_begin(dbenv, NULL, &txnp, 0)) != 0)
217				goto err;
218		}
219		data_val->id = 0;
220		do {
221			switch (ret = dbp->put(dbp, txnp, &key, &data, 0)) {
222			case 0:
223				count++;
224				break;
225			default:
226				dbp->err(dbp, ret, "DB->put");
227				goto err;
228			}
229		} while (++data_val->id < dups);
230	}
231	if (txnp != NULL)
232		ret = txnp->commit(txnp, 0);
233
234	printf("%d\n", count);
235	return (ret);
236
237err:	if (txnp != NULL)
238		(void)txnp->abort(txnp);
239	return (ret);
240}
241
242int
243main(argc, argv)
244	int argc;
245	char *argv[];
246{
247	extern char *optarg;
248	extern int optind;
249	DB *dbp;
250	DB_ENV *dbenv;
251	DB_TXN *txnp;
252	struct timeval start_time, end_time;
253	double secs;
254	u_int cache, datalen, pagesize;
255	int ch, count, dups, env, init, iter, num;
256	int ret, rflag, txn;
257
258	txnp = NULL;
259	datalen = 20;
260	iter = num = 1000000;
261	env = 1;
262	dups = init = rflag = txn = 0;
263
264	pagesize = 65536;
265	cache = 1000 * pagesize;
266
267	while ((ch = getopt(argc, argv, "c:d:EIi:l:n:p:RT")) != EOF)
268		switch (ch) {
269		case 'c':
270			cache = (u_int)atoi(optarg);
271			break;
272		case 'd':
273			dups = atoi(optarg);
274			break;
275		case 'E':
276			env = 0;
277			break;
278		case 'I':
279			init = 1;
280			break;
281		case 'i':
282			iter = atoi(optarg);
283			break;
284		case 'l':
285			datalen = (u_int)atoi(optarg);
286			break;
287		case 'n':
288			num = atoi(optarg);
289			break;
290		case 'p':
291			pagesize = (u_int)atoi(optarg);
292			break;
293		case 'R':
294			rflag = 1;
295			break;
296		case 'T':
297			txn = 1;
298			break;
299		case '?':
300		default:
301			usage();
302		}
303	argc -= optind;
304	argv += optind;
305
306	/* Remove the previous database. */
307	if (!rflag) {
308		if (env)
309			(void)system("rm -rf BENCH_001; mkdir BENCH_001");
310		else
311			(void)unlink(DATABASE);
312	}
313
314	dbenv = NULL;
315	if (env == 1 &&
316	    (dbenv = db_init("BENCH_001", "bench_001", cache, txn)) == NULL)
317		return (-1);
318	if (init)
319		exit(0);
320	/* Create and initialize database object, open the database. */
321	if ((ret = db_create(&dbp, dbenv, 0)) != 0) {
322		fprintf(stderr,
323		    "%s: db_create: %s\n", progname, db_strerror(ret));
324		exit(EXIT_FAILURE);
325	}
326	dbp->set_errfile(dbp, stderr);
327	dbp->set_errpfx(dbp, progname);
328	if ((ret = dbp->set_bt_compare(dbp, compare_int)) != 0) {
329		dbp->err(dbp, ret, "set_bt_compare");
330		goto err;
331	}
332	if ((ret = dbp->set_pagesize(dbp, pagesize)) != 0) {
333		dbp->err(dbp, ret, "set_pagesize");
334		goto err;
335	}
336	if (dups && (ret = dbp->set_flags(dbp, DB_DUP)) != 0) {
337		dbp->err(dbp, ret, "set_flags");
338		goto err;
339	}
340
341	if (env == 0 && (ret = dbp->set_cachesize(dbp, 0, cache, 0)) != 0) {
342		dbp->err(dbp, ret, "set_cachesize");
343		goto err;
344	}
345
346	if ((ret = dbp->set_flags(dbp, DB_DUP)) != 0) {
347		dbp->err(dbp, ret, "set_flags");
348		goto err;
349	}
350
351	if (txn != 0)
352		if ((ret = dbenv->txn_begin(dbenv, NULL, &txnp, 0)) != 0)
353			goto err;
354
355	if ((ret = dbp->open(
356	    dbp, txnp, DATABASE, NULL, DB_BTREE, DB_CREATE, 0664)) != 0) {
357		dbp->err(dbp, ret, "%s: open", DATABASE);
358		if (txnp != NULL)
359			(void)txnp->abort(txnp);
360		goto err;
361	}
362
363	if (txnp != NULL)
364		ret = txnp->commit(txnp, 0);
365	txnp = NULL;
366	if (ret != 0)
367		goto err;
368
369	if (rflag) {
370		/* If no environment, fill the cache. */
371		if (!env && (ret =
372		    get(dbp, txn, datalen, num, dups, iter, &count)) != 0)
373			goto err;
374
375		/* Time the get loop. */
376		(void)gettimeofday(&start_time, NULL);
377		if ((ret =
378		    get(dbp, txn, datalen, num, dups, iter, &count)) != 0)
379			goto err;
380		(void)gettimeofday(&end_time, NULL);
381		secs =
382		    (((double)end_time.tv_sec * 1000000 + end_time.tv_usec) -
383		    ((double)start_time.tv_sec * 1000000 + start_time.tv_usec))
384		    / 1000000;
385		printf("%d records read using %d batches in %.2f seconds: ",
386		    count, iter, secs);
387		printf("%.0f records/second\n", (double)count / secs);
388
389	} else if ((ret = fill(dbenv, dbp, txn, datalen, num, dups)) != 0)
390		goto err;
391
392	/* Close everything down. */
393	if ((ret = dbp->close(dbp, rflag ? DB_NOSYNC : 0)) != 0) {
394		fprintf(stderr,
395		    "%s: DB->close: %s\n", progname, db_strerror(ret));
396		return (1);
397	}
398	return (ret);
399
400err:	(void)dbp->close(dbp, 0);
401	return (1);
402}
403
404int
405compare_int(dbp, a, b)
406	DB *dbp;
407	const DBT *a, *b;
408{
409	int ai, bi;
410
411	dbp = dbp;				/* Lint. */
412
413	/*
414	 * Returns:
415	 *	< 0 if a < b
416	 *	= 0 if a = b
417	 *	> 0 if a > b
418	 */
419	memcpy(&ai, a->data, sizeof(int));
420	memcpy(&bi, b->data, sizeof(int));
421	return (ai - bi);
422}
423
424void
425usage()
426{
427	(void)fprintf(stderr, "usage: %s %s\n\t%s\n",
428	    progname, "[-EIRT] [-c cachesize] [-d dups]",
429	    "[-i iterations] [-l datalen] [-n keys] [-p pagesize]");
430	exit(EXIT_FAILURE);
431}
432