Skip to content

Instantly share code, notes, and snippets.

@pida42
Last active May 31, 2016 04:38
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pida42/95350fcfb1d5579c58c07c5ec9303c46 to your computer and use it in GitHub Desktop.
Save pida42/95350fcfb1d5579c58c07c5ec9303c46 to your computer and use it in GitHub Desktop.
Simple benchmark class that allows you write markers in code and get info about exec time, memory usage and more...
<?php
/*
* The MIT License
*
* Copyright (c) 2016 Frantisek Preissler <github@ntisek.cz>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
namespace Pida42;
/**
* @package Pida42
* @copyright Copyright (c) 2016
* @author Frantisek Preissler <github@ntisek.cz>
* @license [MIT](http://en.wikipedia.org/wiki/MIT_License)
* @encoding UTF-8
*/
class Benchmark {
// <editor-fold defaultstate="collapsed" desc="Properties">
/** @var string Result metrics output type (default: array) */
protected static $outputType = 'array';
/** @var array Available output types that's are supported/implemented */
protected static $outputTypeList = ['array', 'string', 'json'];
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Cache">
/**
* Get a new (or already eixisting) instance using singleton.
*
* @static
* @return \ArrayObject
*/
public static function getCache() {
// magic of singletons
static $instance = null;
// This is first time instance creation
if (false === isset($instance)) {
$instance = new \ArrayObject([], \ArrayObject::STD_PROP_LIST);
}
// Return already created instance
return $instance;
}
/**
* Check if exists item by index
*
* @param string $index
*
* @static
* @return bool
*/
public static function cacheExists($index) {
return self::getCache()->offsetExists($index);
}
/**
* Set value into cache
*
* @param string $index
* @param mixed $value
*
* @static
* @return mixed
*/
public static function cacheSet($index, $value) {
return self::getCache()->offsetSet($index, $value);
}
/**
* Get value by index
*
* @param string $index
*
* @static
* @return bool
*/
public static function cacheGet($index) {
return self::getCache()->offsetGet($index);
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Helpers">
/**
* Get microtime
*
* @static
* @return float
*/
private static function _time() {
return microtime(true);
}
/**
* Return memory usage
*
* @static
* @return int|null
*/
private static function _memory() {
// function is not supported
if (false === function_exists('memory_get_usage')) {
return null;
}
return memory_get_usage();
}
/**
* Return memory peak
*
* @static
* @return int|null
*/
private static function _memoryPeak() {
// function is not supported
if (false === function_exists('memory_get_peak_usage')) {
return null;
}
return memory_get_peak_usage();
}
/**
* Return average server load
*
* @return array|null
*/
private static function _serverLoad() {
// function is not supported
if (false === function_exists('sys_getloadavg')) {
return null;
}
return sys_getloadavg();
}
/**
* Get difference of arrays with keys intact
*
* @param array $arr1
* @param array $arr2
*
* @static
* @return array
*/
private static function getRUsage($arr1, $arr2) {
// result data
$array = [];
// Add user mode time.
$arr1['ru_utime.tv'] = ($arr1['ru_utime.tv_usec'] / 1000000) + $arr1['ru_utime.tv_sec'];
$arr2['ru_utime.tv'] = ($arr2['ru_utime.tv_usec'] / 1000000) + $arr2['ru_utime.tv_sec'];
// Add system mode time.
$arr1['ru_stime.tv'] = ($arr1['ru_stime.tv_usec'] / 1000000) + $arr1['ru_stime.tv_sec'];
$arr2['ru_stime.tv'] = ($arr2['ru_stime.tv_usec'] / 1000000) + $arr2['ru_stime.tv_sec'];
// Unset time splits.
unset(
$arr1['ru_utime.tv_usec'],
$arr1['ru_utime.tv_sec'],
$arr2['ru_utime.tv_usec'],
$arr2['ru_utime.tv_sec'],
$arr1['ru_stime.tv_usec'],
$arr1['ru_stime.tv_sec'],
$arr2['ru_stime.tv_usec'],
$arr2['ru_stime.tv_sec']
);
// Iterate over values.
foreach ($arr1 as $key => $value) {
$array[$key] = ($arr2[$key] - $arr1[$key]);
}
return $array;
}
/**
* Try to get stats using try{}catch() statement
*
* @param string $point
*
* @return array
* @internal param string $slabel
* @static
*/
protected static function check($point) {
try {
return [
'time' => self::getTime($point),
'usage' => self::getUsage($point),
'memory' => self::getMemory($point),
'peak_memory' => self::getMemoryPeak($point),
];
}
catch (\Exception $e) {
return $e;
}
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Common">
/**
* Mark point in code
*
* @param string $point
* @param null|string $outputType
*
* @static
* @return void
*/
public static function mark($point, $outputType = null) {
self::setTime($point);
self::setUsage($point, ($outputType ?: self::getOutputType()));
self::setMemory($point);
self::setMemoryPeak($point);
}
/**
* Try to get report using try{}catch() statement
*
* @param string $point
*
* @static
* @return array
* @throws \Exception
*/
public static function report($point) {
try {
// stats separately
$results = self::check($point);
// result array
$result = [
'ExecutionTime' => @$results['time'],
'MemoryLimit' => @rtrim(ini_get('memory_limit'), 'M'),
'MemoryUsage' => @$results['memory'],
'PeakMemory' => @$results['peak_memory'],
'AvgServerLoad' => @self::_serverLoad()['0']
];
return $result;
}
catch (Exception $e) {
return $e;
}
}
/**
* Get result data
*
* @param null|string $outputType Target result format type
*
* @static
* @return null|array|string
*/
public static function usage($outputType = null) {
// If is not set, use default
if (null === $outputType) {
$outputType = self::getOutputType();
}
if ('array' === $outputType) {
return getrusage();
}
if ('string' === $outputType) {
return str_replace('&', ', ', http_build_query(getrusage()));
}
if ('json' === $outputType) {
return json_encode(getrusage());
}
return null;
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Getters/Setters - Output Type">
/**
* Set the output type for results
*
* @param string $outputType Output type
*
* @static
* @return void
* @throws \UnexpectedValueException
* @see self::$outputTypeList
*/
public static function setOutputType($outputType) {
// defined format is not supported
if (false === in_array(strtolower($outputType) . self::$outputTypeList)) {
throw new \UnexpectedValueException(
"Defined output type '{$outputType}' is not supported." .
"Supported output types are: " . implode(',', self::$outputTypeList) . "."
);
}
self::$outputType = strtolower($outputType);
}
/**
* Get actual output type
*
* @static
* @return string
*/
public static function getOutputType() {
return self::$outputType;
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Getters/Setters - Profile data">
/**
* Get time difference
*
* @param string $point
*
* @static
* @return null|float
*/
protected static function getTime($point) {
// if any point is missing, so end with null returning
if (false === self::cacheExists("{$point}_time_start") || false === self::cacheExists("{$point}_time_end")) {
return null;
}
return (float) number_format((self::cacheGet("{$point}_time_end") - self::cacheGet("{$point}_time_start")), 5, '.', '');
}
/**
* Set time by label
*
* @param string $point
*
* @static
* @return void
*/
protected static function setTime($point) {
if (true === self::cacheExists("{$point}_time_start") && false === self::cacheExists("{$point}_time_end")) {
self::cacheSet("{$point}_time_end", self::_time());
}
else {
self::cacheSet("{$point}_time_start", self::_time());
}
}
/**
* Get usage difference
*
* @param string $point
*
* @static
* @return null|array
*/
protected static function getUsage($point) {
// if any point is missing, so end with null returning
if (false === self::cacheExists("{$point}_usage_start") || false === self::cacheExists("{$point}_usage_end")) {
return null;
}
return self::getRUsage(self::cacheGet("{$point}_usage_start"), self::cacheGet("{$point}_usage_end"));
}
/**
* Set usage by label
*
* @param string $point
* @param string $outputType
*
* @static
* @return void
*/
protected static function setUsage($point, $outputType = null) {
self::cacheSet($point, self::usage($outputType));
}
/**
* Get memory usage difference (in MB)
*
* @param string $point
*
* @static
* @return null|float
*/
protected static function getMemory($point) {
// if any point is missing, so end with null returning
if (false === self::cacheExists("{$point}_memory_start") || false === self::cacheExists("{$point}_memory_end")) {
return null;
}
return (float) number_format(((self::cacheGet("{$point}_memory_end") - self::cacheGet("{$point}_memory_start")) / 1024 / 1024), 5, '.', '');
}
/**
* Set memory by label.
*
* @param string $point
*
* @static
* @return void
*/
protected static function setMemory($point) {
if (true === self::cacheExists("{$point}_memory_start") && false === self::cacheExists("{$point}_memory_end")) {
self::cacheSet("{$point}_memory_end", self::_memory());
}
else {
self::cacheSet("{$point}_memory_start", self::_memory());
}
}
/**
* Get memory peak usage
*
* @param string $point
*
* @static
* @return null|float
*/
protected static function getMemoryPeak($point) {
// if any point is missing, so end with null returning
if (false === self::cacheExists("{$point}_peak_memory")) {
return null;
}
return (float) number_format((self::cacheGet("{$point}_peak_memory") / 1024 / 1024), 5, '.', '');
}
/**
* Set peak memory by label.
*
* @param string $point
*
* @static
* @return void
*/
protected static function setMemoryPeak($point) {
self::cacheSet("{$point}_peak_memory", self::_memoryPeak());
}
// </editor-fold>
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment