Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial source release

  • Loading branch information...
commit 447a195b0e9958666b479d2dddb79acf8cfb466a 1 parent 3f6eb63
David Sklar authored
Showing with 1,538 additions and 0 deletions.
  1. +4 −0 .gitignore
  2. +13 −0 LICENSE
  3. +145 −0 README
  4. +6 −0 b/array-key-exists-missing.php
  5. +6 −0 b/array-key-exists.php
  6. +50 −0 b/cache-chained-method-calls.php
  7. +7 −0 b/call-user-func.php
  8. +50 −0 b/chained-method-calls.php
  9. +6 −0 b/function-call.php
  10. +8 −0 b/instantiate-simple.php
  11. +5 −0 b/isset-missing.php
  12. +5 −0 b/isset.php
  13. +11 −0 b/magic-method-call.php
  14. +21 −0 b/magic-method-dispatch.php
  15. +13 −0 b/mb_strlen-1k.php
  16. +5 −0 b/mb_strlen.php
  17. +11 −0 b/method-call.php
  18. +4 −0 b/no-comments.php
  19. 0  b/overhead.php
  20. +12 −0 b/preg-ascii-1k.php
  21. +5 −0 b/preg-ascii.php
  22. +12 −0 b/preg-utf8-1k.php
  23. +5 −0 b/preg-utf8.php
  24. +62 −0 b/registry-direct-access.php
  25. +62 −0 b/registry-no-wrapper-function.php
  26. +81 −0 b/registry.php
  27. +8 −0 b/scan-byte-32.php
  28. +8 −0 b/scan-byte-64.php
  29. +13 −0 b/strlen-1k.php
  30. +5 −0 b/strlen.php
  31. +11 −0 b/without-registry.php
  32. +28 −0 b/word-filter-preg-args.php
  33. +29 −0 b/word-filter-preg-boundaries.php
  34. +28 −0 b/word-filter-preg.php
  35. +27 −0 b/word-filter-str-ireplace.php
  36. +135 −0 clock/clock-gettime.c
  37. +13 −0 clock/clock-gettime.h
  38. +134 −0 clock/clock.c
  39. +59 −0 clock/config.h
  40. +9 −0 clock/config.m4
  41. +13 −0 clock/config.w32
  42. +61 −0 clock/php_clock.h
  43. +21 −0 clock/tests/001.phpt
  44. +11 −0 clock/tests/002.phpt
  45. +11 −0 test/parse-opts.php
  46. +112 −0 ub
  47. +193 −0 ub.php
4 .gitignore
View
@@ -0,0 +1,4 @@
+.deps
+*.lo
+*.la
+*~
13 LICENSE
View
@@ -0,0 +1,13 @@
+Copyright 2010 Ning, Inc.
+
+Licensed under the Apache License, Version 2.0 (the "License"); you
+may not use this file except in compliance with the License. You may
+obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+implied. See the License for the specific language governing
+permissions and limitations under the License.
145 README
View
@@ -0,0 +1,145 @@
+-*-org-*-
+#+TITLE: ub
+
+* What
+
+ub is a PHP microbenchmarking framework. Its purpose is to make it
+fast and easy to write little code snippets and see how long they take
+to run. This is particularly useful if there are a few potential ways
+of solving a problem and you want to choose the fastest one.
+
+* Basics
+
+An ub microbenchmark is just a regular PHP script. It needs a few
+comments in a few places to tell ub what to measure.
+
+The most important comment is "// time". It tells ub to measure how
+long it takes to execute the code that comes after the comment. For
+example:
+
+<?php
+// time
+array_merge(array('alice'), array('alice','bob'));
+
+Save that code to a file called "array-merge.php" and then run:
+
+ub array-merge.php
+
+You'll get some output that looks like:
+
+ array-merge: mean=0.005670 median=0.005000 min=0.005000 max=0.027000 stdev=0.001417
+
+All times are in milliseconds. By default, ub runs your code 1000
+times and then reports the results across all 1000 iterations. The
+"-i" command line argument changes the number of iterations.
+
+Frequently, you need some setup code that you don't want to include in
+the timing but needs to be run on each benchmark iteration
+nonetheless. The ub comment that marks this kind of code is
+"// init". For example:
+
+<?php
+// init
+$numbers = range(0, 100);
+shuffle($numbers);
+// time
+sort($numbers);
+
+Each time ub executes a timing loop, it runs the range() and shuffle()
+functions, turns on the clock, runs sort(), and then turns off the
+clock. This ensures a potentially different array is shuffled in each
+iteration, but the time required to construct the shuffled array isn't
+part of the results.
+
+When benchmarking functions or classes, you need to define the
+function or class just once, not each time through the timing
+loop. That's where the "// once" comment comes in. Use that to denote
+code that should run just once, before the timing loop starts. For
+example:
+
+<?php
+// once
+function range_and_shuffle($size) {
+ $numbers = range(0, $size);
+ shuffle($numbers);
+ return $numbers;
+}
+// init
+$nums = range_and_shuffle(100);
+// time
+sort($nums);
+
+There are two other comments that are useful:
+- "// done" marks code that is run inside each loop *after* the code
+ to time is run. The "// done" code does not count towards the
+ measured times.
+- "// ignore" marks code that is ignored when benchmarking is
+ active. This is useful for debugging or other information you want
+ to print out when running the benchmark script through regular PHP
+ or through ub's runner mode.
+
+Only the "// time" comment is required. The rest are optional, but if
+they appear, have to be in order: once, init, time, done, ignore. You
+must use the "//" comment style. (And strictly speaking, "//time" is
+not required -- if you leave it out, the results will just reflect the
+overhead of two successive gettimeofday() calls in PHP.)
+
+* Options and Intracacies
+
+** Passing Options to Benchmarks
+
+Having to write entirely separate benchmark files for small variations
+(such as the number of elements in an array of numbers to sort) is
+tedious. Instead, use ub's option passing capabilities. The -x
+command-line argument, which can be specified more than once, tells ub
+to populate an $___opts array that will be available to the benchmark
+file. Depending on how -x is used, different values show up in
+$___opts. For example:
+
+ Command line: no -x arguments
+$___opts structure: array()
+
+ Command line: -x asparagus
+$___opts structure: array('asparagus' => true)
+
+ Command line: -x asparagus -x broccoli=green
+$___opts structure: array('asparagus' => true, 'broccoli' => 'green')
+
+ Command line: -x asparagus -x broccoli=green -x broccoli=12
+$___opts structure: array('asparagus' => true, 'broccoli' => array('green','12')
+
+-x values with = in them are converted to key/value pairs (and the
+values are always strings); -x values without = result in a value of
+boolean true. Multiple keys result in $___opts array values which are
+themselves values.
+
+** Runner Mode
+
+If you've created a benchmark file that uses $___opts, you've lost a
+little bit of the ease of testing your benchmarking file by running it
+through regular PHP -- there is no $___opts array available. ub's
+"runner mode", activated with the -r command line argument helps with
+this. Runner mode populates $___opts from command line arguments and
+runs the benchmark file -- that's it. No iteration, no special
+attention to the comments that delimit the file. After runner mode
+builds $___opts, your benchmark file is run as usual.
+
+** Multiple Benchmarks
+
+You can supply multiple benchmark names or filenames on the
+commandline to run them all in one go. Internally, ub uses a separate
+PHP process for each benchmark. This prevents any collision between
+classes or functions that may have the same name in separate benchmark
+files.
+
+** Clock Extension
+
+ub uses PHP's gettimeofday() function (which uses the underlying
+gettimeofday(2) system call) to obtain wall clock time before and
+after the code to be timed is run. The experimental extension in the
+"clock" subdirectory provides a PHP interface to the clock_gettime(3)
+Linux system call. For now, it just offers the CLOCK_MONOTONIC clock,
+but this can provide a higher granularity measure of elapsed time with
+less overhead. The clock-gettime.[ch] files in this directory provide
+OS X functions that duplicate Linux's clock_gettime(3) functionality.
+
6 b/array-key-exists-missing.php
View
@@ -0,0 +1,6 @@
+<?php
+// init
+$a = array('alice' => 12);
+// time
+array_key_exists('bob', $a);
+
6 b/array-key-exists.php
View
@@ -0,0 +1,6 @@
+<?php
+// init
+$a = array('alice' => 12);
+// time
+array_key_exists('alice', $a);
+
50 b/cache-chained-method-calls.php
View
@@ -0,0 +1,50 @@
+<?php
+// once
+class Library {
+ function doSomething($arg) {
+ $a = 1 + 2;
+ $a *= $arg;
+ return $a;
+ }
+}
+
+class Component {
+ private $libs = array();
+
+ public function lib($lib) {
+ if (! isset($this->libs[$lib])) {
+ $this->libs[$lib] = new Library;
+ $this->libs[$lib]->name = "library=$lib";
+ }
+ return $this->libs[$lib];
+ }
+
+ public function clearLibs() {
+ $this->libs = array();
+ }
+}
+
+class Controller {
+
+ public function __construct() {
+ $this->C = new Component();
+ }
+
+ public function withLocal() {
+ $lib = $this->C->lib('photo');
+ $value1 = $lib->doSomething(12);
+ $value2 = $lib->doSomething(24);
+ }
+
+ public function withCalls() {
+ $value1 = $this->C->lib('photo')->doSomething(12);
+ $value2 = $this->C->lib('photo')->doSomething(24);
+ }
+}
+
+$c = new Controller;
+
+// init
+$c->C->clearLibs();
+// time
+$c->withLocal();
7 b/call-user-func.php
View
@@ -0,0 +1,7 @@
+<?php
+// once
+function alice() {
+}
+// time
+call_user_func('alice');
+
50 b/chained-method-calls.php
View
@@ -0,0 +1,50 @@
+<?php
+// once
+class Library {
+ function doSomething($arg) {
+ $a = 1 + 2;
+ $a *= $arg;
+ return $a;
+ }
+}
+
+class Component {
+ private $libs = array();
+
+ public function lib($lib) {
+ if (! isset($this->libs[$lib])) {
+ $this->libs[$lib] = new Library;
+ $this->libs[$lib]->name = "library=$lib";
+ }
+ return $this->libs[$lib];
+ }
+
+ public function clearLibs() {
+ $this->libs = array();
+ }
+}
+
+class Controller {
+
+ public function __construct() {
+ $this->C = new Component();
+ }
+
+ public function withLocal() {
+ $lib = $this->C->lib('photo');
+ $value1 = $lib->doSomething(12);
+ $value2 = $lib->doSomething(24);
+ }
+
+ public function withCalls() {
+ $value1 = $this->C->lib('photo')->doSomething(12);
+ $value2 = $this->C->lib('photo')->doSomething(24);
+ }
+}
+
+$c = new Controller;
+
+// init
+$c->C->clearLibs();
+// time
+$c->withCalls();
6 b/function-call.php
View
@@ -0,0 +1,6 @@
+<?php
+// once
+function alice() {
+}
+// time
+alice();
8 b/instantiate-simple.php
View
@@ -0,0 +1,8 @@
+<?php
+// once
+class Simple {
+ public function minded() {
+ }
+}
+// time
+$a = new Simple;
5 b/isset-missing.php
View
@@ -0,0 +1,5 @@
+<?php
+// init
+$a = array('alice' => 12);
+// time
+isset($a['bob']);
5 b/isset.php
View
@@ -0,0 +1,5 @@
+<?php
+// init
+$a = array('alice' => 12);
+// time
+isset($a['alice']);
11 b/magic-method-call.php
View
@@ -0,0 +1,11 @@
+<?php
+// once
+class CallMyFunction {
+ public function __call($method, $args) {
+ return 0;
+ }
+}
+// init
+$c = new CallMyFunction();
+// time
+$c->please();
21 b/magic-method-dispatch.php
View
@@ -0,0 +1,21 @@
+<?php
+// once
+class CallMyFunction {
+ public function __call($method, $args) {
+ if ($method == 'please') {
+ return call_user_func_array(array($this, 'doPlease'), $args);
+ }
+ return 0;
+ }
+
+ protected function doPlease() {
+ return 0;
+ }
+
+}
+// init
+$c = new CallMyFunction();
+// time
+$c->please();
+// ignore
+print "c->please = " . $c->please() . "\n";
13 b/mb_strlen-1k.php
View
@@ -0,0 +1,13 @@
+<?php
+// once
+function gen_string($len) {
+ $s = '';
+ while ($len--) {
+ $s .= chr(mt_rand(33,126));
+ }
+ return $s;
+}
+// init
+$s = gen_string(1024);
+// time
+$j = mb_strlen($s);
5 b/mb_strlen.php
View
@@ -0,0 +1,5 @@
+<?php
+// init
+$s = md5(mt_rand()) . md5(mt_rand());
+// time
+$j = mb_strlen($s);
11 b/method-call.php
View
@@ -0,0 +1,11 @@
+<?php
+// once
+class CallMyFunction {
+ public function please() {
+ return 0;
+ }
+}
+// init
+$c = new CallMyFunction();
+// time
+$c->please();
4 b/no-comments.php
View
@@ -0,0 +1,4 @@
+<?php
+$s = 'موقع البي بي سي للأخبار بالعربية، يضم بثا إذاعيا مباشرا وأقوال الصحف بالاضافة لتغطيته لاهم الاحداث على شكل ملفات مفصلة.';
+mb_strlen($s);
+?>
0  b/overhead.php
View
No changes.
12 b/preg-ascii-1k.php
View
@@ -0,0 +1,12 @@
+<?php
+// once
+function gen_string($len) {
+ $s = '';
+ while ($len--) {
+ $s .= chr(mt_rand(33,126));
+ }
+ return $s;
+}
+$s = gen_string(1024);
+// time
+preg_match('/<[^>]+?>/', $s);
5 b/preg-ascii.php
View
@@ -0,0 +1,5 @@
+<?php
+// init
+$s = "this is some text to match against. it's not short but not incredibly long";
+// time
+preg_match('/<[^>]+?>/', $s);
12 b/preg-utf8-1k.php
View
@@ -0,0 +1,12 @@
+<?php
+// once
+function gen_string($len) {
+ $s = '';
+ while ($len--) {
+ $s .= chr(mt_rand(33,126));
+ }
+ return $s;
+}
+$s = gen_string(1024);
+// time
+preg_match('/<[^>]+?>/u', $s);
5 b/preg-utf8.php
View
@@ -0,0 +1,5 @@
+<?php
+// init
+$s = "this is some text to match against. it's not short but not incredibly long";
+// time
+preg_match('/<[^>]+?>/u', $s);
62 b/registry-direct-access.php
View
@@ -0,0 +1,62 @@
+<?php
+// once
+class Registry {
+ /** The map of keys to objects */
+ public static $registry = array();
+
+ /**
+ * Retrieve an object, or null if there is no object with
+ * the provided key.
+ *
+ * @param $key string
+ * @return object|null
+ */
+ public static function get($key) {
+ return isset(self::$registry[$key]) ? self::$registry[$key] : null;
+ }
+
+ /**
+ * Put an object, or clear an entry
+ *
+ * @param $key string
+ * @param $instance object|null. If an object, put that object
+ * with key $key. If null, clear the entry at key
+ * $key.
+ *
+ * @return null
+ */
+ public static function set($key, $instance) {
+ if ((! is_object($instance)) && (! is_null($instance))) {
+ throw new Exception("Expected object for instance of $key");
+ }
+ self::$registry[$key] = $instance;
+ }
+ /**
+ * Get all entries
+ * @return array
+ */
+ public static function getAll() {
+ return self::$registry;
+ }
+
+ /**
+ * Overwrite all entries with the provided array
+ * of keys => objects
+ *
+ * @param $registry array
+ */
+ public static function setAll($registry) {
+ self::$registry = $registry;
+ }
+}
+
+class Helper {
+ public function help() {
+ }
+}
+// init
+Registry::setAll(array('helper' => new Helper));
+// time
+for ($j = 0; $j < $___opts['m']; $j++) {
+ Registry::$registry['helper']->help();
+}
62 b/registry-no-wrapper-function.php
View
@@ -0,0 +1,62 @@
+<?php
+// once
+class Registry {
+ /** The map of keys to objects */
+ protected static $registry = array();
+
+ /**
+ * Retrieve an object, or null if there is no object with
+ * the provided key.
+ *
+ * @param $key string
+ * @return object|null
+ */
+ public static function get($key) {
+ return isset(self::$registry[$key]) ? self::$registry[$key] : null;
+ }
+
+ /**
+ * Put an object, or clear an entry
+ *
+ * @param $key string
+ * @param $instance object|null. If an object, put that object
+ * with key $key. If null, clear the entry at key
+ * $key.
+ *
+ * @return null
+ */
+ public static function set($key, $instance) {
+ if ((! is_object($instance)) && (! is_null($instance))) {
+ throw new Exception("Expected object for instance of $key");
+ }
+ self::$registry[$key] = $instance;
+ }
+ /**
+ * Get all entries
+ * @return array
+ */
+ public static function getAll() {
+ return self::$registry;
+ }
+
+ /**
+ * Overwrite all entries with the provided array
+ * of keys => objects
+ *
+ * @param $registry array
+ */
+ public static function setAll($registry) {
+ self::$registry = $registry;
+ }
+}
+
+class Helper {
+ public function help() {
+ }
+}
+// init
+Registry::setAll(array('helper' => new Helper));
+// time
+for ($j = 0; $j < $___opts['m']; $j++) {
+ Registry::get('helper')->help();
+}
81 b/registry.php
View
@@ -0,0 +1,81 @@
+<?php
+// once
+/**
+ * Set or retrieve an object
+ *
+ * @param $key string entry key to set or get
+ * @param $instance optional object. If provided, set the entry
+ * for $key to this object. If not provided, return the entry
+ * for $key. If null, clear the entry for $key.
+ *
+ * @return object|null
+ */
+function Registry($key, $instance = -1) {
+ if ($instance === -1) {
+ return Registry::get($key);
+ } else {
+ return Registry::set($key, $instance);
+ }
+}
+
+class Registry {
+ /** The map of keys to objects */
+ protected static $registry = array();
+
+ /**
+ * Retrieve an object, or null if there is no object with
+ * the provided key.
+ *
+ * @param $key string
+ * @return object|null
+ */
+ public static function get($key) {
+ return isset(self::$registry[$key]) ? self::$registry[$key] : null;
+ }
+
+ /**
+ * Put an object, or clear an entry
+ *
+ * @param $key string
+ * @param $instance object|null. If an object, put that object
+ * into the with key $key. If null, clear the entry at key
+ * $key.
+ *
+ * @return null
+ */
+ public static function set($key, $instance) {
+ if ((! is_object($instance)) && (! is_null($instance))) {
+ throw new Exception("Expected object for instance of $key");
+ }
+ self::$registry[$key] = $instance;
+ }
+ /**
+ * Get all entries
+ * @return array
+ */
+ public static function getAll() {
+ return self::$registry;
+ }
+
+ /**
+ * Overwrite all entries in the with the provided array
+ * of keys => objects
+ *
+ * @param $registry array
+ */
+ public static function setAll($registry) {
+ self::$registry = $registry;
+ }
+}
+
+class Helper {
+ public function help() {
+ }
+}
+// init
+Registry::setAll(array()); /* clear */
+Registry('helper', new Helper);
+// time
+for ($j = 0; $j < $___opts['m']; $j++) {
+ Registry('helper')->help();
+}
8 b/scan-byte-32.php
View
@@ -0,0 +1,8 @@
+<?php
+// init
+$s = md5(mt_rand());
+$j = strlen($s);
+// time
+for ($i = 0; $i < $j; $i++) {
+ $b = $s[$i];
+}
8 b/scan-byte-64.php
View
@@ -0,0 +1,8 @@
+<?php
+// init
+$s = md5(mt_rand()) . md5(mt_rand());
+$j = strlen($s);
+// time
+for ($i = 0; $i < $j; $i++) {
+ $b = $s[$i];
+}
13 b/strlen-1k.php
View
@@ -0,0 +1,13 @@
+<?php
+// once
+function gen_string($len) {
+ $s = '';
+ while ($len--) {
+ $s .= chr(mt_rand(33,126));
+ }
+ return $s;
+}
+// init
+$s = gen_string(1024);
+// time
+$j = strlen($s);
5 b/strlen.php
View
@@ -0,0 +1,5 @@
+<?php
+// init
+$s = md5(mt_rand()) . md5(mt_rand());
+// time
+$j = strlen($s);
11 b/without-registry.php
View
@@ -0,0 +1,11 @@
+<?php
+// once
+class Helper {
+ public function help() {
+ }
+}
+// time
+for ($j = 0; $j < $___opts['m']; $j++) {
+ $h = new Helper;
+ $h->help();
+}
28 b/word-filter-preg-args.php
View
@@ -0,0 +1,28 @@
+<?php
+//init
+$raw ='
+At the same time in England, a number of illustrious collections were being formed. Many of these collections are well known and need only to be mentioned here. Among these were the collection of Ole Worm, whose Museum Wormianum achieved great fame. Another collection of rarities was preserved at South Lambeth by Elias Ashmole. Mr Ashmole, a botanist, presented his collection to his friend and neighbor Samuel Dule (author of the Pharmacologia) to whom it was delivered one week before Mr. Dule\'s death.
+
+Through the second half of the eighteenth century and into the nineteenth century it became fashionable to donate these collections to budding public institutions. The first of these "public" museums were little more than formalized displays of private collections of rarities and curios, often with little regard for any meaningful order of display. These institutions, though public in name, were accessible in fact only to the cognoscenti and then only by appointment, in small groups, and for limited periods of time.
+
+However, at the same time, in the city of Philadelphia in America, Charles Willson Peale was forming a museum that was to become a model for the institution for years to come. Mr. Peale\'s Museum was open to all people (including children and the fair sex) and was philosophically grounded in the thoughts of Jean Jacques Rousseau. Peale fervently believed that teaching is a sublime ministry inseparable from human happiness, and that the learner must be led always from familiar objects toward the unfamiliar - guided along, as it were, a chain of flowers into the mysteries of life.
+
+"Rational amusement" was the Peale Museum\'s instrument but also, by curious irony, its eventual undoing. Imitators sprang up almost at once. A collection of oddities, unencumbered by scientific purpose was found to be "good business". Tawdry and specious museums soon appeared in almost every American city and town. This unsavory tendency finally reached its peak with Barnum, who in the end obtained, scattered, and ultimately incinerated, the Peale collections.
+
+The Museum of Jurassic Technology traces its origins to this period when many of the important collections of today were beginning to take form. Many exhibits which we today have come to know as part of the Museum were, in fact, formally part of other less well known collections and were subsequently consolidated into the single collection which we have come to know as The Museum of Jurassic Technology and thus configured, received great public acclaim as well as much discussion in scholastic circles.
+';
+
+$words = (array) $___opts['words'];
+
+$patterns = array();
+$replacements = array();
+foreach ($words as $word) {
+ $patterns[] = '/' . $word . '/ui';
+ $replacements[] = $word[0] . str_repeat('*', strlen($word) - 1);
+}
+
+// time
+/* museum = 1, Museum = 6 */
+$matches = preg_replace($patterns, $replacements, $raw);
+// ignore
+var_dump($matches);
29 b/word-filter-preg-boundaries.php
View
@@ -0,0 +1,29 @@
+<?php
+//init
+$raw ='
+At the same time in England, a number of illustrious collections were being formed. Many of these collections are well known and need only to be mentioned here. Among these were the collection of Ole Worm, whose Museum Wormianum achieved great fame. Another collection of rarities was preserved at South Lambeth by Elias Ashmole. Mr Ashmole, a botanist, presented his collection to his friend and neighbor Samuel Dule (author of the Pharmacologia) to whom it was delivered one week before Mr. Dule\'s death.
+
+Through the second half of the eighteenth century and into the nineteenth century it became fashionable to donate these collections to budding public institutions. The first of these "public" museums were little more than formalized displays of private collections of rarities and curios, often with little regard for any meaningful order of display. These institutions, though public in name, were accessible in fact only to the cognoscenti and then only by appointment, in small groups, and for limited periods of time.
+
+However, at the same time, in the city of Philadelphia in America, Charles Willson Peale was forming a museum that was to become a model for the institution for years to come. Mr. Peale\'s Museum was open to all people (including children and the fair sex) and was philosophically grounded in the thoughts of Jean Jacques Rousseau. Peale fervently believed that teaching is a sublime ministry inseparable from human happiness, and that the learner must be led always from familiar objects toward the unfamiliar - guided along, as it were, a chain of flowers into the mysteries of life.
+
+"Rational amusement" was the Peale Museum\'s instrument but also, by curious irony, its eventual undoing. Imitators sprang up almost at once. A collection of oddities, unencumbered by scientific purpose was found to be "good business". Tawdry and specious museums soon appeared in almost every American city and town. This unsavory tendency finally reached its peak with Barnum, who in the end obtained, scattered, and ultimately incinerated, the Peale collections.
+
+The Museum of Jurassic Technology traces its origins to this period when many of the important collections of today were beginning to take form. Many exhibits which we today have come to know as part of the Museum were, in fact, formally part of other less well known collections and were subsequently consolidated into the single collection which we have come to know as The Museum of Jurassic Technology and thus configured, received great public acclaim as well as much discussion in scholastic circles.
+';
+
+$words = array('museum','origins','monkeys','bob','alligator','business','turtles','scholas','zip','zap','zoom','races');
+
+$patterns = array();
+$replacements = array();
+foreach ($words as $word) {
+ $patterns[] = '/\b' . $word . '/ui';
+ $replacements[] = $word[0] . str_repeat('*', strlen($word) - 1);
+ $patterns[] = '/' . $word . '\b/ui';
+ $replacements[] = $word[0] . str_repeat('*', strlen($word) - 1);
+}
+
+// time
+$matches = preg_replace($patterns, $replacements, $raw);
+// ignore
+var_dump($matches);
28 b/word-filter-preg.php
View
@@ -0,0 +1,28 @@
+<?php
+//init
+$raw ='
+At the same time in England, a number of illustrious collections were being formed. Many of these collections are well known and need only to be mentioned here. Among these were the collection of Ole Worm, whose Museum Wormianum achieved great fame. Another collection of rarities was preserved at South Lambeth by Elias Ashmole. Mr Ashmole, a botanist, presented his collection to his friend and neighbor Samuel Dule (author of the Pharmacologia) to whom it was delivered one week before Mr. Dule\'s death.
+
+Through the second half of the eighteenth century and into the nineteenth century it became fashionable to donate these collections to budding public institutions. The first of these "public" museums were little more than formalized displays of private collections of rarities and curios, often with little regard for any meaningful order of display. These institutions, though public in name, were accessible in fact only to the cognoscenti and then only by appointment, in small groups, and for limited periods of time.
+
+However, at the same time, in the city of Philadelphia in America, Charles Willson Peale was forming a museum that was to become a model for the institution for years to come. Mr. Peale\'s Museum was open to all people (including children and the fair sex) and was philosophically grounded in the thoughts of Jean Jacques Rousseau. Peale fervently believed that teaching is a sublime ministry inseparable from human happiness, and that the learner must be led always from familiar objects toward the unfamiliar - guided along, as it were, a chain of flowers into the mysteries of life.
+
+"Rational amusement" was the Peale Museum\'s instrument but also, by curious irony, its eventual undoing. Imitators sprang up almost at once. A collection of oddities, unencumbered by scientific purpose was found to be "good business". Tawdry and specious museums soon appeared in almost every American city and town. This unsavory tendency finally reached its peak with Barnum, who in the end obtained, scattered, and ultimately incinerated, the Peale collections.
+
+The Museum of Jurassic Technology traces its origins to this period when many of the important collections of today were beginning to take form. Many exhibits which we today have come to know as part of the Museum were, in fact, formally part of other less well known collections and were subsequently consolidated into the single collection which we have come to know as The Museum of Jurassic Technology and thus configured, received great public acclaim as well as much discussion in scholastic circles.
+';
+
+$words = array('museum','origins','monkeys','bob','alligator','business','turtles','scholas','zip','zap','zoom','races');
+
+$patterns = array();
+$replacements = array();
+foreach ($words as $word) {
+ $patterns[] = '/' . $word . '/ui';
+ $replacements[] = $word[0] . str_repeat('*', strlen($word) - 1);
+}
+
+// time
+/* museum = 1, Museum = 6 */
+$matches = preg_replace($patterns, $replacements, $raw);
+// ignore
+var_dump($matches);
27 b/word-filter-str-ireplace.php
View
@@ -0,0 +1,27 @@
+<?php
+//init
+$raw ='
+At the same time in England, a number of illustrious collections were being formed. Many of these collections are well known and need only to be mentioned here. Among these were the collection of Ole Worm, whose Museum Wormianum achieved great fame. Another collection of rarities was preserved at South Lambeth by Elias Ashmole. Mr Ashmole, a botanist, presented his collection to his friend and neighbor Samuel Dule (author of the Pharmacologia) to whom it was delivered one week before Mr. Dule\'s death.
+
+Through the second half of the eighteenth century and into the nineteenth century it became fashionable to donate these collections to budding public institutions. The first of these "public" museums were little more than formalized displays of private collections of rarities and curios, often with little regard for any meaningful order of display. These institutions, though public in name, were accessible in fact only to the cognoscenti and then only by appointment, in small groups, and for limited periods of time.
+
+However, at the same time, in the city of Philadelphia in America, Charles Willson Peale was forming a museum that was to become a model for the institution for years to come. Mr. Peale\'s Museum was open to all people (including children and the fair sex) and was philosophically grounded in the thoughts of Jean Jacques Rousseau. Peale fervently believed that teaching is a sublime ministry inseparable from human happiness, and that the learner must be led always from familiar objects toward the unfamiliar - guided along, as it were, a chain of flowers into the mysteries of life.
+
+"Rational amusement" was the Peale Museum\'s instrument but also, by curious irony, its eventual undoing. Imitators sprang up almost at once. A collection of oddities, unencumbered by scientific purpose was found to be "good business". Tawdry and specious museums soon appeared in almost every American city and town. This unsavory tendency finally reached its peak with Barnum, who in the end obtained, scattered, and ultimately incinerated, the Peale collections.
+
+The Museum of Jurassic Technology traces its origins to this period when many of the important collections of today were beginning to take form. Many exhibits which we today have come to know as part of the Museum were, in fact, formally part of other less well known collections and were subsequently consolidated into the single collection which we have come to know as The Museum of Jurassic Technology and thus configured, received great public acclaim as well as much discussion in scholastic circles.
+';
+
+$words = array('museum','origins','monkeys','bob','alligator','business','turtles','scholas','zip','zap','zoom','races');
+
+$patterns = array();
+$replacements = array();
+foreach ($words as $word) {
+ $patterns[] = $word ;
+ $replacements[] = $word[0] . str_repeat('*', strlen($word) - 1);
+}
+
+// time
+$matches = str_ireplace($patterns, $replacements, $raw);
+// ignore
+var_dump($matches);
135 clock/clock-gettime.c
View
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c), MM Weiss
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the MM Weiss nor the names of its contributors
+ * may be used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * clock_gettime_stub.c
+ * gcc -Wall -c clock_gettime_stub.c
+ * posix realtime functions; MacOS user space glue
+ */
+
+/* @comment
+ * other possible implementation using intel builtin rdtsc
+ * rdtsc-workaround: http://www.mcs.anl.gov/~kazutomo/rdtsc.html
+ *
+ * we could get the ticks by doing this
+ *
+ * __asm __volatile("mov %%ebx, %%esi\n\t"
+ * "cpuid\n\t"
+ * "xchg %%esi, %%ebx\n\t"
+ * "rdtsc"
+ * : "=a" (a),
+ * "=d" (d)
+ * );
+
+ * we could even replace our tricky sched_yield call by assembly code to get a better accurency,
+ * anyway the following C stub will satisfy 99% of apps using posix clock_gettime call,
+ * moreover, the setter version (clock_settime) could be easly written using mach primitives:
+ * http://www.opensource.apple.com/source/xnu/xnu-${VERSION}/osfmk/man/ (clock_[set|get]_time)
+ *
+ * hackers don't be crackers, don't you use a flush toilet?
+ *
+ *
+ * @see draft: ./posix-realtime-stub/posix-realtime-stub.c
+ *
+ */
+
+
+#ifdef __APPLE__
+
+#pragma weak clock_gettime
+
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <mach/mach.h>
+#include <mach/clock.h>
+#include <mach/mach_time.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sched.h>
+#include "clock-gettime.h"
+
+static mach_timebase_info_data_t __clock_gettime_inf;
+
+int clock_gettime(clockid_t clk_id, struct timespec *tp) {
+ kern_return_t ret;
+ clock_serv_t clk;
+ clock_id_t clk_serv_id;
+ mach_timespec_t tm;
+
+ uint64_t start, end, delta, nano;
+
+ /* task_basic_info_data_t tinfo; */
+ /* task_thread_times_info_data_t ttinfo; */
+ /* mach_msg_type_number_t tflag; */
+
+ int retval = -1;
+ switch (clk_id) {
+ case CLOCK_REALTIME:
+ case CLOCK_MONOTONIC:
+ clk_serv_id = clk_id == CLOCK_REALTIME ? CALENDAR_CLOCK : SYSTEM_CLOCK;
+ if (KERN_SUCCESS == (ret = host_get_clock_service(mach_host_self(), clk_serv_id, &clk))) {
+ if (KERN_SUCCESS == (ret = clock_get_time(clk, &tm))) {
+ tp->tv_sec = tm.tv_sec;
+ tp->tv_nsec = tm.tv_nsec;
+ retval = 0;
+ }
+ }
+ if (KERN_SUCCESS != ret) {
+ errno = EINVAL;
+ retval = -1;
+ }
+ break;
+ case CLOCK_PROCESS_CPUTIME_ID:
+ case CLOCK_THREAD_CPUTIME_ID:
+ start = mach_absolute_time();
+ if (clk_id == CLOCK_PROCESS_CPUTIME_ID) {
+ getpid();
+ } else {
+ sched_yield();
+ }
+ end = mach_absolute_time();
+ delta = end - start;
+ if (0 == __clock_gettime_inf.denom) {
+ mach_timebase_info(&__clock_gettime_inf);
+ }
+ nano = delta * __clock_gettime_inf.numer / __clock_gettime_inf.denom;
+ tp->tv_sec = nano * 1e-9;
+ tp->tv_nsec = nano - (tp->tv_sec * 1e9);
+ retval = 0;
+ break;
+ default:
+ errno = EINVAL;
+ retval = -1;
+ }
+ return retval;
+}
+
+#endif // __APPLE__
+
+/* EOF */
13 clock/clock-gettime.h
View
@@ -0,0 +1,13 @@
+#ifdef __APPLE__
+
+typedef enum {
+ CLOCK_REALTIME,
+ CLOCK_MONOTONIC,
+ CLOCK_PROCESS_CPUTIME_ID,
+ CLOCK_THREAD_CPUTIME_ID
+} clockid_t;
+
+int clock_gettime(clockid_t clk_id, struct timespec *tp);
+
+#endif
+
134 clock/clock.c
View
@@ -0,0 +1,134 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2008 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id: header 252479 2008-02-07 19:39:50Z iliaa $ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+#include "php_ini.h"
+#include "ext/standard/info.h"
+#include "php_clock.h"
+#include "clock-gettime.h"
+
+/* If you declare any globals in php_clock.h uncomment this:
+ZEND_DECLARE_MODULE_GLOBALS(clock)
+*/
+
+/* True global resources - no need for thread safety here */
+// static int le_clock;
+
+/* {{{ clock_functions[]
+ *
+ * Every user visible function must have an entry in clock_functions[].
+ */
+const zend_function_entry clock_functions[] = {
+ PHP_FE(clock_gettime, NULL)
+ {NULL, NULL, NULL} /* Must be the last line in clock_functions[] */
+};
+/* }}} */
+
+/* {{{ clock_module_entry
+ */
+zend_module_entry clock_module_entry = {
+#if ZEND_MODULE_API_NO >= 20010901
+ STANDARD_MODULE_HEADER,
+#endif
+ "clock",
+ clock_functions,
+ PHP_MINIT(clock),
+ PHP_MSHUTDOWN(clock),
+ NULL, /* PHP_RINIT(clock), */
+ NULL, /* PHP_RSHUTDOWN(clock), */
+ PHP_MINFO(clock),
+#if ZEND_MODULE_API_NO >= 20010901
+ "0.1",
+#endif
+ STANDARD_MODULE_PROPERTIES
+};
+/* }}} */
+
+#ifdef COMPILE_DL_CLOCK
+ZEND_GET_MODULE(clock)
+#endif
+
+/* {{{ PHP_INI
+ */
+/* PHP_INI_BEGIN() */
+/* PHP_INI_END() */
+/* }}} */
+
+/* {{{ PHP_MINIT_FUNCTION
+ */
+PHP_MINIT_FUNCTION(clock)
+{
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MSHUTDOWN_FUNCTION
+ */
+PHP_MSHUTDOWN_FUNCTION(clock)
+{
+ return SUCCESS;
+}
+/* }}} */
+
+/* {{{ PHP_MINFO_FUNCTION
+ */
+PHP_MINFO_FUNCTION(clock)
+{
+ php_info_print_table_start();
+ php_info_print_table_header(2, "clock support", "enabled");
+ php_info_print_table_end();
+
+}
+/* }}} */
+
+/* {{{ proto array clock_gettime(int clock_type)
+ Return the time from the given clock */
+PHP_FUNCTION(clock_gettime)
+{
+ long clock_type;
+ struct timespec tp;
+ int c;
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &clock_type) == FAILURE) {
+ return;
+ }
+
+ /* todo actually match clock_type to a defined constant */
+ c = clock_gettime(CLOCK_MONOTONIC, &tp);
+
+ array_init(return_value);
+ add_next_index_long(return_value, tp.tv_sec);
+ add_next_index_long(return_value, tp.tv_nsec);
+
+ return;
+}
+/* }}} */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
59 clock/config.h
View
@@ -0,0 +1,59 @@
+/* config.h. Generated from config.h.in by configure. */
+/* config.h.in. Generated from configure.in by autoheader. */
+
+/* Whether to build clock as dynamic module */
+#define COMPILE_DL_CLOCK 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+/* #undef NO_MINUS_C_MINUS_O */
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME ""
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING ""
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME ""
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION ""
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
9 clock/config.m4
View
@@ -0,0 +1,9 @@
+dnl $Id$
+dnl config.m4 for extension clock
+
+PHP_ARG_ENABLE(clock, whether to enable clock support,
+[ --enable-clock Enable clock support])
+
+if test "$PHP_CLOCK" != "no"; then
+ PHP_NEW_EXTENSION(clock, clock.c clock-gettime.c, $ext_shared)
+fi
13 clock/config.w32
View
@@ -0,0 +1,13 @@
+// $Id$
+// vim:ft=javascript
+
+// If your extension references something external, use ARG_WITH
+// ARG_WITH("clock", "for clock support", "no");
+
+// Otherwise, use ARG_ENABLE
+// ARG_ENABLE("clock", "enable clock support", "no");
+
+if (PHP_CLOCK != "no") {
+ EXTENSION("clock", "clock.c");
+}
+
61 clock/php_clock.h
View
@@ -0,0 +1,61 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2008 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Author: |
+ +----------------------------------------------------------------------+
+*/
+
+/* $Id: header 252479 2008-02-07 19:39:50Z iliaa $ */
+
+#ifndef PHP_CLOCK_H
+#define PHP_CLOCK_H
+
+extern zend_module_entry clock_module_entry;
+#define phpext_clock_ptr &clock_module_entry
+
+#ifdef PHP_WIN32
+# define PHP_CLOCK_API __declspec(dllexport)
+#elif defined(__GNUC__) && __GNUC__ >= 4
+# define PHP_CLOCK_API __attribute__ ((visibility("default")))
+#else
+# define PHP_CLOCK_API
+#endif
+
+#ifdef ZTS
+#include "TSRM.h"
+#endif
+
+PHP_MINIT_FUNCTION(clock);
+PHP_MSHUTDOWN_FUNCTION(clock);
+PHP_MINFO_FUNCTION(clock);
+
+PHP_FUNCTION(clock_gettime);
+
+#ifdef ZTS
+#define CLOCK_G(v) TSRMG(clock_globals_id, zend_clock_globals *, v)
+#else
+#define CLOCK_G(v) (clock_globals.v)
+#endif
+
+#endif /* PHP_CLOCK_H */
+
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: noet sw=4 ts=4 fdm=marker
+ * vim<600: noet sw=4 ts=4
+ */
21 clock/tests/001.phpt
View
@@ -0,0 +1,21 @@
+--TEST--
+Check for clock presence
+--SKIPIF--
+<?php if (!extension_loaded("clock")) print "skip"; ?>
+--FILE--
+<?php
+echo "clock extension is available";
+/*
+ you can add regression tests for your extension here
+
+ the output of your test code has to be equal to the
+ text in the --EXPECT-- section below for the tests
+ to pass, differences between the output and the
+ expected text are interpreted as failure
+
+ see php5/README.TESTING for further information on
+ writing regression tests
+*/
+?>
+--EXPECT--
+clock extension is available
11 clock/tests/002.phpt
View
@@ -0,0 +1,11 @@
+--TEST--
+Check for clock_gettime() function
+--SKIPIF--
+<?php if (!extension_loaded("clock")) print "skip"; ?>
+--FILE--
+<?php
+$a = clock_gettime(0);
+print is_array($a) ? 1 : 0;
+?>
+--EXPECT--
+1
11 test/parse-opts.php
View
@@ -0,0 +1,11 @@
+<?php
+
+include __DIR__ . '/../ub.php';
+
+assert(parse_opts('ab',array()) === array('_' => array(), 'a' => false, 'b' => false));
+assert(parse_opts('a:b',array()) === array('_' => array(), 'a' => null, 'b' => false));
+assert(parse_opts('a:b',array('-a','alice')) === array('_' => array(), 'a' => 'alice', 'b' => false));
+assert(parse_opts('a:b',array('-b','-a','alice')) === array('_' => array(), 'a' => 'alice', 'b' => true));
+assert(parse_opts('a:b',array('-b','-a','-c','foo')) === array('_' => array('-c','foo'), 'a' => null, 'b'=>true));
+assert(parse_opts('a:',array('-a','alice','bob')) === array('_' => array('bob'), 'a' =>'alice'));
+assert(parse_opts('a:',array('-a','alice','-a','bob')) === array('_' => array(), 'a' => array('alice','bob')));
112 ub
View
@@ -0,0 +1,112 @@
+#!/usr/bin/env php
+<?php /* -*-php-*- */
+include __DIR__ . '/ub.php';
+
+define('___DEFAULT_ITER', 1000);
+date_default_timezone_set('GMT');
+
+$opts = ub_parse_opts('vi:hcx:r', array_slice($_SERVER['argv'], 1));
+
+$opts['h'] and usage();
+
+if (($opts['c']) && (! is_callable("clock_gettime"))) {
+ usage("-c specified for clock_gettime(), but function is not available");
+}
+
+if (! is_null($opts['i'])) {
+ (strval(intval($opts['i'])) === $opts['i']) or usage();
+ ($opts['i'] > 0) or usage();
+ $iter = intval($opts['i']);
+} else {
+ $iter = ___DEFAULT_ITER;
+}
+
+/* If more than one benchmark was specified, then
+ * reinvoke this program once for each benchmark */
+if (count($opts['_']) > 1) {
+ $program = escapeshellcmd($_SERVER['_']);
+ $args = array();
+ if ($_SERVER['argv'][0] != $_SERVER['_']) {
+ $args[] = escapeshellarg($_SERVER['argv'][0]);
+ }
+ if ($opts['c']) { $args[] = '-c'; }
+ if ($opts['v']) { $args[] = '-v'; }
+ array_push($args, '-i', $iter);
+ if (! is_null($opts['x'])) {
+ foreach ((array) $opts['x'] as $opt) {
+ array_push($args, '-x', escapeshellarg($opt));
+ }
+ }
+ $base_command = $program . ' ' . implode(' ' , $args);
+ foreach ($opts['_'] as $benchmark) {
+ passthru($base_command . ' ' . escapeshellarg($benchmark));
+ }
+ exit(0);
+}
+
+/* There should be one and only one benchmark to run at this point */
+(count($opts['_']) == 1) or usage();
+$file = $opts['_'][0];
+$benchmark = pathinfo($file, PATHINFO_FILENAME);
+
+/* Make sure file is actually readable */
+is_readable($file) or usage("Can't read $file");
+
+/* Massage any extra arguments if provided */
+$extra = array();
+if (! is_null($opts['x'])) {
+ foreach ((array) $opts['x'] as $opt) {
+ if (strpos($opt, '=') !== false) {
+ list($k,$v) = explode('=', $opt, 2);
+ if (isset($extra[$k])) {
+ $extra[$k] = (array) $extra[$k];
+ $extra[$k][] = $v;
+ } else {
+ $extra[$k] = $v;
+ }
+ }
+ else {
+ $extra[$opt] = true;
+ }
+ }
+}
+
+/* Runner mode? Just set $__opts and run the script */
+if ($opts['r']) {
+ $___opts = $extra;
+ include $file;
+ exit(0);
+}
+
+/* Benchmark mode */
+$blocks = ub_parse_code(file_get_contents($file));
+$lambda = ub_generate_function($blocks, $iter, $opts['v'], $opts['c'], $extra);
+$times = $lambda();
+ub_print_results($benchmark, $times);
+
+
+/** How to run this program */
+function usage($error = null) {
+ if (! is_null($error)) {
+ print "Error: $error\n";
+ }
+ print "
+Usage: {$_SERVER['argv'][0]} [OPTIONS] BENCHMARK [BENCHMARK2 ...]
+
+Runs microbenchmark BENCHMARK BENCHMARK2, ... and display
+timing results. All output times are in milliseconds.
+
+Options:
+-i How many iterations each benchmark is run for.
+ Defaults to ". ___DEFAULT_ITER ." if not specified.
+-v Enables more verbose output
+-c Enables experimental clock extension for timing
+-h Prints this usage message
+-x Passes options to the benchmark script(s). Can be specified multiple
+ times.
+-r Run the benchmark script(s) just once without any parsing or
+ modification other than setting options appropriately. No output
+ timing is displayed.
+";
+ exit(-1);
+}
193 ub.php
View
@@ -0,0 +1,193 @@
+<?php
+
+/** Simple getopt()-like option parser */
+function ub_parse_opts($expected,$args) {
+ /* _ is the key for unexpected opts */
+ $parsed = array('_' => array());
+ preg_match_all('/([a-z]:?)/', $expected, $matches);
+ foreach ($matches[1] as $match) {
+ /* Default value for opts not expecting a value is "false",
+ * For opts expecting a value, it's "null" */
+ $parsed[$match[0]] = (strlen($match) == 1) ? false : null;
+ }
+ $current_opt = '_';
+ foreach ($args as $arg) {
+ /* If current arg begins with "-", it's probably an option */
+ if ($arg[0] == '-') {
+ $current_opt = substr($arg, 1);
+ /* Is it expected ?*/
+ if (array_key_exists($current_opt, $parsed)) {
+ /* Set no-value (boolean) opt to true */
+ if ($parsed[$current_opt] === false) {
+ $parsed[$current_opt] = true;
+ $current_opt = '_';
+ }
+ }
+ /* Unexpected, toss it onto the list */
+ else {
+ $parsed['_'][] = $arg;
+ }
+ }
+ /* Current arg doesn't begin with "-", it's probably a value */
+ else {
+ /* If we're not expecting a value (or at the start), toss it onto the unexpected list */
+ if ($current_opt == '_') {
+ $parsed['_'][] = $arg;
+ }
+ /* If we are expecting ... */
+ else if (array_key_exists($current_opt, $parsed)) {
+ /* Regular ol' expected value */
+ if (is_null($parsed[$current_opt])) {
+ $parsed[$current_opt] = $arg;
+ }
+ /* Value after a boolean option, put the value on the unexpected list */
+ else if (is_bool($parsed[$current_opt])) {
+ $parsed['_'] = $arg;
+ }
+ /* We've already seen multiple values for this opt, add the new value */
+ else if (is_array($parsed[$current_opt])) {
+ $parsed[$current_opt][] = $arg;
+ }
+ /* The second value for this opt, convert to an array */
+ else {
+ $parsed[$current_opt] = array($parsed[$current_opt], $arg);
+ }
+ /* Reset current_opt flag so we can look for a new option next */
+ $current_opt = '_';
+ }
+ /* Unexpected, toss it onto the list */
+ else {
+ $parsed['_'][] = $arg;
+ }
+ }
+ }
+ return $parsed;
+}
+
+/** Chop up the provided PHP code into separate blocks based on the
+ * state transitions denoted by comments in the code */
+function ub_parse_code($code) {
+
+ /* What state we start in */
+ $state = 'start';
+ /* Allowable transitions: key is the "from" state and value is
+ * an array of acceptable "to" states. A state transition is denoted
+ * in the code by a single line comment like "// new-state-name"
+ */
+ $transitions = array('start' => array('once', 'init', 'time','ignore'),
+ 'once' => array('init', 'time','ignore'),
+ 'init' => array('time', 'done','ignore'),
+ 'time' => array('done', 'ignore'),
+ 'done' => array('ignore')
+ );
+
+ /* Where the chopped up code will be stored */
+ $blocks = array();
+
+ /* This is a little inefficient, but prevents having to update some
+ * hardcoded $blocks initialization code when $transitions changes */
+ foreach ($transitions as $from => $to) {
+ if (! isset($blocks[$from])) {
+ $blocks[$from] = '';
+ }
+ foreach ($to as $to_state) {
+ if (! isset($blocks[$to_state])) {
+ $blocks[$to_state] = '';
+ }
+ }
+ }
+ $states_rx = join("|", array_keys($blocks));
+
+ foreach (token_get_all($code) as $tok) {
+ /* Standardize token representation as an array */
+ if (! is_array($tok)) { $tok = array(T_STRING, $tok); }
+
+ if (($tok[0] == T_COMMENT) && /* regular comment */
+ /* Referencing a known state */
+ preg_match('@^//\s*('. $states_rx . ')$@s', trim($tok[1]), $matches) &&
+ /* That the current state can transition to */
+ in_array($matches[1], $transitions[$state])) {
+ /* Then transition to that state */
+ $state = $matches[1];
+ }
+ /* Otherwise, just concatenate the current token onto this state's code block */
+ else {
+ $blocks[$state] .= $tok[1];
+ }
+ }
+
+ if (strlen($blocks['start']) == array_sum(array_map('strlen', $blocks))) {
+ $blocks['time'] = '?>' . $blocks['start'];
+ $blocks['start'] = '';
+ }
+
+ return $blocks;
+}
+
+/** Generate a function that will invoke the microbenchmark the appropriate number of
+ * times. The microbenchmark is run inside a function to protect its variable scope */
+function ub_generate_function($blocks, $iter, $verbose = false, $use_clock = false, $___opts = array()) {
+ if ($use_clock) {
+ $stamp = 'clock_gettime(0)';
+ $diff = '
+$___diff_sec = $___end[0] - $___start[0];
+if ($___diff_sec > 0) {
+ $___end[1] += ($___diff_sec * 1000000000);
+}
+$___diff_nsec = $___end[1] - $___start[1];
+$___times[] = ($___diff_nsec / 1000000);
+';
+ } else {
+ $stamp = 'gettimeofday()';
+ $diff = '
+$___diff_sec = $___end["sec"] - $___start["sec"];
+if ($___diff_sec > 0) {
+ $___end["usec"] += ($___diff_sec * 1000000);
+}
+$___diff_usec = $___end["usec"] - $___start["usec"];
+$___times[] = ($___diff_usec / 1000);
+';
+ }
+ $s = '
+$___times = array();
+$___opts = '.var_export($___opts, true) .';
+'.$blocks['once'].'
+for ($___i = 0; $___i < '.$iter.'; $___i++) {
+ '.$blocks['init'].'
+ $___start = '.$stamp.';
+ '.$blocks['time'].'
+ $___end = '.$stamp.';
+ '. $diff .'
+}
+'.$blocks['done'].'
+return $___times;
+';
+ if ($verbose) {
+ print "Running function() { $s}\n";
+ }
+ return create_function('', $s);
+}
+
+/** Given a label and an array of execution times, print out some useful statistics
+ * about the times */
+function ub_print_results($label, $times) {
+ sort($times);
+ $min = $times[0];
+ $max = $times[count($times) - 1];
+ if ((count($times) % 2) == 1) {
+ $median = $times[ floor(count($times) / 2) ];
+ } else {
+ $median = ($times[ count($times) / 2 ] + $times[ (count($times) / 2) -1 ]) / 2;
+ }
+ $mean = array_sum($times) / count($times);
+
+ $variance = 0.0;
+ foreach ($times as $time) {
+ $variance += pow(($time - $mean), 2);
+ }
+ $variance = $variance / (count($times) - 1);
+ $stdev = sqrt($variance);
+
+ printf("%32s: mean=%.06f median=%.06f min=%.06f max=%.06f stdev=%.06f\n",
+ $label, $mean, $median, $min, $max, $stdev);
+}
Please sign in to comment.
Something went wrong with that request. Please try again.