benchmarks: Add setup, bench naming, and custom count default
authorTimo Tijhof <krinklemail@gmail.com>
Wed, 19 Apr 2017 23:22:50 +0000 (16:22 -0700)
committerTim Starling <tstarling@wikimedia.org>
Thu, 20 Apr 2017 04:49:36 +0000 (04:49 +0000)
* bench(): Add support for setup function.
  Demonstrated by converting bench_delete_truncate.php to use Benchmarker.

* bench(): Allow benchmarks to be named. Default remains (fn + args).
  Useful for closures.

* Benchmarker: Support overriding the default count of 100.
  Demonstrated in bench_delete_truncate.php to run 10x instead of
  100x (previous: 1x).

Change-Id: Iac182eaf3053f5bf0e811cd23082f530629d8a4e

maintenance/benchmarks/Benchmarker.php
maintenance/benchmarks/bench_delete_truncate.php

index 70dc1f4..7d8280d 100644 (file)
@@ -35,6 +35,7 @@ require_once __DIR__ . '/../Maintenance.php';
  */
 abstract class Benchmarker extends Maintenance {
        private $results;
+       protected $defaultCount = 100;
 
        public function __construct() {
                parent::__construct();
@@ -42,31 +43,40 @@ abstract class Benchmarker extends Maintenance {
        }
 
        public function bench( array $benchs ) {
-               $bench_number = 0;
-               $count = $this->getOption( 'count', 100 );
-
-               foreach ( $benchs as $bench ) {
-                       // handle empty args
-                       if ( !array_key_exists( 'args', $bench ) ) {
+               $count = $this->getOption( 'count', $this->defaultCount );
+               foreach ( $benchs as $key => $bench ) {
+                       // Default to no arguments
+                       if ( !isset( $bench['args'] ) ) {
                                $bench['args'] = [];
                        }
 
-                       $bench_number++;
+                       // Optional setup called outside time measure
+                       if ( isset( $bench['setup'] ) ) {
+                               call_user_func( $bench['setup'] );
+                       }
                        $start = microtime( true );
                        for ( $i = 0; $i < $count; $i++ ) {
                                call_user_func_array( $bench['function'], $bench['args'] );
                        }
                        $delta = microtime( true ) - $start;
 
-                       // function passed as a callback
-                       if ( is_array( $bench['function'] ) ) {
-                               $ret = get_class( $bench['function'][0] ) . '->' . $bench['function'][1];
-                               $bench['function'] = $ret;
+                       // Name defaults to name of called function
+                       if ( is_string( $key ) ) {
+                               $name = $key;
+                       } else {
+                               if ( is_array( $bench['function'] ) ) {
+                                       $name = get_class( $bench['function'][0] ) . '::' . $bench['function'][1];
+                               } else {
+                                       $name = strval( $bench['function'] );
+                               }
+                               $name = sprintf( "%s(%s)",
+                                       $name,
+                                       implode( ', ', $bench['args'] )
+                               );
                        }
 
-                       $this->results[$bench_number] = [
-                               'function' => $bench['function'],
-                               'arguments' => $bench['args'],
+                       $this->results[] = [
+                               'name' => $name,
                                'count' => $count,
                                'total' => $delta,
                                'average' => $delta / $count,
@@ -84,10 +94,9 @@ abstract class Benchmarker extends Maintenance {
                );
                foreach ( $this->results as $res ) {
                        // show function with args
-                       $ret .= sprintf( "%s times: function %s(%s) :\n",
+                       $ret .= sprintf( "%s times: %s\n",
                                $res['count'],
-                               $res['function'],
-                               implode( ', ', $res['arguments'] )
+                               $res['name']
                        );
                        $ret .= sprintf( "   %6.2fms (%6.4fms each)\n",
                                $res['total'] * 1e3,
index 042f8bd..de655b1 100644 (file)
@@ -32,6 +32,8 @@ use Wikimedia\Rdbms\IMaintainableDatabase;
  * @ingroup Benchmark
  */
 class BenchmarkDeleteTruncate extends Benchmarker {
+       protected $defaultCount = 10;
+
        public function __construct() {
                parent::__construct();
                $this->addDescription( 'Benchmarks SQL DELETE vs SQL TRUNCATE.' );
@@ -46,29 +48,28 @@ class BenchmarkDeleteTruncate extends Benchmarker {
   text varbinary(255) NOT NULL
 );" );
 
-               $this->insertData( $dbw );
-
-               $start = microtime( true );
-
-               $this->delete( $dbw );
-
-               $end = microtime( true );
-
-               echo "Delete: " . sprintf( "%6.3fms", ( $end - $start ) * 1000 );
-               echo "\r\n";
-
-               $this->insertData( $dbw );
-
-               $start = microtime( true );
-
-               $this->truncate( $dbw );
-
-               $end = microtime( true );
-
-               echo "Truncate: " . sprintf( "%6.3fms", ( $end - $start ) * 1000 );
-               echo "\r\n";
+               $this->bench( [
+                       'Delete' => [
+                               'setup' => function () use ( $dbw ) {
+                                       $this->insertData( $dbw );
+                               },
+                               'function' => function () use ( $dbw ) {
+                                       $this->delete( $dbw );
+                               }
+                       ],
+                       'Truncate' => [
+                               'setup' => function () use ( $dbw ) {
+                                       $this->insertData( $dbw );
+                               },
+                               'function' => function () use ( $dbw ) {
+                                       $this->truncate( $dbw );
+                               }
+                       ]
+               ] );
 
                $dbw->dropTable( 'test' );
+
+               $this->output( $this->getFormattedResults() );
        }
 
        /**