-
-
Save mgburns/36021cbda59773c36970 to your computer and use it in GitHub Desktop.
Script to benchmark basic memcached operations (set, get and delete) with different server configurations.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env php | |
<?php | |
$servers = array(); | |
$files = array(); | |
$input_path = $report_path = null; | |
// Command line arg parsing | |
$args = $argv; | |
array_shift( $args ); | |
while ( $arg = array_shift( $args ) ) { | |
switch ( $arg ) { | |
case '--help': | |
case '-h'; | |
usage(); | |
exit(); | |
break; | |
case '--report': | |
case '-r'; | |
$report_path = array_shift( $args ); | |
break; | |
default: | |
if ( ! $input_path ) { | |
$input_path = $arg; | |
} else { | |
$servers[] = $arg; | |
} | |
} | |
} | |
// Parse input file path(s) | |
if ( $input_path && is_file( $input_path ) ) { | |
$files[] = realpath( $input_path ); | |
} else if ( $input_path && is_dir( $input_path ) ) { | |
foreach ( new DirectoryIterator( $input_path ) as $file ) { | |
if ( $file->isFile() ) { | |
$files[] = $file->getPathname(); | |
} | |
} | |
} else { | |
err( "Bad input path." ); | |
usage(); | |
exit( 1 ); | |
} | |
// Validate memcached servers | |
if ( empty( $servers ) ) { | |
err( "No memcached servers could be found." ); | |
usage(); | |
exit( 1 ); | |
} | |
// Default report file | |
if ( is_null( $report_path ) ) { | |
$report_path = dirname( __FILE__ ) . "/results.csv"; | |
} | |
// Print run stats | |
$hostname = php_uname( 'n' ); | |
$ip = gethostbyname( $hostname ); | |
out(); | |
out( "Running Memcached benchmark tests" ); | |
out( "Host: $hostname" ); | |
out( "IP: $ip" ); | |
out(); | |
// Reporting setup | |
$rh = null; | |
report_open( $report_path, $rh ); | |
$headers = array( | |
'Date', 'Data Source', 'Servers', | |
'Total Items', 'Total Bytes', | |
'Set Time', 'Set Succeded', 'Set Failed', | |
'Get Time', 'Get Succeded', 'Get Failed', | |
'Delete Time', 'Delete Succeded', 'Delete Failed', | |
); | |
report_write_row( $rh, $headers ); | |
// Run benchmarks using provided data file(s) | |
foreach( $files as $file ) { | |
if ( count( $servers ) > 1 ) { | |
print_section( "Test 1 - Multiple Servers" ); | |
$bm = new MemcachedBenchmark( $servers, $file ); | |
$bm->run(); | |
report_write_row( $rh, $bm->get_stats() ); | |
print_section( "Test 2 - Individual Servers" ); | |
foreach ( $servers as $server ) { | |
$bm = new MemcachedBenchmark( $server, $file ); | |
$bm->run(); | |
report_write_row( $rh, $bm->get_stats() ); | |
} | |
} else { | |
$bm = new MemcachedBenchmark( $servers, $file ); | |
$bm->run(); | |
report_write_row( $rh, $bm->get_stats() ); | |
} | |
} | |
report_close( $rh ); | |
class MemcachedBenchmark { | |
private $mc; | |
private $servers; | |
private $data; | |
public $stats; | |
function __construct( $servers, $file ) { | |
$this->servers = (array) $servers; | |
$this->stats = array(); | |
$this->file = rawurldecode( basename( $file ) ); | |
$this->data = json_decode( file_get_contents( $file ), true ); | |
if ( false === $this->data ) { | |
err( "Error loading JSON data from file: $file" ); | |
} | |
$this->mc = new Memcache(); | |
foreach( $this->servers as $server ) { | |
list ( $node, $port ) = explode( ':', $server ); | |
$this->mc->addServer( $node, $port, true, 1, 1, 15, true, 'memcached_failure' ); | |
} | |
} | |
function __destruct() { | |
$this->mc->close(); | |
unset( $this->mc ); | |
unset( $this->data ); | |
} | |
function run() { | |
if ( ! $this->data ) { | |
err("No cache data available"); | |
return $this; | |
} | |
$this->stats['servers'] = $servers = implode( ', ', $this->servers ); | |
$this->stats['file'] = $file = $this->file; | |
$this->stats['time'] = date('Y-m-d H:i:s'); | |
$this->stats['items'] = $items = count( $this->data ); | |
$this->stats['bytes'] = $bytes = mb_strlen( serialize ( array_values( $this->data ) ), '8bit' ); | |
$this->stats['ops'] = array(); | |
out( "Running memcached benchmark" ); | |
out( "Servers: $servers" ); | |
out( "File: $file" ); | |
out(); | |
foreach ( array( 'set', 'get', 'delete' ) as $op ) { | |
print_section( "Starting operation: $op", '-' ); | |
$results = $this->benchmark_op( $op ); | |
if ( $results ) { | |
$this->stats['ops'][$op] = $results; | |
out( sprintf( "Completed '$op' with %d items in %f seconds", $items, $results['time_elapsed'] ) ); | |
out( "Succeded: {$results['success']}" ); | |
out( "Failed: {$results['fail']}" ); | |
out(); | |
} | |
} | |
return $this; | |
} | |
function benchmark_op( $op ) { | |
if ( ! method_exists( $this->mc, $op ) ) { | |
return false; | |
} | |
$success = $fail = 0; | |
$results = array(); | |
$time_start = microtime(true); | |
foreach ( $this->data as $key => $val ) { | |
if ( ! $key ) { | |
err( "Bad key: $key" ); | |
continue; | |
} | |
$args = ( $op == 'set' ) ? array( $key, $val ) : array( $key ); | |
$results = call_user_func_array( array( $this->mc, $op ), $args ); | |
if ( $results !== false ) { | |
$success++; | |
} else { | |
$fail++; | |
} | |
} | |
$time_elapsed = microtime(true) - $time_start; | |
return compact( 'time_elapsed', 'success', 'fail' ); | |
} | |
function get_stats() { | |
$results = array( $this->stats['time'], $this->stats['file'], $this->stats['servers'], | |
$this->stats['items'], $this->stats['bytes'] ); | |
foreach ( $this->stats['ops'] as $op => $stats ) { | |
$results[] = $stats['time_elapsed']; | |
$results[] = $stats['success']; | |
$results[] = $stats['fail']; | |
} | |
return $results; | |
} | |
} | |
function memcached_failure( $host, $tcp_port, $udp_port, $err_msg, $err_num ) { | |
out(); | |
err( "[$err_num] $err_msg ($host:$tcp_port)" ); | |
out(); | |
} | |
function report_open( $report_path, &$rh) { | |
$rh = fopen( $report_path, 'w' ); | |
} | |
function report_write_row( &$rh, $row ) { | |
fputcsv( $rh, (array) $row ); | |
} | |
function report_close( &$rh ) { | |
fclose( $rh ); | |
} | |
function out( $out = '' ) { | |
echo $out . PHP_EOL; | |
} | |
function err( $out ) { | |
out( "[!] $out" ); | |
} | |
function print_section( $title, $div = "=" ) { | |
out( str_repeat( $div, 80 ) ); | |
out( $title ); | |
out( str_repeat( $div, 80 ) ); | |
out(); | |
} | |
function usage() { | |
echo <<<OUT | |
./benchmark_memcached.php [-h] [-r /path/to/file.csv] /path/to/input node:port [node:port ...] | |
Optional arguments | |
-h|--help | |
Print this usage | |
-r|--report /path/to/file.csv | |
File path for CSV report output. Default is "results.csv" | |
Positional arguments: | |
/path/to/input | |
Path to JSON file or directory containing JSON files to load cache test data from | |
node:port [node:port] | |
One or more memcached host/port strings, i.e. | |
$ ./benchmark_memcached.php test.json 127.0.0.1:6991 | |
$ ./benchmark_memcached.php test.json 127.0.0.1:6991 127.0.0.1:6992 | |
OUT; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment