initial add of the hrtime ext

git-svn-id: http://svn.php.net/repository/pecl/hrtime/trunk@333366 c90b9560-bf6c-de11-be94-00142212c4b1
This commit is contained in:
Anatol Belski
2014-04-18 16:05:52 +00:00
parent 294990a889
commit dcbb526814
16 changed files with 1396 additions and 0 deletions

2
CREDITS Normal file
View File

@@ -0,0 +1,2 @@
hrtime
Anatol Belski

24
README.md Normal file
View File

@@ -0,0 +1,24 @@
# High resolution timer for PHP #
This PHP extension brings the possibility to use a high resolution timer from the user land PHP scripts. The best available APIs are used on differend platforms to achieve this goal.
## Synopsis ##
```php
$c = new HRTime\StopWatch;
$c->start();
/* do some interesting stuff*/
$c->stop();
$elapsed0 = $c->getLastElapsedTime(HRTime\Unit::NANOSECOND);
/* do something else */
$c->start();
/* do some interesting stuff again */
$c->stop();
$elapsed1 = $c->getLastElapsedTime(HRTime\Unit::NANOSECOND);
$elapsed_total = $c->getElapsedTime(HRTime\Unit::NANOSECOND);
```

12
config.m4 Normal file
View File

@@ -0,0 +1,12 @@
dnl $Id$
dnl config.m4 for extension hrtime
dnl Otherwise use enable:
PHP_ARG_ENABLE(hrtime, whether to enable hrtime support,
[ --enable-hrtime Enable hrtime support])
if test "$PHP_HRTIME" != "no"; then
PHP_ADD_LIBRARY(rt)
PHP_NEW_EXTENSION(hrtime, hrtime.c timer.c, $ext_shared)
fi

9
config.w32 Normal file
View File

@@ -0,0 +1,9 @@
// $Id$
// vim:ft=javascript
ARG_ENABLE("hrtime", "enable hrtime support", "no");
if (PHP_HRTIME != "no") {
EXTENSION("hrtime", "hrtime.c timer.c");
}

415
hrtime.c Normal file
View File

@@ -0,0 +1,415 @@
/*
* Copyright (c) 2014 Anatol Belski
* All rights reserved.
*
* Author: Anatol Belski <ab@php.net>
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
*
* $Id$
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "php_hrtime.h"
/*ZEND_DECLARE_MODULE_GLOBALS(hrtime);*/
/* True global resources - no need for thread safety here */
static int le_hrtime;
zend_class_entry *PerformanceCounter_ce;
zend_class_entry *StopWatch_ce;
zend_class_entry *Unit_ce;
/* {{{ hrtime_functions[]
*
* Every user visible function must have an entry in hrtime_functions[].
*/
const zend_function_entry hrtime_functions[] = {
PHP_FE_END
};
/* }}} */
const zend_function_entry PerformanceCounter_methods[] = {/*{{{*/
PHP_ME(PerformanceCounter, start, NULL, ZEND_ACC_PUBLIC)
PHP_ME(PerformanceCounter, stop, NULL, ZEND_ACC_PUBLIC)
PHP_ME(PerformanceCounter, getElapsedTicks, NULL, ZEND_ACC_PUBLIC)
PHP_ME(PerformanceCounter, getLastElapsedTicks, NULL, ZEND_ACC_PUBLIC)
PHP_ME(PerformanceCounter, getFrequency, NULL, ZEND_ACC_PUBLIC)
PHP_ME(PerformanceCounter, isRunning, NULL, ZEND_ACC_PUBLIC)
{NULL, NULL, NULL}
};/*}}}*/
const zend_function_entry StopWatch_methods[] = {/*{{{*/
PHP_ME(StopWatch, getElapsedTime, NULL, ZEND_ACC_PUBLIC)
PHP_ME(StopWatch, getLastElapsedTime, NULL, ZEND_ACC_PUBLIC)
{NULL, NULL, NULL}
};/*}}}*/
/* {{{ hrtime_module_entry
*/
zend_module_entry hrtime_module_entry = {
#if ZEND_MODULE_API_NO >= 20010901
STANDARD_MODULE_HEADER,
#endif
"hrtime",
hrtime_functions,
PHP_MINIT(hrtime),
PHP_MSHUTDOWN(hrtime),
PHP_RINIT(hrtime),
PHP_RSHUTDOWN(hrtime),
PHP_MINFO(hrtime),
#if ZEND_MODULE_API_NO >= 20010901
PHP_HRTIME_VERSION,
#endif
STANDARD_MODULE_PROPERTIES
};
/* }}} */
#ifdef COMPILE_DL_HRTIME
ZEND_GET_MODULE(hrtime)
#endif
void
php_performance_counter_obj_destroy(void *obj TSRMLS_DC)
{/*{{{*/
struct ze_performance_counter_obj *zvco = (struct ze_performance_counter_obj *)obj;
zend_object_std_dtor(&zvco->zo TSRMLS_CC);
/*hrtime_counter_destroy(&zvco->htc);*/
efree(zvco);
}/*}}}*/
zend_object_value
php_performance_counter_obj_init(zend_class_entry *ze TSRMLS_DC)
{/*{{{*/
zend_object_value ret;
struct ze_performance_counter_obj *zvco;
#if PHP_VERSION_ID < 50399
zval *tmp;
#endif
zvco = (struct ze_performance_counter_obj *)emalloc(sizeof(struct ze_performance_counter_obj));
memset(&zvco->zo, 0, sizeof(zend_object));
zend_object_std_init(&zvco->zo, ze TSRMLS_CC);
#if PHP_VERSION_ID < 50399
zend_hash_copy(zvco->zo.properties, &ze->default_properties, (copy_ctor_func_t) zval_add_ref,
(void *) &tmp, sizeof(zval *));
#else
object_properties_init(&zvco->zo, ze);
#endif
zvco->is_running = 0;
zvco->start = 0;
zvco->elapsed = 0;
zvco->elapsed_ref = 0;
ret.handle = zend_objects_store_put(zvco, NULL,
(zend_objects_free_object_storage_t) php_performance_counter_obj_destroy,
NULL TSRMLS_CC);
#if PHP_VERSION_ID < 50399
ret.handlers = zend_get_std_object_handlers();
ret.handlers->clone_obj = NULL;
#else
ret.handlers = &default_hrtime_handlers;
#endif
return ret;
}/*}}}*/
/* {{{ PHP_INI
*/
/* Remove comments and fill if you need to have entries in php.ini
PHP_INI_BEGIN()
STD_PHP_INI_ENTRY("hrtime.global_value", "42", PHP_INI_ALL, OnUpdateLong, global_value, zend_hrtime_globals, hrtime_globals)
STD_PHP_INI_ENTRY("hrtime.global_string", "foobar", PHP_INI_ALL, OnUpdateString, global_string, zend_hrtime_globals, hrtime_globals)
PHP_INI_END()
*/
/* }}} */
/* {{{ php_hrtime_counter_init_globals
*/
/* Uncomment this function if you have INI entries
static void php_hrtime_init_globals(zend_hrtime_globals *hrtime_globals)
{
hrtime_globals->global_value = 0;
hrtime_globals->global_string = NULL;
}
*/
/* }}} */
#define HRTIME_SECOND 0
#define HRTIME_MILLISECOND 1
#define HRTIME_MICROSECOND 2
#define HRTIME_NANOSECOND 3
/* {{{ PHP_MINIT_FUNCTION
*/
PHP_MINIT_FUNCTION(hrtime)
{
zend_class_entry ce;
if (timer_lib_initialize()) {
php_error_docref(NULL TSRMLS_CC, E_ERROR, "Failed to initialize internal timer");
return FAILURE;
}
#if PHP_VERSION_ID >= 50399
memcpy(&default_hrtime_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
default_hrtime_handlers.clone_obj = NULL;
#endif
/* Init internal classes */
INIT_CLASS_ENTRY(ce, "HRTime\\PerformanceCounter", PerformanceCounter_methods);
ce.create_object = php_performance_counter_obj_init;
PerformanceCounter_ce = zend_register_internal_class(&ce TSRMLS_CC);
INIT_CLASS_ENTRY(ce, "HRTime\\StopWatch", StopWatch_methods);
ce.create_object = php_performance_counter_obj_init;
StopWatch_ce = zend_register_internal_class_ex(&ce, PerformanceCounter_ce, "HRTime\\PerformanceCounter" TSRMLS_CC);
INIT_CLASS_ENTRY(ce, "HRTime\\Unit", NULL);
Unit_ce = zend_register_internal_class(&ce TSRMLS_CC);
zend_declare_class_constant_long(Unit_ce, "SECOND", sizeof("SECOND")-1, HRTIME_SECOND TSRMLS_CC);
zend_declare_class_constant_long(Unit_ce, "MILLISECOND", sizeof("MILLISECOND")-1, HRTIME_MILLISECOND TSRMLS_CC);
zend_declare_class_constant_long(Unit_ce, "MICROSECOND", sizeof("MICROSECOND")-1, HRTIME_MICROSECOND TSRMLS_CC);
zend_declare_class_constant_long(Unit_ce, "NANOSECOND", sizeof("NANOSECOND")-1, HRTIME_NANOSECOND TSRMLS_CC);
/*
REGISTER_INI_ENTRIES();
*/
return SUCCESS;
}
/* }}} */
/* {{{ PHP_MSHUTDOWN_FUNCTION
*/
PHP_MSHUTDOWN_FUNCTION(hrtime)
{
timer_lib_shutdown();
/*
UNREGISTER_INI_ENTRIES();
*/
return SUCCESS;
}
/* }}} */
/* Remove if there's nothing to do at request start */
/* {{{ PHP_RINIT_FUNCTION
*/
PHP_RINIT_FUNCTION(hrtime)
{
return SUCCESS;
}
/* }}} */
/* Remove if there's nothing to do at request end */
/* {{{ PHP_RSHUTDOWN_FUNCTION
*/
PHP_RSHUTDOWN_FUNCTION(hrtime)
{
return SUCCESS;
}
/* }}} */
/* {{{ PHP_MINFO_FUNCTION
*/
PHP_MINFO_FUNCTION(hrtime)
{
php_info_print_table_start();
php_info_print_table_header(2, "hrtime support", "enabled");
php_info_print_table_header(2, "Version", PHP_HRTIME_VERSION);
php_info_print_table_end();
/* Remove comments if you have entries in php.ini
DISPLAY_INI_ENTRIES();
*/
}
/* }}} */
PHP_METHOD(PerformanceCounter, start)
{/*{{{*/
struct ze_performance_counter_obj *zvco;
if (zend_parse_parameters_none() == FAILURE) {
return;
}
zvco = (struct ze_performance_counter_obj *) zend_object_store_get_object(getThis() TSRMLS_CC);
if (zvco->is_running) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The counter is already running");
return;
}
zvco->start = timer_current();
zvco->is_running = 1;
}/*}}}*/
PHP_METHOD(PerformanceCounter, stop)
{/*{{{*/
struct ze_performance_counter_obj *zvco;
if (zend_parse_parameters_none() == FAILURE) {
return;
}
zvco = (struct ze_performance_counter_obj *) zend_object_store_get_object(getThis() TSRMLS_CC);
if (!zvco->is_running) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "The counter is not running");
return;
}
zvco->elapsed_ref = timer_elapsed_ticks(zvco->start);
zvco->elapsed += zvco->elapsed_ref;
zvco->is_running = 0;
}/*}}}*/
PHP_METHOD(PerformanceCounter, getElapsedTicks)
{/*{{{*/
struct ze_performance_counter_obj *zvco;
if (zend_parse_parameters_none() == FAILURE) {
return;
}
zvco = (struct ze_performance_counter_obj *) zend_object_store_get_object(getThis() TSRMLS_CC);
if (zvco->is_running) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Counter is still running");
}
RETURN_LONG(zvco->elapsed);
}/*}}}*/
PHP_METHOD(PerformanceCounter, getLastElapsedTicks)
{/*{{{*/
struct ze_performance_counter_obj *zvco;
if (zend_parse_parameters_none() == FAILURE) {
return;
}
zvco = (struct ze_performance_counter_obj *) zend_object_store_get_object(getThis() TSRMLS_CC);
if (zvco->is_running) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Counter is still running");
RETURN_LONG(0);
}
RETURN_LONG(zvco->elapsed_ref);
}/*}}}*/
PHP_METHOD(PerformanceCounter, getFrequency)
{/*{{{*/
struct ze_performance_counter_obj *zvco;
if (zend_parse_parameters_none() == FAILURE) {
return;
}
zvco = (struct ze_performance_counter_obj *) zend_object_store_get_object(getThis() TSRMLS_CC);
RETURN_LONG(timer_ticks_per_second());
}/*}}}*/
PHP_METHOD(PerformanceCounter, isRunning)
{/*{{{*/
struct ze_performance_counter_obj *zvco;
if (zend_parse_parameters_none() == FAILURE) {
return;
}
zvco = (struct ze_performance_counter_obj *) zend_object_store_get_object(getThis() TSRMLS_CC);
RETURN_BOOL(zvco->is_running);
}/*}}}*/
#define RET_TIME_BY_UNIT(t, unit) do { \
double factor; \
switch (unit) { \
default: \
case HRTIME_SECOND: \
factor = 1.0; \
break; \
case HRTIME_MILLISECOND: \
factor = 1000.0; \
break; \
case HRTIME_MICROSECOND: \
factor = 1000000.0; \
break; \
case HRTIME_NANOSECOND: \
factor = 1000000000.0; \
break; \
} \
RETURN_DOUBLE(((double)t/(double)timer_ticks_per_second())*factor); \
/*RETURN_DOUBLE((double)timer_elapsed(t)/factor);*/ \
} while (0);
PHP_METHOD(StopWatch, getElapsedTime)
{/*{{{*/
struct ze_performance_counter_obj *zvco;
long unit;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &unit) == FAILURE) {
RETURN_NULL();
}
zvco = (struct ze_performance_counter_obj *) zend_object_store_get_object(getThis() TSRMLS_CC);
RET_TIME_BY_UNIT(zvco->elapsed, unit);
}/*}}}*/
PHP_METHOD(StopWatch, getLastElapsedTime)
{/*{{{*/
struct ze_performance_counter_obj *zvco;
long unit;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "|l", &unit) == FAILURE) {
RETURN_NULL();
}
zvco = (struct ze_performance_counter_obj *) zend_object_store_get_object(getThis() TSRMLS_CC);
RET_TIME_BY_UNIT(zvco->elapsed_ref, unit);
}/*}}}*/
/*
* 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
hrtime.php Normal file
View File

@@ -0,0 +1,21 @@
<?php
$br = (php_sapi_name() == "cli")? "":"<br>";
if(!extension_loaded('hrtime')) {
dl('hrtime.' . PHP_SHLIB_SUFFIX);
}
$module = 'hrtime';
$functions = get_extension_funcs($module);
echo "Functions available in the test extension:$br\n";
foreach($functions as $func) {
echo $func."$br\n";
}
echo "$br\n";
$function = 'confirm_' . $module . '_compiled';
if (extension_loaded($module)) {
$str = $function($module);
} else {
$str = "Module $module is not compiled into PHP";
}
echo "$str\n";
?>

100
php_hrtime.h Normal file
View File

@@ -0,0 +1,100 @@
/*
* Copyright (c) 2014 Anatol Belski
* All rights reserved.
*
* Author: Anatol Belski <ab@php.net>
*
* 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.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
*
* $Id$
*/
#ifndef PHP_HRTIME_H
#define PHP_HRTIME_H
#define PHP_HRTIME_VERSION "0.4.3"
extern zend_module_entry hrtime_module_entry;
#define phpext_hrtime_ptr &hrtime_module_entry
#ifdef PHP_WIN32
# define PHP_HRTIME_API __declspec(dllexport)
#elif defined(__GNUC__) && __GNUC__ >= 4
# define PHP_HRTIME_API __attribute__ ((visibility("default")))
#else
# define PHP_HRTIME_API
#endif
#ifdef ZTS
#include "TSRM.h"
#endif
#include "timer.h"
PHP_MINIT_FUNCTION(hrtime);
PHP_MSHUTDOWN_FUNCTION(hrtime);
PHP_RINIT_FUNCTION(hrtime);
PHP_RSHUTDOWN_FUNCTION(hrtime);
PHP_MINFO_FUNCTION(hrtime);
PHP_METHOD(PerformanceCounter, start);
PHP_METHOD(PerformanceCounter, stop);
PHP_METHOD(PerformanceCounter, getElapsedTicks);
PHP_METHOD(PerformanceCounter, getLastElapsedTicks);
PHP_METHOD(PerformanceCounter, getFrequency);
PHP_METHOD(PerformanceCounter, isRunning);
PHP_METHOD(StopWatch, getElapsedTime);
PHP_METHOD(StopWatch, getLastElapsedTime);
/*ZEND_BEGIN_MODULE_GLOBALS(hrtime)
ZEND_END_MODULE_GLOBALS(hrtime)*/
#ifdef ZTS
#define HRTIME_G(v) TSRMG(hrtime_globals_id, zend_hrtime_globals *, v)
#else
#define HRTIME_G(v) (hrtime_globals.v)
#endif
struct ze_performance_counter_obj {
zend_object zo;
tick_t start;
tick_t elapsed;
tick_t elapsed_ref;
zend_bool is_running;
};
#if PHP_VERSION_ID >= 50399
zend_object_handlers default_hrtime_handlers;
#endif
#endif /* PHP_HRTIME_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
*/

100
tests/001.phpt Normal file
View File

@@ -0,0 +1,100 @@
--TEST--
HRTime\StopWatch lasts not more that microtime()
--SKIPIF--
<?php if (!extension_loaded("hrtime")) print "skip"; ?>
--FILE--
<?php
include 'helper.inc';
for ($j = 0; $j < 4; $j++) {
$c = new HRTime\StopWatch;
$t0 = microtime(true);
for ($i = 0; $i < 1024; $i++);
$t1 = microtime(true);
$elapsed0 = $t1 - $t0;
$c->start();
for ($i = 0; $i < 1024; $i++);
$c->stop();
$e0 = $elapsed0;
$e1 = $c->getLastElapsedTime(HRTime\Unit::SECOND);
echo "Microtime s: ", $e0, "s", "\n";
echo "Stopwatch s: ", $e1, "s", "\n";
var_dump(is_uncertainty_acceptable($e0, $e1, HRTime\Unit::SECOND));
$e0 = s_to($elapsed0, HRTime\Unit::MILLISECOND);
$e1 = $c->getLastElapsedTime(HRTime\Unit::MILLISECOND);
echo "Microtime ms: ", $e0, "ms", "\n";
echo "Stopwatch ms: ", $e1, "ms", "\n";
var_dump(is_uncertainty_acceptable($e0, $e1, HRTime\Unit::MILLISECOND));
$e0 = s_to($elapsed0, HRTime\Unit::MICROSECOND);
$e1 = $c->getLastElapsedTime(HRTime\Unit::MICROSECOND);
echo "Microtime us: ", $e0, "us", "\n";
echo "Stopwatch us: ", $e1, "us", "\n";
var_dump(is_uncertainty_acceptable($e0, $e1, HRTime\Unit::MICROSECOND));
$e0 = s_to($elapsed0, HRTime\Unit::NANOSECOND);
$e1 = $c->getLastElapsedTime(HRTime\Unit::NANOSECOND);
echo "Microtime ns: ", $e0, "ns", "\n";
echo "Stopwatch ns: ", $e1, "ns", "\n";
var_dump(is_uncertainty_acceptable($e0, $e1, HRTime\Unit::NANOSECOND));
echo "\n";
}
--EXPECTF--
Microtime s: %fs
Stopwatch s: %fs
bool(true)
Microtime ms: %fms
Stopwatch ms: %fms
bool(true)
Microtime us: %fus
Stopwatch us: %fus
bool(true)
Microtime ns: %fns
Stopwatch ns: %fns
bool(true)
Microtime s: %fs
Stopwatch s: %fs
bool(true)
Microtime ms: %fms
Stopwatch ms: %fms
bool(true)
Microtime us: %fus
Stopwatch us: %fus
bool(true)
Microtime ns: %fns
Stopwatch ns: %fns
bool(true)
Microtime s: %fs
Stopwatch s: %fs
bool(true)
Microtime ms: %fms
Stopwatch ms: %fms
bool(true)
Microtime us: %fus
Stopwatch us: %fus
bool(true)
Microtime ns: %fns
Stopwatch ns: %fns
bool(true)
Microtime s: %fs
Stopwatch s: %fs
bool(true)
Microtime ms: %fms
Stopwatch ms: %fms
bool(true)
Microtime us: %fus
Stopwatch us: %fus
bool(true)
Microtime ns: %fns
Stopwatch ns: %fns
bool(true)

64
tests/002.phpt Normal file
View File

@@ -0,0 +1,64 @@
--TEST--
HRTime\StopWatch partial elapsed ticks sum == total elapsed ticks
--SKIPIF--
<?php if (!extension_loaded("hrtime")) print "skip"; ?>
--FILE--
<?php
for ($j = 0; $j < 4; $j++) {
$c = new HRTime\StopWatch;
$c->start();
for ($i = 0; $i < 1024; $i++);
$c->stop();
$elapsed0 = $c->getLastElapsedTicks();
usleep(1000);
$c->start();
for ($i = 0; $i < 1024; $i++);
$c->stop();
$elapsed1 = $c->getLastElapsedTicks();
usleep(1000);
$c->start();
for ($i = 0; $i < 1024; $i++);
$c->stop();
$elapsed2 = $c->getLastElapsedTicks();
$elapsed_total = $c->getElapsedTicks();
echo "elapsed0=$elapsed0\n";
echo "elapsed1=$elapsed1\n";
echo "calculated elapsed total=" . ($elapsed0 + $elapsed1 + $elapsed2) . "\n";
echo "elapsed total=$elapsed_total\n";
var_dump(($elapsed0 + $elapsed1 + $elapsed2) == $elapsed_total);
echo "\n";
}
--EXPECTF--
elapsed0=%d
elapsed1=%d
calculated elapsed total=%d
elapsed total=%d
bool(true)
elapsed0=%d
elapsed1=%d
calculated elapsed total=%d
elapsed total=%d
bool(true)
elapsed0=%d
elapsed1=%d
calculated elapsed total=%d
elapsed total=%d
bool(true)
elapsed0=%d
elapsed1=%d
calculated elapsed total=%d
elapsed total=%d
bool(true)

101
tests/003.phpt Normal file
View File

@@ -0,0 +1,101 @@
--TEST--
HRTime\StopWatch is fine with sleep
--SKIPIF--
<?php if (!extension_loaded("hrtime")) print "skip"; ?>
--FILE--
<?php
include 'helper.inc';
for ($j = 0; $j < 4; $j++) {
$c = new HRTime\StopWatch;
$t0 = microtime(true);
sleep(1);
$t1 = microtime(true);
$elapsed0 = $t1 - $t0;
$c->start();
sleep(1);
$c->stop();
$e0 = $elapsed0;
$e1 = $c->getLastElapsedTime(HRTime\Unit::SECOND);
echo "Microtime s: ", $e0, "s", "\n";
echo "Stopwatch s: ", $e1, "s", "\n";
var_dump(is_uncertainty_acceptable($e0, $e1, HRTime\Unit::SECOND));
$e0 = s_to($elapsed0, HRTime\Unit::MILLISECOND);
$e1 = $c->getLastElapsedTime(HRTime\Unit::MILLISECOND);
echo "Microtime ms: ", $e0, "ms", "\n";
echo "Stopwatch ms: ", $e1, "ms", "\n";
var_dump(is_uncertainty_acceptable($e0, $e1, HRTime\Unit::MILLISECOND));
$e0 = s_to($elapsed0, HRTime\Unit::MICROSECOND);
$e1 = $c->getLastElapsedTime(HRTime\Unit::MICROSECOND);
echo "Microtime us: ", $e0, "us", "\n";
echo "Stopwatch us: ", $e1, "us", "\n";
var_dump(is_uncertainty_acceptable($e0, $e1, HRTime\Unit::MICROSECOND));
$e0 = s_to($elapsed0, HRTime\Unit::NANOSECOND);
$e1 = $c->getLastElapsedTime(HRTime\Unit::NANOSECOND);
echo "Microtime ns: ", $e0, "ns", "\n";
echo "Stopwatch ns: ", $e1, "ns", "\n";
var_dump(is_uncertainty_acceptable($e0, $e1, HRTime\Unit::NANOSECOND));
echo "\n";
}
--EXPECTF--
Microtime s: %fs
Stopwatch s: %fs
bool(true)
Microtime ms: %fms
Stopwatch ms: %fms
bool(true)
Microtime us: %fus
Stopwatch us: %fus
bool(true)
Microtime ns: %fns
Stopwatch ns: %fns
bool(true)
Microtime s: %fs
Stopwatch s: %fs
bool(true)
Microtime ms: %fms
Stopwatch ms: %fms
bool(true)
Microtime us: %fus
Stopwatch us: %fus
bool(true)
Microtime ns: %fns
Stopwatch ns: %fns
bool(true)
Microtime s: %fs
Stopwatch s: %fs
bool(true)
Microtime ms: %fms
Stopwatch ms: %fms
bool(true)
Microtime us: %fus
Stopwatch us: %fus
bool(true)
Microtime ns: %fns
Stopwatch ns: %fns
bool(true)
Microtime s: %fs
Stopwatch s: %fs
bool(true)
Microtime ms: %fms
Stopwatch ms: %fms
bool(true)
Microtime us: %fus
Stopwatch us: %fus
bool(true)
Microtime ns: %fns
Stopwatch ns: %fns
bool(true)

100
tests/004.phpt Normal file
View File

@@ -0,0 +1,100 @@
--TEST--
HRTime\StopWatch is fine with usleep
--SKIPIF--
<?php if (!extension_loaded("hrtime")) print "skip"; ?>
--FILE--
<?php
include 'helper.inc';
for ($j = 0; $j < 4; $j++) {
$c = new HRTime\StopWatch;
$t0 = microtime(true);
usleep(1);
$t1 = microtime(true);
$elapsed0 = $t1 - $t0;
$c->start();
usleep(1);
$c->stop();
$e0 = $elapsed0;
$e1 = $c->getLastElapsedTime(HRTime\Unit::SECOND);
echo "Microtime s: ", $e0, "s", "\n";
echo "Stopwatch s: ", $e1, "s", "\n";
var_dump(is_uncertainty_acceptable($e0, $e1, HRTime\Unit::SECOND));
$e0 = s_to($elapsed0, HRTime\Unit::MILLISECOND);
$e1 = $c->getLastElapsedTime(HRTime\Unit::MILLISECOND);
echo "Microtime ms: ", $e0, "ms", "\n";
echo "Stopwatch ms: ", $e1, "ms", "\n";
var_dump(is_uncertainty_acceptable($e0, $e1, HRTime\Unit::MILLISECOND));
$e0 = s_to($elapsed0, HRTime\Unit::MICROSECOND);
$e1 = $c->getLastElapsedTime(HRTime\Unit::MICROSECOND);
echo "Microtime us: ", $e0, "us", "\n";
echo "Stopwatch us: ", $e1, "us", "\n";
var_dump(is_uncertainty_acceptable($e0, $e1, HRTime\Unit::MICROSECOND));
$e0 = s_to($elapsed0, HRTime\Unit::NANOSECOND);
$e1 = $c->getLastElapsedTime(HRTime\Unit::NANOSECOND);
echo "Microtime ns: ", $e0, "ns", "\n";
echo "Stopwatch ns: ", $e1, "ns", "\n";
var_dump(is_uncertainty_acceptable($e0, $e1, HRTime\Unit::NANOSECOND));
echo "\n";
}
--EXPECTF--
Microtime s: %fs
Stopwatch s: %fs
bool(true)
Microtime ms: %fms
Stopwatch ms: %fms
bool(true)
Microtime us: %fus
Stopwatch us: %fus
bool(true)
Microtime ns: %fns
Stopwatch ns: %fns
bool(true)
Microtime s: %fs
Stopwatch s: %fs
bool(true)
Microtime ms: %fms
Stopwatch ms: %fms
bool(true)
Microtime us: %fus
Stopwatch us: %fus
bool(true)
Microtime ns: %fns
Stopwatch ns: %fns
bool(true)
Microtime s: %fs
Stopwatch s: %fs
bool(true)
Microtime ms: %fms
Stopwatch ms: %fms
bool(true)
Microtime us: %fus
Stopwatch us: %fus
bool(true)
Microtime ns: %fns
Stopwatch ns: %fns
bool(true)
Microtime s: %fs
Stopwatch s: %fs
bool(true)
Microtime ms: %fms
Stopwatch ms: %fms
bool(true)
Microtime us: %fus
Stopwatch us: %fus
bool(true)
Microtime ns: %fns
Stopwatch ns: %fns
bool(true)

98
tests/005.phpt Normal file
View File

@@ -0,0 +1,98 @@
--TEST--
HRTime\StopWatch elapsed tick count vs. elapsed time
--SKIPIF--
<?php if (!extension_loaded("hrtime")) print "skip"; ?>
--FILE--
<?php
include 'helper.inc';
$c = new HRTime\StopWatch;
$freq = $c->getFrequency();
for ($j = 0; $j < 4; $j++) {
$c->start();
for ($i = 0; $i < 1024; $i++);
$c->stop();
$elapsed0 = $c->getLastElapsedTicks();
$e0 = $elapsed0/$freq;
$e1 = $c->getLastElapsedTime(HRTime\Unit::SECOND);
echo "calc from ticks s: ", $e0, "s", "\n";
echo "time method s: ", $e1, "s", "\n";
var_dump(is_uncertainty_acceptable($e0, $e1, HRTime\Unit::SECOND));
$e0 = ($elapsed0/$freq)*1000.0;
$e1 = $c->getLastElapsedTime(HRTime\Unit::MILLISECOND);
echo "calc from ticks ms: ", $e0, "ms", "\n";
echo "time method ms: ", $e1, "ms", "\n";
var_dump(is_uncertainty_acceptable($e0, $e1, HRTime\Unit::MILLISECOND));
$e0 = ($elapsed0/$freq)*1000000.0;
$e1 = $c->getLastElapsedTime(HRTime\Unit::MICROSECOND);
echo "calc from ticks us: ", $e0, "us", "\n";
echo "time method us: ", $e1, "us", "\n";
var_dump(is_uncertainty_acceptable($e0, $e1, HRTime\Unit::MICROSECOND));
$e0 = ($elapsed0/$freq)*1000000000.0;
$e1 = $c->getLastElapsedTime(HRTime\Unit::NANOSECOND);
echo "calc from ticks ns: ", $e0, "ns", "\n";
echo "time method ns: ", $e1, "ns", "\n";
var_dump(is_uncertainty_acceptable($e0, $e1, HRTime\Unit::NANOSECOND));
echo "\n";
}
--EXPECTF--
calc from ticks s: %fs
time method s: %fs
bool(true)
calc from ticks ms: %fms
time method ms: %fms
bool(true)
calc from ticks us: %fus
time method us: %fus
bool(true)
calc from ticks ns: %fns
time method ns: %fns
bool(true)
calc from ticks s: %fs
time method s: %fs
bool(true)
calc from ticks ms: %fms
time method ms: %fms
bool(true)
calc from ticks us: %fus
time method us: %fus
bool(true)
calc from ticks ns: %fns
time method ns: %fns
bool(true)
calc from ticks s: %fs
time method s: %fs
bool(true)
calc from ticks ms: %fms
time method ms: %fms
bool(true)
calc from ticks us: %fus
time method us: %fus
bool(true)
calc from ticks ns: %fns
time method ns: %fns
bool(true)
calc from ticks s: %fs
time method s: %fs
bool(true)
calc from ticks ms: %fms
time method ms: %fms
bool(true)
calc from ticks us: %fus
time method us: %fus
bool(true)
calc from ticks ns: %fns
time method ns: %fns
bool(true)

37
tests/006.phpt Normal file
View File

@@ -0,0 +1,37 @@
--TEST--
HRTime\StopWatch start right after stop with nanosecond resolution > 0
--SKIPIF--
<?php if (!extension_loaded("hrtime")) print "skip"; ?>
--INI--
presicion=48
--FILE--
<?php
for ($j = 0; $j < 4; $j++) {
$c = new HRTime\StopWatch;
$c->start();
$c->stop();
$e1 = $c->getLastElapsedTime(HRTime\Unit::NANOSECOND);
var_dump($e1, $e1 >= .0);
echo "\n";
/* Need this because the accuracy won't be as good as 1ns even on the best machines.
Well, at least now it's sience fiction.*/
usleep(1);
}
--EXPECTF--
float(%f)
bool(true)
float(%f)
bool(true)
float(%f)
bool(true)
float(%f)
bool(true)

62
tests/helper.inc Normal file
View File

@@ -0,0 +1,62 @@
<?php
function s_to($secs, $unit)
{
$ret = .0;
switch ($unit) {
case HRTime\Unit::SECOND:
$ret = $secs;
break;
case HRTime\Unit::MILLISECOND:
$ret = $secs*1000.0;
break;
case HRTime\Unit::MICROSECOND:
$ret = $secs*1000000.0;
break;
case HRTime\Unit::NANOSECOND:
$ret = $secs*1000000000.0;
break;
}
return $ret;
}
function uncertainty_abs($a, $b)
{
return $a - $b;
}
function uncertainty_rel($a, $b)
{
return uncertainty_abs($a, $b)/$b;
}
function is_uncertainty_acceptable($a, $b, $unit)
{
$ret = false;
$rel = uncertainty_rel($a, $b);
$treshold = .05;
switch ($unit) {
case HRTime\Unit::SECOND:
$ret = abs($rel) < $treshold;
break;
case HRTime\Unit::MILLISECOND:
$ret = abs($rel) < $treshold;
break;
case HRTime\Unit::MICROSECOND:
$ret = abs($rel) < $treshold;
break;
case HRTime\Unit::NANOSECOND:
$ret = abs($rel) < $treshold;
break;
}
if (!$ret) {
printf("a=$a b=$b relative uncertainty %d%%\n", (int)($rel*100.0));
}
return $ret;
}

174
timer.c Normal file
View File

@@ -0,0 +1,174 @@
/* timer.h - Cross-platform timer library - Public Domain - 2011 Mattias Jansson / Rampant Pixels
*
* This library provides a cross-platform interface to measure
* elapsed time with (at least) millisecond accuracy.
*
* This library is put in the public domain; you can redistribute
* it and/or modify it without any restrictions.
*
*/
#include "timer.h"
#define TIMER_PLATFORM_WINDOWS 0
#define TIMER_PLATFORM_APPLE 0
#define TIMER_PLATFORM_POSIX 0
#if defined( _WIN32 ) || defined( _WIN64 )
# undef TIMER_PLATFORM_WINDOWS
# define TIMER_PLATFORM_WINDOWS 1
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
#elif defined( __APPLE__ )
# undef TIMER_PLATFORM_APPLE
# define TIMER_PLATFORM_APPLE 1
# include <mach/mach_time.h>
# include <string.h>
static mach_timebase_info_data_t _timerlib_info;
static void absolutetime_to_nanoseconds (uint64_t mach_time, uint64_t* clock ) { *clock = mach_time * _timerlib_info.numer / _timerlib_info.denom; }
#else
# undef TIMER_PLATFORM_POSIX
# define TIMER_PLATFORM_POSIX 1
# include <unistd.h>
# include <time.h>
# include <string.h>
#endif
static tick_t _timerlib_freq = 0;
static double _timerlib_oofreq = 0;
int timer_lib_initialize( void )
{
#if TIMER_PLATFORM_WINDOWS
tick_t unused;
if( !QueryPerformanceFrequency( (LARGE_INTEGER*)&_timerlib_freq ) ||
!QueryPerformanceCounter( (LARGE_INTEGER*)&unused ) )
return -1;
#elif TIMER_PLATFORM_APPLE
if( mach_timebase_info( &_timerlib_info ) )
return -1;
_timerlib_freq = 1000000000ULL;
#elif TIMER_PLATFORM_POSIX
struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 };
if( clock_gettime( CLOCK_MONOTONIC, &ts ) )
return -1;
_timerlib_freq = 1000000000ULL;
#endif
_timerlib_oofreq = 1.0 / (double)_timerlib_freq;
return 0;
}
void timer_lib_shutdown( void )
{
}
tick_t timer_current( void )
{
#if TIMER_PLATFORM_WINDOWS
tick_t curclock;
QueryPerformanceCounter( (LARGE_INTEGER*)&curclock );
return curclock;
#elif TIMER_PLATFORM_APPLE
tick_t curclock = 0;
absolutetime_to_nanoseconds( mach_absolute_time(), &curclock );
return curclock;
#elif TIMER_PLATFORM_POSIX
struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 };
clock_gettime( CLOCK_MONOTONIC, &ts );
return ( (uint64_t)ts.tv_sec * 1000000000ULL ) + ts.tv_nsec;
#endif
}
tick_t timer_ticks_per_second( void )
{
return _timerlib_freq;
}
deltatime_t timer_elapsed( const tick_t t )
{
return (deltatime_t)( (double)timer_elapsed_ticks( t ) * _timerlib_oofreq );
}
tick_t timer_elapsed_ticks( const tick_t t )
{
tick_t dt = 0;
#if TIMER_PLATFORM_WINDOWS
tick_t curclock = t;
QueryPerformanceCounter( (LARGE_INTEGER*)&curclock );
dt = curclock - t;
#elif TIMER_PLATFORM_APPLE
tick_t curclock = t;
absolutetime_to_nanoseconds( mach_absolute_time(), &curclock );
dt = curclock - t;
#elif TIMER_PLATFORM_POSIX
tick_t curclock;
struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 };
clock_gettime( CLOCK_MONOTONIC, &ts );
curclock = ( (tick_t)ts.tv_sec * 1000000000ULL ) + ts.tv_nsec;
dt = curclock - t;
#endif
return dt;
}
deltatime_t timer_ticks_to_seconds( const tick_t dt )
{
return (deltatime_t)( (double)dt * _timerlib_oofreq );
}
#if TIMER_PLATFORM_WINDOWS
struct __timeb64 {
__time64_t time;
unsigned short millitm;
short timezone;
short dstflag;
};
_CRTIMP errno_t __cdecl _ftime64_s(_Out_ struct __timeb64 * _Time);
#endif
tick_t timer_system( void )
{
#if TIMER_PLATFORM_WINDOWS
struct __timeb64 tb;
_ftime64_s( &tb );
return ( (tick_t)tb.time * 1000ULL ) + (tick_t)tb.millitm;
#elif TIMER_PLATFORM_APPLE
tick_t curclock = 0;
absolutetime_to_nanoseconds( mach_absolute_time(), &curclock );
return ( curclock / 1000000ULL );
#elif TIMER_PLATFORM_POSIX
struct timespec ts = { .tv_sec = 0, .tv_nsec = 0 };
clock_gettime( CLOCK_REALTIME, &ts );
return ( (uint64_t)ts.tv_sec * 1000ULL ) + ( ts.tv_nsec / 1000000ULL );
#endif
}

77
timer.h Normal file
View File

@@ -0,0 +1,77 @@
/* timer.h - Cross-platform timer library - Public Domain - 2011 Mattias Jansson / Rampant Pixels
*
* This library provides a cross-platform interface to measure
* elapsed time with (at least) millisecond accuracy.
*
* This library is put in the public domain; you can redistribute
* it and/or modify it without any restrictions.
*
*/
#pragma once
/*! \file timer.h
Time measurements */
#if TIMER_COMPILE
#define TIMER_API
#else
#if __cplusplus
#define TIMER_API extern "C"
#else
#define TIMER_API extern
#endif
#endif
#if defined( _WIN32 ) || defined( _WIN64 )
//! Tick type
typedef unsigned __int64 tick_t;
#else
#include <stdint.h>
//! Tick type
typedef uint64_t tick_t;
#endif
//! Deltatime type (float or double)
//typedef float deltatime_t;
typedef double deltatime_t;
/*! Initialize timer library */
TIMER_API int timer_lib_initialize( void );
/*! Shutdown timer library */
TIMER_API void timer_lib_shutdown( void );
/*! Get current timestamp, in ticks of system-specific frequency (queryable with timer_ticks_per_second), measured from some system-specific base timestamp
and not in sync with other timestamps
\return Current timestamp */
TIMER_API tick_t timer_current( void );
/*! Get elapsed time since given timestamp
\param t Timestamp
\return Number of seconds elapsed */
TIMER_API deltatime_t timer_elapsed( const tick_t t );
/*! Get elapsed ticks since given timestamp
\param t Timestamp
\return Number of ticks elapsed */
TIMER_API tick_t timer_elapsed_ticks( const tick_t t );
/*! Get timer frequency, as number of ticks per second
\return Ticks per second */
TIMER_API tick_t timer_ticks_per_second( void );
/*! Get ticks as seconds (effectively calculating ticks/timer_ticks_per_second())
\param dt Deltatime in ticks
\return Deltatime in seconds */
TIMER_API deltatime_t timer_ticks_to_seconds( const tick_t dt );
/*! Get system time, in milliseconds since the epoch (UNIX time)
\return Current timestamp, in milliseconds */
TIMER_API tick_t timer_system( void );