Instantly share code, notes, and snippets.

Embed
What would you like to do?
Script to benchmark basic memcached operations (set, get and delete) with different server configurations.
#!/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