PageRenderTime 43ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/test/bench.php

https://gitlab.com/lishaomin/sphinx
PHP | 656 lines | 535 code | 94 blank | 27 comment | 115 complexity | 800cd9e32868b69535d87b678cace012 MD5 | raw file
  1. <?php
  2. ini_set ( "display_errors", 1 );
  3. ini_set ( "error_reporting", E_ALL | E_STRICT );
  4. assert_options ( ASSERT_ACTIVE, 1 );
  5. assert_options ( ASSERT_BAIL, 1 );
  6. require_once ( "settings.inc" );
  7. ////////////////////////////////////////////////////////////////////////////////
  8. function Fatal ( $message )
  9. {
  10. print "FATAL: $message.\n";
  11. exit ( 1 );
  12. }
  13. function sphCompareTime ( $a, $b )
  14. {
  15. $p = $a ? sprintf ( "%.1f", ( $b / $a - 1 ) * 100 ) : '0.0';
  16. return $p;
  17. }
  18. function sphCompareKeys ( $sets, $i, $key )
  19. {
  20. return
  21. $sets[0][$i][$key] == $sets[1][$i][$key] ?
  22. $sets[0][$i][$key] :
  23. $sets[0][$i][$key] . ' / ' . $sets[1][$i][$key];
  24. }
  25. function sphCompareSets ( $sets )
  26. {
  27. $tag = null;
  28. $report = array();
  29. for ( $i=0; $i<count($sets[0]); $i++ )
  30. {
  31. if ( array_key_exists ( 'tag', $sets[0][$i] ) && $sets[0][$i]['tag'] != $tag )
  32. {
  33. $tag = $sets[0][$i]['tag'];
  34. $report[] = array ( $tag );
  35. }
  36. $report[] = array (
  37. sphCompareKeys ( $sets, $i, 'query' ),
  38. sprintf ( "%.3f", $sets[0][$i]['time'] ),
  39. sprintf ( "%.3f", $sets[1][$i]['time'] ),
  40. sphCompareTime ( $sets[0][$i]['time'] ,$sets[1][$i]['time'] ),
  41. sphCompareKeys ( $sets, $i, 'total' ),
  42. sphCompareKeys ( $sets, $i, 'total_found' )
  43. );
  44. }
  45. return $report;
  46. }
  47. function sphCompare ( $sources )
  48. {
  49. $data = array ( unserialize ( file_get_contents($sources[0]) ),
  50. unserialize ( file_get_contents($sources[1]) ) );
  51. $report = array (
  52. 'report' => array(),
  53. 'sources' => $sources,
  54. 'versions' => array ( $data[0]['version'], $data[1]['version'] ),
  55. 'checksums' => $data[0]['hash'] == $data[1]['hash'],
  56. );
  57. if ( count($data[0]['results']) != count($data[1]['results']) )
  58. Fatal ( "unable to produce the report, result set sizes mismatch" );
  59. $combined = array();
  60. $skip = array();
  61. foreach ( $data as &$set )
  62. {
  63. $out = array();
  64. for ( $i=0; $i<count($set['results']); $i++ )
  65. {
  66. if ( in_array ( $i, $skip ) ) continue;
  67. $row = &$set['results'][$i];
  68. if ( $row['total'] == -1 )
  69. {
  70. $skip[] = $i;
  71. continue;
  72. }
  73. $tag = $row['tag'];
  74. if ( !array_key_exists ( $tag, $out ) )
  75. {
  76. $out[$tag] = array (
  77. 'query' => $tag,
  78. 'time' => 0,
  79. 'total' => 0,
  80. 'total_found' => 0
  81. );
  82. }
  83. $row['time'] = (float)$row['time'];
  84. $out[$tag]['time'] += $row['time'];
  85. $out[$tag]['total'] += $row['total'];
  86. $out[$tag]['total_found'] += $row['total_found'];
  87. }
  88. $combined[] = $out;
  89. }
  90. if ( array_keys($combined[0]) != array_keys($combined[1]) )
  91. Fatal ( "unable to produce the report, tag sets mismatch" );
  92. $report['aggregate'] = sphCompareSets ( array ( array_values ( $combined[0] ),
  93. array_values ( $combined[1] ) ) );
  94. $report['detailed'] = sphCompareSets ( array ( $data[0]['results'],
  95. $data[1]['results'] ) );
  96. return $report;
  97. }
  98. ////////////////////////////////////////////////////////////////////////////////
  99. function sphTextReport ( $report )
  100. {
  101. global $g_locals;
  102. $mode = $g_locals['mode'];
  103. $width = array ();
  104. $header = array ( 'query', basename ( $report['sources'][0], '.bin' ) , basename ( $report['sources'][1], '.bin' ), '%', 'total', 'total found' );
  105. foreach ( $header as $title )
  106. $width[] = strlen($title);
  107. foreach ( $report[$mode] as $row )
  108. {
  109. if ( count($row)==1 ) continue;
  110. for ( $i=0; $i<count($row); $i++ )
  111. {
  112. $len = min ( strlen($row[$i]), 40 );
  113. $width[$i] = max ( $width[$i], $len );
  114. }
  115. }
  116. printf ( "COMPARING: %s - %s\n", $report['versions'][0], $report['sources'][0] );
  117. printf ( " %s - %s\n", $report['versions'][1], $report['sources'][1] );
  118. if ( !$report['checksums'] )
  119. printf ( "WARNING: checksum mismatch, results might be incorrect\n\n" );
  120. if ( $mode=='aggregate' ) printf("\n");
  121. $restart = true;
  122. foreach ( $report[$mode] as $row )
  123. {
  124. if ( count($row)==1 )
  125. {
  126. printf("\n");
  127. $w = str_repeat ( '=', ( array_sum($width) + 9 - strlen($row[0]) ) / 2 );
  128. printf ( " %s %s %s\n", $w, $row[0], $w );
  129. $restart = true;
  130. }
  131. if ( $restart )
  132. {
  133. for ( $i=0; $i<count($header); $i++ )
  134. printf ( ' %s', str_pad ( $header[$i], $width[$i] + 2, ' ', STR_PAD_BOTH ) );
  135. printf("\n");
  136. for ( $i=0; $i<count($header); $i++ )
  137. printf ( ' %s', str_repeat ( '-', $width[$i] + 2 ) );
  138. $restart = false;
  139. printf("\n");
  140. }
  141. $i = 0;
  142. foreach ( $row as $text )
  143. {
  144. if ( strlen($text) > 40 )
  145. $text = substr ( $text, 0, 37 ) . '...';
  146. printf ( '%s |', str_pad ( $text, $width[$i] + 2, ' ', STR_PAD_LEFT ) );
  147. $i++;
  148. }
  149. printf("\n");
  150. }
  151. if ( !count ( $report[$mode] ) )
  152. printf ( "empty report.\n" );
  153. }
  154. ////////////////////////////////////////////////////////////////////////////////
  155. /// erases rt index
  156. function EraseRtIndex ( $path, $name )
  157. {
  158. $fp = opendir ( $path );
  159. if ( $fp )
  160. {
  161. $name .= '.';
  162. while ( ( $file = readdir ( $fp ) ) !== false )
  163. {
  164. if ( $file != "." && $file != ".." && !is_dir ( $file ) && strripos ( $file, $name ) !==false )
  165. unlink ( "$file" );
  166. }
  167. closedir ( $fp );
  168. }
  169. }
  170. ////////////////////////////////////////////////////////////////////////////////
  171. /// returns results file name on success; false on failure
  172. function sphBenchmark ( $name, $locals, $force_reindex )
  173. {
  174. // load config
  175. $config = new SphinxConfig ( $locals );
  176. if ( !( $config->Load ( "bench/$name.xml" ) && CheckConfig ( $config, $name ) ) )
  177. return false;
  178. global $g_locals;
  179. $g_locals['rt_mode']= $config->Requires('force-rt');
  180. $reindex_rt = $config->Requires('reindex-rt');
  181. // temporary limitations
  182. assert ( $config->SubtestCount()==1 );
  183. // find unused output prefix
  184. $i = 0;
  185. for ( ; file_exists("bench-results/$name.$i.bin"); $i++ );
  186. $output = "bench-results/$name.$i";
  187. printf ( "benchmarking: %s\n", $config->Name() );
  188. // grab index names and paths
  189. $msg = '';
  190. $config->WriteConfig ( 'config.conf', 'all', $msg );
  191. $indexes = array();
  192. $indexes_rt = array();
  193. $text = file_get_contents('config.conf');
  194. preg_match_all ( '/index\s+(\S+)\s+{[^}]+path\s*=\s*(.*)[^}]+}/m', $text, $matches );
  195. for ( $i=0; $i<count($matches[1]); $i++ )
  196. $indexes[$matches[1][$i]] = $matches[2][$i];
  197. for ( $i=0; $i<count($matches[0]); $i++ )
  198. if ( preg_match ( '/type\s*=\s*rt/', $matches[0][$i] ) )
  199. $indexes_rt[$matches[1][$i]] = 1;
  200. // checksum/reindex as needed
  201. $hash = null;
  202. foreach ( $indexes as $indexName => $path )
  203. {
  204. $skip_rt = ( $reindex_rt && array_key_exists ( $indexName, $indexes_rt ) );
  205. printf ( "index: %s - ", $indexName );
  206. if ( ( $config->IsRt() && $force_reindex ) || $skip_rt )
  207. EraseRtIndex ( $locals['data'], $path );
  208. if ( !$config->IsRt() && ( !is_readable ( "$path.spa" ) || !is_readable ( "$path.spi" ) || $force_reindex ) && !$skip_rt )
  209. {
  210. printf ( "indexing... " );
  211. $tm = MyMicrotime();
  212. $result = RunIndexer ( $error, $indexName );
  213. $tm = MyMicrotime() - $tm;
  214. if ( $result==1 )
  215. {
  216. printf ( "\nerror running the indexer:\n%s\n", $error );
  217. return false;
  218. }
  219. else if ( $result==2 )
  220. printf ( "done in %s, there were warnings:\n%s\n", sphFormatTime($tm), $error );
  221. else
  222. printf ( "done in %s - ", sphFormatTime($tm) );
  223. }
  224. if ( !$config->IsRt() && !$skip_rt )
  225. {
  226. $hash = array ( 'spi' => md5_file ( "$path.spi" ),
  227. 'spa' => md5_file ( "$path.spa" ) );
  228. printf ( "%s\n", $hash['spi'] );
  229. }
  230. else
  231. {
  232. $hash = array ( 'xml'=>md5_file ("bench/$name.xml") );
  233. printf ( "%s\n", $hash['xml'] );
  234. }
  235. }
  236. // start searchd
  237. if ( !$locals['skip-searchd'] )
  238. {
  239. $result = StartSearchd ( 'config.conf', "$output.searchd.txt", 'searchd.pid', $error );
  240. if ( $result==1 )
  241. {
  242. printf ( "error starting searchd:\n%s\n", $error );
  243. return false;
  244. }
  245. else if ( $result==2 )
  246. printf ( "searchd warning: %s\n", $error );
  247. }
  248. $report = array (
  249. 'results' => array(),
  250. 'time' => time(),
  251. 'hash' => $hash,
  252. 'version' => GetVersion()
  253. );
  254. // run the benchmark
  255. foreach ( array('custom', 'ql', 'api') as $mode )
  256. {
  257. $config->ResetResults();
  258. $isOK = false;
  259. if ( $mode=='ql' && $config->IsSphinxqlTest () )
  260. {
  261. $isOK = $config->RunQuerySphinxQL ( $error, true );
  262. } else if ( $mode=='api' && $config->IsQueryTest () )
  263. {
  264. $isOK = $config->RunQuery ( '*', $error, 'warming-up:' ) && $config->RunQuery ( '*', $error, 'profiling:' );
  265. } else if ( $mode=='custom' )
  266. {
  267. $isOK = $config->RunCustomTest ( $error );
  268. } else
  269. {
  270. continue;
  271. }
  272. if ( $isOK )
  273. {
  274. $i = 0; $q = null;
  275. $lastQuery = '';
  276. $lastTag = '';
  277. foreach ( $config->Results() as $result )
  278. {
  279. if ( $mode=='ql' )
  280. {
  281. if ( $result['sphinxql']=='show meta' )
  282. {
  283. $report['results'][] =
  284. array ( 'total' => $result['rows'][0]['Value'],
  285. 'total_found' => $result['rows'][1]['Value'],
  286. 'time' => $result['rows'][2]['Value'],
  287. 'query' => $lastQuery,
  288. 'tag' => $lastTag );
  289. } else
  290. {
  291. $lastQuery = $result['sphinxql'];
  292. $lastTag = $lastQuery;
  293. $matches = array();
  294. if ( preg_match_all ( "/comment.*=.*\'(.*)\'/", $lastQuery, $matches ) )
  295. {
  296. $lastTag = $matches[1][0];
  297. }
  298. }
  299. } else if ( $mode=='api' )
  300. {
  301. if ( $result[0] !== $q )
  302. {
  303. $i = 0;
  304. $q = $result[0];
  305. }
  306. $query = $config->GetQuery ( $q );
  307. $report['results'][] =
  308. array ( 'total' => $result[1],
  309. 'total_found' => $result[2],
  310. 'time' => $result[3],
  311. 'query' => $query['query'][$i++],
  312. 'tag' => $query['tag'] );
  313. } else if ( $mode=='custom' )
  314. {
  315. foreach ( $result as $r )
  316. {
  317. $report['results'][] =
  318. array ( 'total' => $r['total'],
  319. 'total_found' => $r['total_found'],
  320. 'time' => $r['time'],
  321. 'query' => $r['query'],
  322. 'tag' => $r['tag'] );
  323. }
  324. }
  325. }
  326. } else
  327. {
  328. printf ( "\nfailed to run queries at %s:\n%s\n", $mode, $error );
  329. }
  330. }
  331. file_put_contents ( "$output.bin", serialize ( $report ) );
  332. printf ( "results saved to: $output.bin\n" );
  333. // shutdown
  334. StopSearchd ( 'config.conf', 'searchd.pid' );
  335. // all good
  336. return $output;
  337. }
  338. ////////////////////////////////////////////////////////////////////////////////
  339. /// pick $count freshest run files for $bench benchmark
  340. function PickFreshest ( $bench, $count )
  341. {
  342. // traverse results dir for $bench.RUNID.bin
  343. $found = array();
  344. $dh = opendir ( "bench-results" );
  345. if ( $dh )
  346. {
  347. while ( $entry = readdir ( $dh ) )
  348. {
  349. if ( substr ( $entry, 0, 1+strlen($bench) )!==$bench."." )
  350. continue;
  351. if ( substr ( $entry, -4 )!==".bin" )
  352. continue;
  353. $index = (int)substr ( $entry, 1+strlen($bench) );
  354. $found[$index] = "bench-results/$entry";
  355. }
  356. closedir ( $dh );
  357. }
  358. if ( !$found )
  359. return null;
  360. ksort ( $found );
  361. return array_slice ( array_values ( $found ), -$count );
  362. }
  363. /// lookup run file by name, doing some common guesses
  364. function LookupRun ( $name )
  365. {
  366. // try full BENCH.RUNID guesses first
  367. $found = null;
  368. foreach ( array ( $name, "$name.bin", "bench-results/$name", "bench-results/$name.bin" ) as $guess )
  369. {
  370. if ( is_readable($guess) )
  371. {
  372. $found = $guess;
  373. break;
  374. }
  375. }
  376. // try pick the freshest one by bench name next
  377. if ( !$found )
  378. list($found) = PickFreshest ( $name, 1 );
  379. // still not found? too bad
  380. if ( !$found )
  381. Fatal ( "no run files for '$name' found" );
  382. return $found;
  383. }
  384. function BenchPrintHelp ( $path )
  385. {
  386. print <<<EOT
  387. Usage: php $path <COMMAND> [OPTIONS]
  388. Commands are:
  389. b, benchmark BENCH benchmark (and store run result)
  390. bv BENCH benchmark, view run result
  391. bb BENCH (forcibly) build index, benchmark
  392. bbv BENCH (forcibly) build index, benchmark, view run result
  393. c, compare BENCH compare two latest run results of given benchmark
  394. c, compare BENCH RUNID1 RUNID2
  395. c, compare BENCH.RUNID1 BENCH.RUNID2
  396. compare two given run results
  397. t, try BENCH benchmark, view, but do not store run result
  398. v, view BENCH.RUNID view given run result
  399. Examples:
  400. bench.php b mytest run 'mytest' benchmark
  401. bench.php v mytest view latest 'mytest' run result
  402. bench.php v mytest.4 view 4th 'mytest' run result
  403. bench.php c mytest 3 7
  404. bench.php c mytest.3 mytest.7
  405. compare runs 3 and 7 of 'mytest' benchmark
  406. EOT;
  407. }
  408. function AvailableBenchmarks ()
  409. {
  410. $dh = opendir ( "bench" );
  411. if ( !$dh )
  412. return;
  413. $avail = array ();
  414. while ( $entry = readdir ( $dh ) )
  415. if ( substr ( $entry, -4 )===".xml" )
  416. $avail[] = substr ( $entry, 0, -4 );
  417. closedir ( $dh );
  418. return join ( ", or ", $avail );
  419. }
  420. function Main ( $argv )
  421. {
  422. if ( count($argv)==1 )
  423. {
  424. BenchPrintHelp ( $argv[0] );
  425. return 0;
  426. }
  427. $mode = null;
  428. $files = array();
  429. $locals = array();
  430. // parse arguments
  431. $force_reindex = false;
  432. $view_results = false;
  433. $unlink_run = false;
  434. for ( $i=1; $i<count($argv); $i++ )
  435. {
  436. $opt = $argv[$i];
  437. if ( substr ( $opt, 0, 2 ) == '--' )
  438. {
  439. $values = explode ( '=', substr ( $opt, 2 ), 2 );
  440. if ( count($values)==2 )
  441. $locals[$values[0]] = $values[1];
  442. }
  443. else if ( $opt[0] == '-' && $i < ( count($argv) - 1 ) )
  444. {
  445. $locals [ substr ( $opt, 1 ) ] = $argv[++$i];
  446. }
  447. else if ( $mode )
  448. {
  449. $files[] = $opt;
  450. }
  451. else
  452. {
  453. switch ( $opt )
  454. {
  455. case 'b':
  456. case 'benchmark':
  457. $mode = 'benchmark';
  458. break;
  459. case 'bb':
  460. $mode = 'benchmark';
  461. $force_reindex = true;
  462. break;
  463. case 'bv':
  464. $mode = 'benchmark';
  465. $view_results = true;
  466. break;
  467. case 'bbv':
  468. $mode = 'benchmark';
  469. $force_reindex = true;
  470. $view_results = true;
  471. break;
  472. case 'c':
  473. case 'compare':
  474. $mode = 'compare';
  475. break;
  476. case 't':
  477. case 'try':
  478. $mode = 'benchmark';
  479. $view_results = true;
  480. $unlink_run = true;
  481. break;
  482. case 'v':
  483. case 'view':
  484. $mode = 'view';
  485. break;
  486. default:
  487. Fatal ( "unknown mode '$opt' (run bench.php without arguments for help)" );
  488. }
  489. }
  490. }
  491. global $g_locals;
  492. PublishLocals ( $locals, true );
  493. require_once ( "helpers.inc" );
  494. require_once ( "helpers_rt.inc" );
  495. // run the command
  496. if ( $mode=='benchmark' )
  497. {
  498. if ( count($files)==0 )
  499. {
  500. $avail = AvailableBenchmarks ();
  501. if ( $avail )
  502. $avail = " ($avail)";
  503. Fatal ( "benchmark command requires a benchmark name$avail" );
  504. }
  505. if ( count($files)!=1 )
  506. Fatal ( "benchmark command requires exactly one name" );
  507. $path = "bench/$files[0].xml";
  508. if ( !is_readable($path) )
  509. Fatal ( "benchmark scenario '$path' is not readable" );
  510. $res = sphBenchmark ( $files[0], $g_locals, $force_reindex );
  511. if ( !$res )
  512. return 1;
  513. if ( $view_results )
  514. sphTextReport ( sphCompare ( array ( "$res.bin", "$res.bin" ) ) );
  515. if ( $unlink_run )
  516. {
  517. printf ( "\nunlinked %s\n", $res );
  518. unlink ( "$res.bin" );
  519. unlink ( "$res.searchd.txt" );
  520. }
  521. }
  522. else if ( $mode=='compare' )
  523. {
  524. switch ( count($files) )
  525. {
  526. case 1:
  527. // pick two freshest by name
  528. $runs = PickFreshest ( $files[0], 2 );
  529. if ( count($runs)!=2 )
  530. Fatal ( "not enough run files for '$files[0]' (needed 2, got ".count($runs).")" );
  531. break;
  532. case 2:
  533. // explicit run names given
  534. $runs = array ( LookupRun ( $files[0] ), LookupRun ( $files[1] ) );
  535. break;
  536. case 3:
  537. // explicit run names, shortcut syntax
  538. $runs = array (
  539. LookupRun ( "$files[0].$files[1]" ),
  540. LookupRun ( "$files[0].$files[2]" ) );
  541. break;
  542. default:
  543. Fatal ( "invalid compare syntax (expected 1 to 3 args, got ".count($files).")" );
  544. }
  545. sphTextReport ( sphCompare ( $runs ) );
  546. }
  547. else if ( $mode=='view' )
  548. {
  549. if ( count($files)!=1 )
  550. {
  551. Fatal ( "view command requires exactly one argument" );
  552. }
  553. $run = LookupRun ( $files[0] );
  554. sphTextReport ( sphCompare ( array ( $run, $run ) ) );
  555. }
  556. else
  557. {
  558. BenchPrintHelp ( $argv[0] );
  559. }
  560. }
  561. Main ( $argv );
  562. exit ( 0 );