Using timing utility
[idzebra-moved-to-github.git] / isamb / benchindex1.c
1 /* $Id: benchindex1.c,v 1.3 2006-12-11 10:02:14 adam Exp $
2    Copyright (C) 1995-2006
3    Index Data ApS
4
5 This file is part of the Zebra server.
6
7 Zebra is free software; you can redistribute it and/or modify it under
8 the terms of the GNU General Public License as published by the Free
9 Software Foundation; either version 2, or (at your option) any later
10 version.
11
12 Zebra is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20
21 */
22
23 #include <yaz/options.h>
24 #include <ctype.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <yaz/log.h>
28 #include <yaz/nmem.h>
29 #include <yaz/xmalloc.h>
30 #include <yaz/marcdisp.h>
31 #include <it_key.h>
32 #include <idzebra/isamb.h>
33 #include <idzebra/dict.h>
34 #include <assert.h>
35
36 struct index_block {
37     NMEM nmem;
38     int no_entries;
39     size_t current_entry;
40     size_t current_max;
41     struct index_term *terms;
42     struct index_term **ar;
43     int round;
44 };
45
46 struct index_term {
47     const char *term;
48     zint docid;
49     zint seqno;
50     int word_id;
51     struct index_term *next;
52 };
53
54 struct index_block *index_block_new(int memory)
55 {
56     struct index_block *b = xmalloc(sizeof(*b));
57     b->no_entries = 0;
58     b->current_max = memory * 1024 * 1024;
59     b->terms = 0;
60     b->nmem = nmem_create();
61     b->round = 0;
62     return b;
63 }
64
65 void index_block_destroy(struct index_block **bp)
66 {
67     if (*bp)
68     {
69         nmem_destroy((*bp)->nmem);
70         xfree(*bp);
71         *bp = 0;
72     }
73 }
74
75 static int cmp_ar(const void *p1, const void *p2)
76 {
77     struct index_term *t1 = *(struct index_term **) p1;
78     struct index_term *t2 = *(struct index_term **) p2;
79     int d = strcmp(t1->term, t2->term);
80     if (d)
81         return d;
82
83     if (t1->docid > t2->docid)
84         return 1;
85     else if (t1->docid < t2->docid)
86         return -1;
87     if (t1->seqno > t2->seqno)
88         return 1;
89     else if (t1->seqno < t2->seqno)
90         return -1;
91     return 0;
92 }
93
94
95 int code_read(void *vp, char **dst, int *insertMode)
96 {
97     struct index_block *b = (struct index_block *)vp;
98     struct index_term *t;
99     struct it_key key;
100
101     if (b->current_entry >= b->no_entries)
102         return 0;
103     
104     t = b->ar[b->current_entry];
105     b->current_entry++;
106     
107     key.len = 3;
108     key.mem[0] = t->word_id;
109     key.mem[1] = t->docid;
110     key.mem[2] = t->seqno;
111     key.mem[3] = 0;
112
113     memcpy(*dst, &key, sizeof(key));
114
115     (*dst) += sizeof(key);
116     *insertMode = 1;
117 #if 0
118     yaz_log(YLOG_LOG, "returning " ZINT_FORMAT " " ZINT_FORMAT "\n",
119             key.mem[0], key.mem[1]);
120 #endif
121     return 1;
122 }
123
124 void index_block_flush(struct index_block *b, ISAMB isb, Dict dict,
125                        int no_docs)
126 {
127     struct index_term *t = b->terms;
128     int i;
129     int word_id_seq = 0;
130     int no_words = 0, no_new_words = 0;
131     const char *dict_info = 0;
132     ISAM_P isamc_p = 0;
133     zebra_timing_t tim = zebra_timing_create();
134     
135     b->ar = xmalloc(sizeof(*b->ar) * b->no_entries);
136     for (i = 0; i < b->no_entries; i++, t = t->next)
137     {
138         assert(t);
139         b->ar[i] = t;
140     }
141     assert(!t);
142     
143     qsort(b->ar, b->no_entries, sizeof(*b->ar), cmp_ar);
144 #if 0
145     for (i = 0; i < b->no_entries; i++)
146     {
147         printf("%s " ZINT_FORMAT " " ZINT_FORMAT "\n",
148                ar[i]->term, ar[i]->docid, ar[i]->seqno);
149     }
150 #endif
151     dict_info = dict_lookup(dict, "_w");
152     if (dict_info)
153     {
154         assert(*dict_info == sizeof(word_id_seq));
155         memcpy(&word_id_seq, dict_info+1, sizeof(word_id_seq));
156     }
157
158     dict_info = dict_lookup(dict, "_i");
159     if (dict_info)
160     {
161         assert(*dict_info == sizeof(isamc_p));
162         memcpy(&isamc_p, dict_info+1, sizeof(isamc_p));
163     }
164
165     for (i = 0; i < b->no_entries; i++)
166     {
167         if (i > 0 && strcmp(b->ar[i-1]->term, b->ar[i]->term) == 0)
168             b->ar[i]->word_id = b->ar[i-1]->word_id;
169         else
170         {
171             const char *dict_info = dict_lookup(dict, b->ar[i]->term);
172             if (dict_info)
173             {
174                 memcpy(&b->ar[i]->word_id, dict_info+1, sizeof(int));
175             }
176             else
177             {
178                 word_id_seq++;
179                 no_new_words++;
180                 dict_insert(dict, b->ar[i]->term, sizeof(int), &word_id_seq);
181                 b->ar[i]->word_id = word_id_seq;
182             }
183             no_words++;
184         }
185     }
186     dict_insert(dict, "_w", sizeof(word_id_seq), &word_id_seq);
187
188     b->current_entry = 0;
189
190     if (b->no_entries)
191     {
192         ISAMC_I isamc_i;
193         
194         isamc_i.clientData = b;
195         isamc_i.read_item = code_read;
196
197         isamb_merge (isb, &isamc_p, &isamc_i);
198
199         assert(isamc_p);
200         dict_insert(dict, "_i", sizeof(isamc_p), &isamc_p);
201     }
202
203     yaz_log(YLOG_LOG, "Flushed %d postings, %d/%d words, %d records",
204             b->no_entries, no_words, no_new_words, no_docs);
205     xfree(b->ar);
206     b->ar = 0;
207     nmem_reset(b->nmem);
208     b->no_entries = 0;
209     b->terms = 0;
210
211     zebra_timing_stop(tim);
212
213     printf("%3d %8.6f %5.2f %5.2f\n",
214            b->round,
215            zebra_timing_get_real(tim),
216            zebra_timing_get_user(tim),
217            zebra_timing_get_sys(tim));
218     zebra_timing_destroy(&tim);
219 }
220
221 void index_block_check_flush(struct index_block *b, ISAMB isb, Dict dict,
222                              int no_docs)
223 {
224     int total = nmem_total(b->nmem);
225     int max = b->current_max;
226     if (total > max)
227     {
228         yaz_log(YLOG_LOG, "flush to disk total=%d max=%d", total, max);
229         index_block_flush(b, isb, dict, no_docs);
230     }
231 }
232
233 void index_block_add(struct index_block *b,
234                      const char *term, zint docid, zint seqno)
235 {
236     struct index_term *t = nmem_malloc(b->nmem, sizeof(*t));
237     t->term = nmem_strdup(b->nmem, term);
238     t->docid = docid;
239     t->seqno = seqno;
240     t->next = b->terms;
241     b->terms = t;
242     b->no_entries++;
243 }
244
245 void exit_usage(void)
246 {
247     fprintf(stderr, "benchindex1 [-m mem] [-i] [iso2709file]\n");
248     exit(1);
249 }
250
251 void index_term(struct index_block *b, const char *term,
252                 zint docid, zint *seqno)
253 {
254 #if 0
255     printf("%s " ZINT_FORMAT " " ZINT_FORMAT "\n", term,
256            docid, *seqno);
257 #endif
258     index_block_add(b, term, docid, *seqno);
259     (*seqno)++;
260 }
261
262 void index_wrbuf(struct index_block *b, WRBUF wrbuf, zint docid)
263 {
264     int nl = 1;
265     const char *cp = wrbuf_buf(wrbuf);
266     char term[4096];
267     size_t sz = 0;
268     zint seqno = 0;
269
270     while (*cp)
271     {
272         if (nl)
273         {
274             int i;
275             for (i = 0; i<6 && *cp; i++, cp++)
276                 ;
277         }
278         nl = 0;
279         if (*cp == '\n')
280         {
281             if (sz)
282             {
283                 index_term(b, term, docid, &seqno);
284                 sz = 0;
285             }
286             nl = 1;
287             cp++;
288         }
289         else if (*cp == '$' && cp[1])
290         {
291             if (sz)
292             {
293                 index_term(b, term, docid, &seqno);
294                 sz = 0;
295             }
296             cp += 2;
297         }
298         else if (strchr("$/-;,.:[]\"&(){} ", *cp))
299         {
300             if (sz)
301             {
302                 index_term(b, term, docid, &seqno);
303                 sz = 0;
304             }
305             cp++;
306         }
307         else
308         {
309             unsigned ch = *(const unsigned char *)cp;
310             if (sz < sizeof(term))
311             {
312                 term[sz] = tolower(ch);
313                 term[sz+1] = '\0';
314                 sz++;
315             }
316             cp++;
317         }            
318     }
319     if (sz)
320         index_term(b, term, docid, &seqno);
321 }
322
323 void index_marc_from_file(ISAMB isb,
324                           Dict dict,
325                           FILE *inf,
326                           int memory,
327                           int verbose, int print_offset)
328 {
329     yaz_marc_t mt = yaz_marc_create();
330     WRBUF wrbuf = wrbuf_alloc();
331     struct index_block *b = index_block_new(memory);
332     const char *dict_info = 0;
333     zint docid_seq = 1;
334     int no_docs = 0;
335
336     dict_info = dict_lookup(dict, "_s");
337     if (dict_info)
338     {
339         assert(*dict_info == sizeof(docid_seq));
340         memcpy(&docid_seq, dict_info+1, sizeof(docid_seq));
341     }
342
343     while (1)
344     {
345         size_t r;
346         char buf[100001];
347         int len, rlen;
348
349         r = fread (buf, 1, 5, inf);
350         if (r < 5)
351         {
352             if (r && print_offset && verbose)
353                 printf ("<!-- Extra %ld bytes at end of file -->\n",
354                         (long) r);
355             break;
356         }
357         while (*buf < '0' || *buf > '9')
358         {
359             int i;
360             long off = ftell(inf) - 5;
361             if (verbose || print_offset)
362                 printf("<!-- Skipping bad byte %d (0x%02X) at offset "
363                        "%ld (0x%lx) -->\n", 
364                        *buf & 0xff, *buf & 0xff,
365                        off, off);
366             for (i = 0; i<4; i++)
367                 buf[i] = buf[i+1];
368             r = fread(buf+4, 1, 1, inf);
369             if (r < 1)
370                 break;
371         }
372         if (r < 1)
373         {
374             if (verbose || print_offset)
375                 printf ("<!-- End of file with data -->\n");
376             break;
377         }
378         len = atoi_n(buf, 5);
379         if (len < 25 || len > 100000)
380         {
381             long off = ftell(inf) - 5;
382             printf("Bad Length %ld read at offset %ld (%lx)\n",
383                    (long)len, (long) off, (long) off);
384             break;
385         }
386         rlen = len - 5;
387         r = fread (buf + 5, 1, rlen, inf);
388         if (r < rlen)
389             break;
390         yaz_marc_read_iso2709(mt, buf, len);
391         
392         if (yaz_marc_write_line(mt, wrbuf))
393             break;
394
395         index_wrbuf(b, wrbuf, docid_seq);
396         wrbuf_rewind(wrbuf);
397         docid_seq++;
398
399         no_docs++;
400         index_block_check_flush(b, isb, dict, no_docs);
401     }
402     index_block_flush(b, isb, dict, no_docs);
403     wrbuf_free(wrbuf, 1);
404     yaz_marc_destroy(mt);
405     index_block_destroy(&b);
406     yaz_log(YLOG_LOG, "Total " ZINT_FORMAT " documents", docid_seq);
407     dict_insert(dict, "_s", sizeof(docid_seq), &docid_seq);
408 }
409
410 int main(int argc, char **argv)
411 {
412     BFiles bfs;
413     ISAMB isb;
414     ISAMC_M method;
415     Dict dict;
416     int ret;
417     int reset = 0;
418     char *arg;
419     int memory = 5;
420     const char *fname = 0;
421     FILE *inf = stdin;
422     zebra_timing_t tim = 0;
423
424     while ((ret = options("im:", argv, argc, &arg)) != -2)
425     {
426         switch(ret)
427         {
428         case 'm':
429             memory = atoi(arg);
430             break;
431         case 'i':
432             reset = 1;
433             break;
434         case 0:
435             fname = arg;
436             break;
437         default:
438             fprintf(stderr, "bad option.\n");
439             exit_usage();
440         }
441     }
442         
443     if (fname)
444     {
445         inf = fopen(fname, "rb");
446         if (!inf)
447         {
448             fprintf(stderr, "Cannot open %s\n", fname);
449             exit(1);
450         }
451     }
452     /* setup method (attributes) */
453     method.compare_item = key_compare;
454     method.log_item = key_logdump_txt;
455
456     method.codec.start = iscz1_start;
457     method.codec.decode = iscz1_decode;
458     method.codec.encode = iscz1_encode;
459     method.codec.stop = iscz1_stop;
460     method.codec.reset = iscz1_reset;
461
462     method.debug = 0;
463
464     /* create block system */
465     bfs = bfs_create(0, 0);
466     if (!bfs)
467     {
468         yaz_log(YLOG_WARN, "bfs_create failed");
469         exit(1);
470     }
471
472     if (reset)
473         bf_reset(bfs);
474
475     tim = zebra_timing_create();
476     /* create isam handle */
477     isb = isamb_open (bfs, "isamb", 1, &method, 0);
478     if (!isb)
479     {
480         yaz_log(YLOG_WARN, "isamb_open failed");
481         exit(2);
482     }
483     dict = dict_open(bfs, "dict", 50, 1, 0, 4096);
484
485     index_marc_from_file(isb, dict, inf, memory, 
486                          0 /* verbose */ , 0 /* print_offset */);
487
488     dict_close(dict);
489     isamb_close(isb);
490
491     if (fname)
492         fclose(inf);
493     /* exit block system */
494     bfs_destroy(bfs);
495     zebra_timing_stop(tim);
496
497     yaz_log(YLOG_LOG, "Total %8.6f %5.2f %5.2f\n",
498             zebra_timing_get_real(tim),
499             zebra_timing_get_user(tim),
500             zebra_timing_get_sys(tim));
501     
502     zebra_timing_destroy(&tim);
503
504     exit(0);
505     return 0;
506 }
507 /*
508  * Local variables:
509  * c-basic-offset: 4
510  * indent-tabs-mode: nil
511  * End:
512  * vim: shiftwidth=4 tabstop=8 expandtab
513  */
514