MySQL 5.6.14 Source Code Document
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
testScanPerf.cpp
1 /*
2  Copyright (C) 2004-2006, 2008 MySQL AB, 2008, 2009 Sun Microsystems, Inc.
3  All rights reserved. Use is subject to license terms.
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; version 2 of the License.
8 
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  GNU General Public License for more details.
13 
14  You should have received a copy of the GNU General Public License
15  along with this program; if not, write to the Free Software
16  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
17 */
18 
19 #include <NDBT.hpp>
20 #include <NDBT_Test.hpp>
21 #include <HugoTransactions.hpp>
22 #include <UtilTransactions.hpp>
23 #include <random.h>
24 #include <getarg.h>
25 
26 struct Parameter {
27  const char * name;
28  unsigned value;
29  unsigned min;
30  unsigned max;
31 };
32 
33 #define P_BATCH 0
34 #define P_PARRA 1
35 #define P_LOCK 2
36 #define P_FILT 3
37 #define P_BOUND 4
38 #define P_ACCESS 5
39 #define P_FETCH 6
40 #define P_ROWS 7
41 #define P_LOOPS 8
42 #define P_CREATE 9
43 #define P_MULTI 11
44 
45 #define P_MAX 12
46 
47 /* Note that this tool can only be run against Hugo tables with an integer
48  * primary key
49  */
50 
51 static
52 Parameter
53 g_paramters[] = {
54  { "batch", 0, 0, 1 }, // 0, 15
55  { "parallelism", 0, 0, 1 }, // 0, 1
56  { "lock", 0, 0, 2 }, // read, exclusive, dirty
57  { "filter", 0, 0, 3 }, // Use ScanFilter to return : all, none, 1, 100
58  { "range", 0, 0, 3 }, // Use IndexBounds to return : all, none, 1, 100
59  // For range==3, Multiple index scans are used with a number of ranges specified
60  // per scan (Number is defined by multi read range.
61  { "access", 0, 0, 2 }, // Table, Index or Ordered Index scan
62  { "fetch", 0, 0, 1 }, // nextResult fetchAllowed. No, yes
63  { "size", 1000000, 1, ~0 }, // Num rows to operate on
64  { "iterations", 3, 1, ~0 }, // Num times to repeat tests
65  { "create_drop", 1, 0, 2 }, // Whether to recreate the table
66  { "data", 1, 0, 1 }, // Ignored currently
67  { "multi read range", 1000, 1, ~0 } // Number of ranges to use in MRR access (range=3)
68 };
69 
70 static Ndb* g_ndb = 0;
71 static const NdbDictionary::Table * g_table;
72 static const NdbDictionary::Index * g_index;
73 static char g_tablename[256];
74 static char g_indexname[256];
75 static const NdbRecord * g_table_record;
76 static const NdbRecord * g_index_record;
77 
78 int create_table();
79 int run_scan();
80 
81 int
82 main(int argc, const char** argv){
83  ndb_init();
84  int verbose = 1;
85  int optind = 0;
86 
87  struct getargs args[1+P_MAX] = {
88  { "verbose", 'v', arg_flag, &verbose, "Print verbose status", "verbose" }
89  };
90  const int num_args = 1 + P_MAX;
91  int i;
92  for(i = 0; i<P_MAX; i++){
93  args[i+1].long_name = g_paramters[i].name;
94  args[i+1].short_name = * g_paramters[i].name;
95  args[i+1].type = arg_integer;
96  args[i+1].value = &g_paramters[i].value;
97  BaseString tmp;
98  tmp.assfmt("min: %d max: %d", g_paramters[i].min, g_paramters[i].max);
99  args[i+1].help = strdup(tmp.c_str());
100  args[i+1].arg_help = 0;
101  }
102 
103  if(getarg(args, num_args, argc, argv, &optind)) {
104  arg_printusage(args, num_args, argv[0], "tabname1 tabname2 ...");
105  return NDBT_WRONGARGS;
106  }
107 
108  myRandom48Init((long)NdbTick_CurrentMillisecond());
109 
111  if(con.connect(12, 5, 1))
112  {
113  return NDBT_ProgramExit(NDBT_FAILED);
114  }
115 
116  g_ndb = new Ndb(&con, "TEST_DB");
117  if(g_ndb->init() != 0){
118  g_err << "init() failed" << endl;
119  goto error;
120  }
121  if(g_ndb->waitUntilReady() != 0){
122  g_err << "Wait until ready failed" << endl;
123  goto error;
124  }
125  for(i = optind; i<argc; i++){
126  const char * T = argv[i];
127  g_info << "Testing " << T << endl;
128  BaseString::snprintf(g_tablename, sizeof(g_tablename), "%s", T);
129  BaseString::snprintf(g_indexname, sizeof(g_indexname), "IDX_%s", T);
130  if(create_table())
131  goto error;
132  if(g_paramters[P_CREATE].value != 2 && run_scan())
133  goto error;
134  }
135 
136  if(g_ndb) delete g_ndb;
137  return NDBT_OK;
138  error:
139  if(g_ndb) delete g_ndb;
140  return NDBT_FAILED;
141 }
142 
143 int
144 create_table(){
145  NdbDictionary::Dictionary* dict = g_ndb->getDictionary();
146  assert(dict);
147  if(g_paramters[P_CREATE].value){
148  g_ndb->getDictionary()->dropTable(g_tablename);
149  const NdbDictionary::Table * pTab = NDBT_Tables::getTable(g_tablename);
150  assert(pTab);
151  NdbDictionary::Table copy = * pTab;
152  copy.setLogging(false);
153  if(dict->createTable(copy) != 0){
154  g_err << "Failed to create table: " << g_tablename << endl;
155  return -1;
156  }
157 
158  NdbDictionary::Index x(g_indexname);
159  x.setTable(g_tablename);
161  x.setLogging(false);
162  for (unsigned k = 0; k < (unsigned) copy.getNoOfColumns(); k++){
163  if(copy.getColumn(k)->getPrimaryKey()){
164  x.addColumnName(copy.getColumn(k)->getName());
165  }
166  }
167 
168  if(dict->createIndex(x) != 0){
169  g_err << "Failed to create index: " << endl;
170  return -1;
171  }
172  }
173  g_table = dict->getTable(g_tablename);
174  g_index = dict->getIndex(g_indexname, g_tablename);
175  assert(g_table);
176  assert(g_index);
177 
178  /* Obtain NdbRecord instances for the table and index */
179  {
180  NdbDictionary::RecordSpecification spec[ NDB_MAX_ATTRIBUTES_IN_TABLE ];
181 
182  Uint32 offset=0;
183  Uint32 cols= g_table->getNoOfColumns();
184  for (Uint32 colNum=0; colNum<cols; colNum++)
185  {
186  const NdbDictionary::Column* col= g_table->getColumn(colNum);
187  Uint32 colLength= col->getLength();
188 
189  spec[colNum].column= col;
190  spec[colNum].offset= offset;
191 
192  offset+= colLength;
193 
194  spec[colNum].nullbit_byte_offset= offset++;
195  spec[colNum].nullbit_bit_in_byte= 0;
196  }
197 
198  g_table_record= dict->createRecord(g_table,
199  &spec[0],
200  cols,
202 
203  assert(g_table_record);
204  }
205  {
206  NdbDictionary::RecordSpecification spec[ NDB_MAX_ATTRIBUTES_IN_TABLE ];
207 
208  Uint32 offset=0;
209  Uint32 cols= g_index->getNoOfColumns();
210  for (Uint32 colNum=0; colNum<cols; colNum++)
211  {
212  /* Get column from the underlying table */
213  // TODO : Add this mechanism to dict->createRecord
214  // TODO : Add NdbRecord queryability methods so that an NdbRecord can
215  // be easily built and later used to read out data.
216  const NdbDictionary::Column* col=
217  g_table->getColumn(g_index->getColumn(colNum)->getName());
218  Uint32 colLength= col->getLength();
219 
220  spec[colNum].column= col;
221  spec[colNum].offset= offset;
222 
223  offset+= colLength;
224 
225  spec[colNum].nullbit_byte_offset= offset++;
226  spec[colNum].nullbit_bit_in_byte= 0;
227  }
228 
229  g_index_record= dict->createRecord(g_index,
230  &spec[0],
231  cols,
233 
234  assert(g_index_record);
235  }
236 
237 
238  if(g_paramters[P_CREATE].value)
239  {
240  int rows = g_paramters[P_ROWS].value;
241  HugoTransactions hugoTrans(* g_table);
242  if (hugoTrans.loadTable(g_ndb, rows)){
243  g_err.println("Failed to load %s with %d rows",
244  g_table->getName(), rows);
245  return -1;
246  }
247  }
248 
249  return 0;
250 }
251 
252 inline
253 void err(NdbError e){
254  ndbout << e << endl;
255 }
256 
257 int
258 setEqBound(NdbIndexScanOperation *isop,
259  const NdbRecord *key_record,
260  Uint32 value,
261  Uint32 rangeNum)
262 {
263  Uint32 space[2];
264  space[0]= value;
265  space[1]= 0; // Null bit set to zero.
266 
268  ib.low_key= ib.high_key= (char*) &space;
269  ib.low_key_count= ib.high_key_count= 1;
270  ib.low_inclusive= ib.high_inclusive= true;
271  ib.range_no= rangeNum;
272 
273  return isop->setBound(key_record, ib);
274 }
275 
276 int
277 run_scan(){
278  int iter = g_paramters[P_LOOPS].value;
279  NDB_TICKS start1, stop;
280  int sum_time= 0;
281 
282  Uint32 sample_rows = 0;
283  int tot_rows = 0;
284  NDB_TICKS sample_start = NdbTick_CurrentMillisecond();
285 
286  Uint32 tot = g_paramters[P_ROWS].value;
287 
288  if(g_paramters[P_BOUND].value >= 2 || g_paramters[P_FILT].value == 2)
289  iter *= g_paramters[P_ROWS].value;
290 
291  NdbScanOperation * pOp = 0;
292  NdbIndexScanOperation * pIOp = 0;
293  NdbConnection * pTrans = 0;
294  int check = 0;
295 
296  for(int i = 0; i<iter; i++){
297  start1 = NdbTick_CurrentMillisecond();
298  pTrans = pTrans ? pTrans : g_ndb->startTransaction();
299  if(!pTrans){
300  g_err << "Failed to start transaction" << endl;
301  err(g_ndb->getNdbError());
302  return -1;
303  }
304 
305  int par = g_paramters[P_PARRA].value;
306  int bat = g_paramters[P_BATCH].value;
308  switch(g_paramters[P_LOCK].value){
309  case 0:
311  break;
312  case 1:
314  break;
315  case 2:
317  break;
318  default:
319  abort();
320  }
321 
323  bzero(&options, sizeof(options));
324 
325  options.optionsPresent=
326  NdbScanOperation::ScanOptions::SO_SCANFLAGS |
327  NdbScanOperation::ScanOptions::SO_PARALLEL |
328  NdbScanOperation::ScanOptions::SO_BATCH;
329 
330  bool ord= g_paramters[P_ACCESS].value == 2;
331  bool mrr= (g_paramters[P_ACCESS].value != 0) &&
332  (g_paramters[P_BOUND].value == 3);
333 
334  options.scan_flags|=
335  ( ord ? NdbScanOperation::SF_OrderBy:0 ) |
336  ( mrr ? NdbScanOperation::SF_MultiRange:0 );
337  options.parallel= par;
338  options.batch= bat;
339 
340  switch(g_paramters[P_FILT].value){
341  case 0: // All
342  break;
343  case 1: // None
344  break;
345  case 2: // 1 row
346  default: {
347  assert(g_table->getNoOfPrimaryKeys() == 1); // only impl. so far
348  abort();
349 #if 0
350  int tot = g_paramters[P_ROWS].value;
351  int row = rand() % tot;
352  NdbInterpretedCode* ic= new NdbInterpretedCode(g_table);
353  NdbScanFilter filter(ic);
354  filter.begin(NdbScanFilter::AND);
355  filter.eq(0, row);
356  filter.end();
357 
358  options.scan_flags|= NdbScanOperation::SF_Interpreted;
359  options.interpretedCode= &ic;
360  break;
361 #endif
362  }
363  }
364 
365  if(g_paramters[P_ACCESS].value == 0){
366  pOp = pTrans->scanTable(g_table_record,
367  lm,
368  NULL, // Mask
369  &options,
371  assert(pOp);
372  } else {
373  pOp= pIOp= pTrans->scanIndex(g_index_record,
374  g_table_record,
375  lm,
376  NULL, // Mask
377  NULL, // First IndexBound
378  &options,
380  if (pIOp == NULL)
381  {
382  err(pTrans->getNdbError());
383  abort();
384  }
385 
386  assert(pIOp);
387 
388  switch(g_paramters[P_BOUND].value){
389  case 0: // All
390  break;
391  case 1: // None
392  check= setEqBound(pIOp, g_index_record, 0, 0);
393  assert(check == 0);
394  break;
395  case 2: { // 1 row
396  default:
397  assert(g_table->getNoOfPrimaryKeys() == 1); // only impl. so far
398  int tot = g_paramters[P_ROWS].value;
399  int row = rand() % tot;
400 
401  check= setEqBound(pIOp, g_index_record, row, 0);
402  assert(check == 0);
403  break;
404  }
405  case 3: { // read multi
406  int multi = g_paramters[P_MULTI].value;
407  int tot = g_paramters[P_ROWS].value;
408  int rangeStart= i;
409  for(; multi > 0 && i < iter; --multi, i++)
410  {
411  int row = rand() % tot;
412  /* Set range num relative to this set of bounds */
413  check= setEqBound(pIOp, g_index_record, row, i- rangeStart);
414  if (check != 0)
415  {
416  err(pIOp->getNdbError());
417  abort();
418  }
419  assert(check == 0);
420  }
421  break;
422  }
423  }
424  }
425  assert(pOp);
426 
427  assert(check == 0);
428 
429  int rows = 0;
430  check = pTrans->execute(NoCommit);
431  assert(check == 0);
432  int fetch = g_paramters[P_FETCH].value;
433 
434  const char * result_row_ptr;
435 
436  while((check = pOp->nextResult(&result_row_ptr, true, false)) == 0){
437  do {
438  rows++;
439  } while(!fetch && ((check = pOp->nextResult(&result_row_ptr, false, false)) == 0));
440  if(check == -1){
441  err(pTrans->getNdbError());
442  return -1;
443  }
444  assert(check == 2);
445  }
446 
447  if(check == -1){
448  err(pTrans->getNdbError());
449  return -1;
450  }
451  assert(check == 1);
452 
453  pTrans->close();
454  pTrans = 0;
455 
456  stop = NdbTick_CurrentMillisecond();
457 
458  int time_passed= (int)(stop - start1);
459  sample_rows += rows;
460  sum_time+= time_passed;
461  tot_rows+= rows;
462 
463  if(sample_rows >= tot)
464  {
465  int sample_time = (int)(stop - sample_start);
466  g_info << "Found " << sample_rows << " rows" << endl;
467  g_err.println("Time: %d ms = %u rows/sec", sample_time,
468  (1000*sample_rows)/sample_time);
469  sample_rows = 0;
470  sample_start = stop;
471  }
472  }
473 
474  g_err.println("Avg time: %d ms = %u rows/sec", sum_time/tot_rows,
475  (1000*tot_rows)/sum_time);
476  return 0;
477 }