array(), 'sources' => $sources, 'versions' => array ( $data[0]['version'], $data[1]['version'] ), 'checksums' => $data[0]['hash'] == $data[1]['hash'], ); if ( count($data[0]['results']) != count($data[1]['results']) ) Fatal ( "unable to produce the report, result set sizes mismatch" ); $combined = array(); $skip = array(); foreach ( $data as &$set ) { $out = array(); for ( $i=0; $i $tag, 'time' => 0, 'total' => 0, 'total_found' => 0 ); } $row['time'] = (float)$row['time']; $out[$tag]['time'] += $row['time']; $out[$tag]['total'] += $row['total']; $out[$tag]['total_found'] += $row['total_found']; } $combined[] = $out; } if ( array_keys($combined[0]) != array_keys($combined[1]) ) Fatal ( "unable to produce the report, tag sets mismatch" ); $report['aggregate'] = sphCompareSets ( array ( array_values ( $combined[0] ), array_values ( $combined[1] ) ) ); $report['detailed'] = sphCompareSets ( array ( $data[0]['results'], $data[1]['results'] ) ); return $report; } //////////////////////////////////////////////////////////////////////////////// function sphTextReport ( $report ) { global $g_locals; $mode = $g_locals['mode']; $width = array (); $header = array ( 'query', 'time', 'total', 'total found' ); foreach ( $header as $title ) $width[] = strlen($title); foreach ( $report[$mode] as $row ) { if ( count($row)==1 ) continue; for ( $i=0; $i<4; $i++ ) { $len = min ( strlen($row[$i]), 40 ); $width[$i] = max ( $width[$i], $len ); } } printf ( "COMPARING: %s - %s\n", $report['versions'][0], $report['sources'][0] ); printf ( " %s - %s\n", $report['versions'][1], $report['sources'][1] ); if ( !$report['checksums'] ) printf ( "WARNING: checksum mismatch, results might be incorrect\n\n" ); if ( $mode=='aggregate' ) printf("\n"); $restart = true; foreach ( $report[$mode] as $row ) { if ( count($row)==1 ) { printf("\n"); $w = str_repeat ( '=', ( array_sum($width) + 9 - strlen($row[0]) ) / 2 ); printf ( " %s %s %s\n", $w, $row[0], $w ); $restart = true; } if ( $restart ) { for ( $i=0; $i<4; $i++ ) printf ( ' %s', str_pad ( $header[$i], $width[$i] + 2, ' ', STR_PAD_BOTH ) ); printf("\n"); for ( $i=0; $i<4; $i++ ) printf ( ' %s', str_repeat ( '-', $width[$i] + 2 ) ); $restart = false; printf("\n"); } if ( count($row)==4 ) for ( $i=0; $i<4; $i++ ) { $text = $row[$i]; if ( strlen($text) > 40 ) $text = substr ( $text, 0, 37 ) . '...'; if ( $i>1 ) $text = number_format ( $text, 0, '', ' ' ); printf ( ' %s', str_pad ( $text, $width[$i] + 2, ' ', STR_PAD_LEFT ) ); } printf("\n"); } if ( !count ( $report[$mode] ) ) printf ( "empty report.\n" ); } //////////////////////////////////////////////////////////////////////////////// /// erases rt index function EraseRtIndex ( $path, $name ) { $fp = opendir ( $path ); if ( $fp ) { $name .= '.'; while ( ( $file = readdir ( $fp ) ) !== false ) { if ( $file != "." && $file != ".." && !is_dir ( $file ) && strripos ( $file, $name ) !==false ) unlink ( "$file" ); } closedir ( $fp ); } } //////////////////////////////////////////////////////////////////////////////// /// returns results file name on success; false on failure function sphBenchmark ( $name, $locals, $force_reindex ) { // load config $config = new SphinxConfig ( $locals ); if ( !( $config->Load ( "bench/$name.xml" ) && CheckConfig ( $config, $name ) ) ) return false; global $g_locals; $g_locals['rt_mode']= $config->Requires('force-rt'); // temporary limitations assert ( $config->SubtestCount()==1 ); assert ( $config->IsQueryTest() ); // find unused output prefix $i = 0; for ( ; file_exists("bench-results/$name.$i.bin"); $i++ ); $output = "bench-results/$name.$i"; printf ( "benchmarking: %s\n", $config->Name() ); // grab index names and paths $msg = ''; if ( !$config->IsRt() ) // enable only in non rt-mode $config->EnableCompat098 (); $config->WriteConfig ( 'config.conf', 'all', $msg ); $indexes = array(); $text = file_get_contents('config.conf'); preg_match_all ( '/index\s+(\S+)\s+{[^}]+path\s*=\s*(.*)[^}]+}/m', $text, $matches ); for ( $i=0; $i $path ) { printf ( "index: %s - ", $indexName ); if ( $config->IsRt() && $force_reindex ) EraseRtIndex ( $locals['data'], $path ); if ( !$config->IsRt() && ( !is_readable ( "$path.spa" ) || !is_readable ( "$path.spi" ) || $force_reindex ) ) { printf ( "indexing... " ); $tm = MyMicrotime(); $result = RunIndexer ( $error, $indexName ); $tm = MyMicrotime() - $tm; if ( $result==1 ) { printf ( "\nerror running the indexer:\n%s\n", $error ); return false; } else if ( $result==2 ) printf ( "done in %s, there were warnings:\n%s\n", sphFormatTime($tm), $error ); else printf ( "done in %s - ", sphFormatTime($tm) ); } if ( !$config->IsRt() ) { $hash = array ( 'spi' => md5_file ( "$path.spi" ), 'spa' => md5_file ( "$path.spa" ) ); printf ( "%s\n", $hash['spi'] ); } else { $hash = array ( 'xml'=>md5_file ("bench/$name.xml") ); printf ( "%s\n", $hash['xml'] ); } } // start searchd if ( !$locals['skip-searchd'] ) { $result = StartSearchd ( 'config.conf', "$output.searchd.txt", 'searchd.pid', $error ); if ( $result==1 ) { printf ( "error starting searchd:\n%s\n", $error ); return false; } else if ( $result==2 ) printf ( "searchd warning: %s\n", $error ); } // run the benchmark $isOK = false; if ( $config->IsSphinxqlTest () ) $isOK = $config->RunQuerySphinxQL ( $error, true ); else $isOK = $config->RunQuery ( '*', $error, 'warming-up:' ) && $config->RunQuery ( '*', $error, 'profiling:' ); if ( $isOK ) { $report = array ( 'results' => array(), 'time' => time(), 'hash' => $hash, 'version' => GetVersion() ); $i = 0; $q = null; $last = ''; foreach ( $config->Results() as $result ) { if ( $config->IsSphinxqlTest () ) { if ( $result['sphinxql']=='show meta' ) { $report['results'][] = array ( 'total' => $result['rows'][0]['Value'], 'total_found' => $result['rows'][1]['Value'], 'time' => $result['rows'][2]['Value'], 'query' => $last, 'tag' => $last ); } $last = $result['sphinxql']; } else { if ( $result[0] !== $q ) { $i = 0; $q = $result[0]; } $query = $config->GetQuery ( $q ); $report['results'][] = array ( 'total' => $result[1], 'total_found' => $result[2], 'time' => $result[3], 'query' => $query['query'][$i++], 'tag' => $query['tag'] ); } } file_put_contents ( "$output.bin", serialize ( $report ) ); printf ( "results saved to: $output.bin\n" ); } else printf ( "\nfailed to run queries:\n%s\n", $error ); // shutdown StopSearchd ( 'config.conf', 'searchd.pid' ); // all good return $output; } //////////////////////////////////////////////////////////////////////////////// /// pick $count freshest run files for $bench benchmark function PickFreshest ( $bench, $count ) { // traverse results dir for $bench.RUNID.bin $found = array(); $dh = opendir ( "bench-results" ); if ( $dh ) { while ( $entry = readdir ( $dh ) ) { if ( substr ( $entry, 0, 1+strlen($bench) )!==$bench."." ) continue; if ( substr ( $entry, -4 )!==".bin" ) continue; $index = (int)substr ( $entry, 1+strlen($bench) ); $found[$index] = "bench-results/$entry"; } closedir ( $dh ); } if ( !$found ) return null; ksort ( $found ); return array_slice ( array_values ( $found ), -$count ); } /// lookup run file by name, doing some common guesses function LookupRun ( $name ) { // try full BENCH.RUNID guesses first $found = null; foreach ( array ( $name, "$name.bin", "bench-results/$name", "bench-results/$name.bin" ) as $guess ) { if ( is_readable($guess) ) { $found = $guess; break; } } // try pick the freshest one by bench name next if ( !$found ) list($found) = PickFreshest ( $name, 1 ); // still not found? too bad if ( !$found ) Fatal ( "no run files for '$name' found" ); return $found; } function BenchPrintHelp ( $path ) { print << [OPTIONS] Commands are: b, benchmark BENCH benchmark (and store run result) bv BENCH benchmark, view run result bb BENCH (forcibly) build index, benchmark bbv BENCH (forcibly) build index, benchmark, view run result c, compare BENCH compare two latest run results of given benchmark c, compare BENCH RUNID1 RUNID2 c, compare BENCH.RUNID1 BENCH.RUNID2 compare two given run results t, try BENCH benchmark, view, but do not store run result v, view BENCH.RUNID view given run result Examples: bench.php b mytest run 'mytest' benchmark bench.php v mytest view latest 'mytest' run result bench.php v mytest.4 view 4th 'mytest' run result bench.php c mytest 3 7 bench.php c mytest.3 mytest.7 compare runs 3 and 7 of 'mytest' benchmark EOT; } function AvailableBenchmarks () { $dh = opendir ( "bench" ); if ( !$dh ) return; $avail = array (); while ( $entry = readdir ( $dh ) ) if ( substr ( $entry, -4 )===".xml" ) $avail[] = substr ( $entry, 0, -4 ); closedir ( $dh ); return join ( ", or ", $avail ); } function Main ( $argv ) { if ( count($argv)==1 ) { BenchPrintHelp ( $argv[0] ); return 0; } $mode = null; $files = array(); $locals = array(); // parse arguments $force_reindex = false; $view_results = false; $unlink_run = false; for ( $i=1; $i