2 * Copyright (c) 2006 Teodor Sigaev <teodor@sigaev.ru>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the author nor the names of any co-contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY CONTRIBUTORS ``AS IS'' AND ANY EXPRESS
18 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
23 * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
26 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
45 typedef struct RDBMSDesc {
49 ftsDB* (*init)(char *);
52 static RDBMSDesc DBDesc[] = {
53 { PostgreSQL, "pgsql", "PostgreSQL", PGInit },
54 { MySQL, "mysql", "MySQL", MYInit },
55 { NULLSQL, NULL, NULL, NULL }
64 for(i=0; DBDesc[i].rdbms != NULLSQL; i++) {
65 if ( DBDesc[i].init == NULL )
69 strcat(buf, DBDesc[i].shortname);
71 strcat(buf, "(default)");
76 "ftsbench - full text search benchmark ofr RDBMS\n"
77 "Initialization of DB:\n"
78 "\tftsbench -i [-b RDBMS] [-n NUMROW] [-l LEXFILE] [-g GAMMAFILE] [-f FLAGS] [-q] -d DBNAME\n"
79 "FLAGS are comma-separate list of:\n"
80 " gin - use GIN index\n"
81 " gist - use GiST index\n"
82 " func - use functional index\n",
87 "\tftsbench [-b RDBMS] [-c NCLIENTS] [-n NUMQUERY] [-l LEXFILE] [-g GAMMAFILE] [-f FLAGS] [-q] -d DBNAME\n"
88 "FLAGS are comma-separate list of:\n"
89 " and - AND'ing lexemes in query (default)\n"
90 " or - OR'ing lexemes in query\n"
91 " sort - sort result of query\n"
93 " -b RDBMS\t- type of DB: ",
99 " -l LEXFILE\t- file with words and its frequents\n"
100 " -g GAMMAFILE\t- file with doc's length distribution\n",
107 getRDBMS(char *name) {
110 for(i=0; DBDesc[i].rdbms != NULLSQL; i++) {
111 if ( name == NULL ) {
112 if ( DBDesc[i].init )
113 return DBDesc[i].rdbms;
114 } else if ( strcasecmp(name,DBDesc[i].shortname) == 0 ) {
115 if ( DBDesc[i].init == NULL ) {
116 fprintf(stderr,"Support of '%s' isn't compiled-in\n", DBDesc[i].longname);
119 return DBDesc[i].rdbms;
123 fprintf(stderr,"Can't find a RDBMS\n");
130 getFLAGS(char *flg) {
133 if ( strcasestr(flg,"gist") )
135 if ( strcasestr(flg,"gin") )
137 if ( strcasestr(flg,"func") )
139 if ( strcasestr(flg,"and") )
141 if ( strcasestr(flg,"or") )
143 if ( strcasestr(flg,"sort") )
146 if ( (flags & FLG_GIST) && (flags & FLG_GIN) ) {
147 fprintf(stderr,"GIN and GiST flags are mutually exclusive\n");
150 if ( (flags & FLG_AND) && (flags & FLG_OR) ) {
151 fprintf(stderr,"AND and OR flags are mutually exclusive\n");
159 initConnections(RDBMS rdbms, int n, char *connstr) {
160 ftsDB **dbs = (ftsDB**)malloc(sizeof(ftsDB*) * n);
164 fprintf(stderr,"Not enough mwmory\n");
169 dbs[i] = DBDesc[rdbms].init(connstr);
170 pthread_mutex_init(&dbs[i]->nqueryMutex, NULL);
177 timediff(struct timeval *begin, struct timeval *end) {
178 return ((double)( end->tv_sec - begin->tv_sec )) + ( (double)( end->tv_usec-begin->tv_usec ) ) / 1.0e+6;
182 elapsedtime(struct timeval *begin) {
184 gettimeofday(&end,NULL);
185 return timediff(begin,&end);
188 static int benchFlags = 0;
189 static int benchCount = 0;
190 static pthread_cond_t condFinish = PTHREAD_COND_INITIALIZER;
191 static pthread_mutex_t mutexFinish = PTHREAD_MUTEX_INITIALIZER;
192 static pthread_mutex_t mutexWordGen = PTHREAD_MUTEX_INITIALIZER;
195 execBench(void *in) {
196 ftsDB *db = (ftsDB*)in;
200 for(i=0;i<benchCount;i++) {
202 * generate_querywords() isn't a thread safe
204 pthread_mutex_lock( &mutexWordGen );
205 words = generate_querywords();
206 pthread_mutex_unlock( &mutexWordGen );
208 db->execQuery(db, words, benchFlags);
213 * send message about exitting
215 pthread_mutex_lock( &mutexFinish );
216 pthread_cond_broadcast( &condFinish );
217 pthread_mutex_unlock( &mutexFinish );
225 main(int argn, char *argv[]) {
227 int n = 0, nclients = 1;
231 RDBMS rdbms = NULLSQL;
235 StringBuf b = {NULL,0,0};
237 while((i=getopt(argn,argv,"ib:n:l:g:d:c:hf:q")) != EOF) {
239 case 'i': initMode = 1; break;
240 case 'b': rdbms = getRDBMS(optarg); break;
241 case 'n': n=atoi(optarg); break;
242 case 'c': nclients=atoi(optarg); break;
243 case 'l': lex = strdup(optarg); break;
244 case 'g': doc = strdup(optarg); break;
245 case 'd': dbname = strdup(optarg); break;
246 case 'f': flags = getFLAGS(optarg); break;
247 case 'q': quiet = 1; break;
254 if (rdbms == NULLSQL)
255 rdbms = getRDBMS(NULL);
257 if ( dbname == NULL || n<0 || nclients<1 )
260 printf("Running with '%s' RDBMS\n", DBDesc[ rdbms ].longname);
263 ftsDB *db = *initConnections(rdbms, 1, dbname);
266 if (!lex) lex = "gendata/lex";
267 if (!doc) doc = "gendata/gamma-lens";
268 finnegan_init(lex, doc);
270 db->startCreateScheme(db, flags);
274 db->InsertRow(db, i+1, b.str);
275 if ( !quiet && prev!=time(NULL) ) {
276 printf("\r%d(%.02f%%) rows inserted", i, (100.0*i)/n);
281 printf("%s%d(100.00%%) rows inserted. Finalyze insertion... ",
282 (quiet) ? "" : "\r", i);
284 db->finishCreateScheme(db);
288 ftsDB **dbs = initConnections(rdbms, nclients, dbname);
289 pthread_t *tid = (pthread_t*)malloc( sizeof(pthread_t) * nclients);
290 struct timeval begin;
293 struct timespec sleepTo = { 0, 0 };
298 if (!lex) lex = "gendata/query-lex";
299 if (!doc) doc = "gendata/query-lens";
300 finnegan_init(lex, doc);
306 printf("\r0(0.00%%) queries proceed");
312 gettimeofday(&begin,NULL);
314 pthread_mutex_lock( &mutexFinish );
315 for(i=0;i<nclients;i++) {
316 if ( pthread_create(tid+i, NULL, execBench, (void*)dbs[i]) != 0 ) {
317 fprintf(stderr,"pthread_create failed: %s\n", strerror(errno));
326 for(i=0;i<nclients;i++) {
327 pthread_mutex_lock(&dbs[i]->nqueryMutex);
328 total +=dbs[i]->nquery;
329 if ( dbs[i]->nquery < n )
331 pthread_mutex_unlock(&dbs[i]->nqueryMutex);
338 printf("\r%d(%.02f%%) queries proceed", total, (100.0*(float)total)/(nclients * n));
342 sleepTo.tv_sec = time(NULL) + 1;
343 res = pthread_cond_timedwait( &condFinish, &mutexFinish, &sleepTo );
345 if ( !(res == ETIMEDOUT || res == 0) ) {
346 fprintf(stderr,"pthread_cond_timedwait failed: %s\n", strerror(errno));
350 elapsed = elapsedtime(&begin);
351 pthread_mutex_unlock( &mutexFinish );
353 for(i=0;i<nclients;i++) {
354 pthread_join(tid[i], NULL);
355 dbs[i]->Close(dbs[i]);
358 printf("%s%d(%.02f%%) queries proceed\n",
359 (quiet) ? "" : "\r", total, (100.0*(float)total)/(nclients * n));
360 printf("Total time: %.02f sec, Queries per second: %.02f\n", elapsed, total/elapsed);