Add a split timer to the stopwatch.

This is just a re-work of Steve's sample patch.

You set the number of splits and whether they overwrite using
qb_util_stopwatch_split_ctl().

Signed-off-by: Angus Salkeld <asalkeld@redhat.com>
This commit is contained in:
Angus Salkeld 2012-01-17 12:16:14 +11:00
parent 86428907c7
commit 63bdfd1b71
4 changed files with 403 additions and 3 deletions

View File

@ -39,6 +39,72 @@ extern "C" {
* @author Angus Salkeld <asalkeld@redhat.com>
*
* These are some convience functions used throughout libqb.
*
* @par Locking
* - qb_thread_lock_create()
* - qb_thread_lock()
* - qb_thread_trylock()
* - qb_thread_unlock()
* - qb_thread_lock_destroy()
*
* @par Time functions
* - qb_timespec_add_ms()
* - qb_util_nano_current_get()
* - qb_util_nano_monotonic_hz()
* - qb_util_nano_from_epoch_get()
* - qb_util_timespec_from_epoch_get()
*
* @par Basic Stopwatch
* @code
* uint64_t elapsed1;
* uint64_t elapsed2;
* qb_util_stopwatch_t *sw = qb_util_stopwatch_create();
*
* qb_util_stopwatch_start(sw);
*
* usleep(sometime);
* qb_util_stopwatch_stop(sw);
* elapsed1 = qb_util_stopwatch_us_elapsed_get(sw);
*
* usleep(somemoretime);
* qb_util_stopwatch_stop(sw);
* elapsed2 = qb_util_stopwatch_us_elapsed_get(sw);
*
* qb_util_stopwatch_free(sw);
* @endcode
*
* @par Stopwatch with splits
* Setup a stopwatch with space for 3 splits.
*
* @code
* uint64_t split;
* qb_util_stopwatch_t *sw = qb_util_stopwatch_create();
*
* qb_util_stopwatch_split_ctl(sw, 3, 0);
* qb_util_stopwatch_start(sw);
*
* usleep(sometime);
* qb_util_stopwatch_split(sw);
*
* usleep(somemoretime);
* qb_util_stopwatch_split(sw);
*
* usleep(somemoretime);
* qb_util_stopwatch_split(sw);
*
* idx = qb_util_stopwatch_split_last(sw);
* do {
* split = qb_util_stopwatch_time_split_get(sw, idx, idx);
* qb_log(LOG_INFO, "split %d is %"PRIu64"", last, split);
* idx--;
* } while (split > 0);
*
* split = qb_util_stopwatch_time_split_get(sw, 2, 1);
* qb_log(LOG_INFO, "time between second and third split is %"PRIu64"", split);
*
* qb_util_stopwatch_free(sw);
* @endcode
*
*/
/**
@ -131,6 +197,9 @@ char *qb_strerror_r(int errnum, char *buf, size_t buflen);
typedef struct qb_util_stopwatch qb_util_stopwatch_t;
#define QB_UTIL_SW_OVERWRITE 0x01
/**
* Create a Stopwatch (to time operations)
*/
@ -145,7 +214,7 @@ void qb_util_stopwatch_free(qb_util_stopwatch_t *sw);
* Start the stopwatch
*
* This also acts as a reset. Essentially it sets the
* starting time.
* starting time and clears the splits.
*/
void qb_util_stopwatch_start(qb_util_stopwatch_t *sw);
@ -172,6 +241,54 @@ uint64_t qb_util_stopwatch_us_elapsed_get(qb_util_stopwatch_t *sw);
*/
float qb_util_stopwatch_sec_elapsed_get(qb_util_stopwatch_t *sw);
/**
*
* @param sw the stopwatch
* @param max_splits maximum number of time splits
* @param options (0 or QB_UTIL_SW_OVERWRITE )
* @retval 0 on success
* @retval -errno on failure
*/
int32_t qb_util_stopwatch_split_ctl(qb_util_stopwatch_t *sw,
uint32_t max_splits, uint32_t options);
/**
* Create a new time split (or lap time)
*
* @param sw the stopwatch
* @retval the relative split time in micro seconds
* @retval 0 if no more splits available
*/
uint64_t qb_util_stopwatch_split(qb_util_stopwatch_t *sw);
/**
* Get the last split index to be used by
* qb_util_stopwatch_time_split_get()
*
* @note this is zero based
*
* @param sw the stopwatch
* @return the last entry index
*/
uint32_t
qb_util_stopwatch_split_last(qb_util_stopwatch_t *sw);
/**
* Read the time split (in us) from "receint" to "older".
*
* If older == receint then the cumulated split will be
* returned (from the stopwatch start).
*
* @param sw the stopwatch
* @param receint split
* @param older split
* @retval the split time in micro seconds
* @retval 0 if not a valid split
*/
uint64_t
qb_util_stopwatch_time_split_get(qb_util_stopwatch_t *sw,
uint32_t receint, uint32_t older);
/* *INDENT-OFF* */
#ifdef __cplusplus
}

View File

@ -223,6 +223,10 @@ qb_util_nano_from_epoch_get(void)
struct qb_util_stopwatch {
uint64_t started;
uint64_t stopped;
uint32_t split_options;
uint32_t split_size;
uint32_t split_entries;
uint64_t *split_entry_list;
};
qb_util_stopwatch_t *
@ -236,6 +240,7 @@ qb_util_stopwatch_create(void)
void
qb_util_stopwatch_free(qb_util_stopwatch_t * sw)
{
free(sw->split_entry_list);
free(sw);
}
@ -244,6 +249,7 @@ qb_util_stopwatch_start(qb_util_stopwatch_t * sw)
{
sw->started = qb_util_nano_current_get();
sw->stopped = 0;
sw->split_entries = 0;
}
void
@ -272,3 +278,86 @@ qb_util_stopwatch_sec_elapsed_get(qb_util_stopwatch_t * sw)
return ((float)e6 / (float)QB_TIME_US_IN_SEC);
}
int32_t
qb_util_stopwatch_split_ctl(qb_util_stopwatch_t *sw,
uint32_t max_splits, uint32_t options)
{
sw->split_size = max_splits;
sw->split_options = options;
sw->split_entry_list = (uint64_t *)calloc(1, sizeof (uint64_t) * max_splits);
if (sw->split_entry_list == NULL) {
return -errno;
}
return 0;
}
uint64_t
qb_util_stopwatch_split(qb_util_stopwatch_t *sw)
{
uint32_t new_entry_pos;
uint64_t time_start;
uint64_t time_end;
if (sw->split_size == 0) {
return 0;
}
if (!(sw->split_options & QB_UTIL_SW_OVERWRITE) &&
sw->split_entries == sw->split_size) {
return 0;
}
if (sw->started == 0) {
qb_util_stopwatch_start(sw);
}
new_entry_pos = sw->split_entries % (sw->split_size);
sw->split_entry_list[new_entry_pos] = qb_util_nano_current_get();
sw->split_entries++;
time_start = sw->split_entry_list[new_entry_pos];
if (sw->split_entries == 1) {
/* first entry */
time_end = sw->started;
} else if (new_entry_pos == 0) {
/* wrap around */
time_end = sw->split_entry_list[sw->split_size - 1];
} else {
time_end = sw->split_entry_list[(new_entry_pos - 1) % sw->split_size];
}
return (time_start - time_end) / QB_TIME_NS_IN_USEC;
}
uint32_t
qb_util_stopwatch_split_last(qb_util_stopwatch_t *sw)
{
if (sw->split_entries) {
return sw->split_entries - 1;
}
return sw->split_entries;
}
uint64_t
qb_util_stopwatch_time_split_get(qb_util_stopwatch_t *sw,
uint32_t receint, uint32_t older)
{
uint64_t time_start;
uint64_t time_end;
if (sw->started == 0 ||
receint >= sw->split_entries ||
older >= sw->split_entries ||
receint < older) {
return 0;
}
if (sw->split_options & QB_UTIL_SW_OVERWRITE &&
(receint < (sw->split_entries - sw->split_size) ||
older < (sw->split_entries - sw->split_size))) {
return 0;
}
time_start = sw->split_entry_list[receint % (sw->split_size)];
if (older == receint) {
time_end = sw->started;
} else {
time_end = sw->split_entry_list[older % (sw->split_size)];
}
return (time_start - time_end) / QB_TIME_NS_IN_USEC;
}

View File

@ -89,11 +89,11 @@ bench_log_LDADD = $(LIB_RT) $(top_builddir)/lib/libqb.la
if HAVE_CHECK
EXTRA_DIST += resources.test
TESTS = array.test map.test rb.test log.test loop.test ipc.test resources.test
TESTS = array.test map.test util.test rb.test log.test loop.test ipc.test resources.test
resources.log: rb.log log.log ipc.log
check_PROGRAMS = array.test map.test rb.test log.test loop.test ipc.test
check_PROGRAMS = array.test map.test rb.test log.test loop.test ipc.test util.test
check_SCRIPTS = resources.test
array_test_SOURCES = check_array.c $(top_builddir)/include/qb/qbarray.h
@ -120,6 +120,10 @@ log_test_SOURCES = check_log.c $(top_builddir)/include/qb/qblog.h
log_test_CFLAGS = @CHECK_CFLAGS@ -I$(top_srcdir)/include
log_test_LDADD = $(top_builddir)/lib/libqb.la $(LIB_RT) @CHECK_LIBS@
util_test_SOURCES = check_util.c $(top_builddir)/include/qb/qbutil.h
util_test_CFLAGS = @CHECK_CFLAGS@ -I$(top_srcdir)/include
util_test_LDADD = $(top_builddir)/lib/libqb.la $(LIB_RT) @CHECK_LIBS@
endif
clean-generic:

190
tests/check_util.c Normal file
View File

@ -0,0 +1,190 @@
/*
* Copyright (c) 2010 Red Hat, Inc.
*
* All rights reserved.
*
* Author: Steven Dake <sdake@redhat.com>
*
* This file is part of libqb.
*
* libqb is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 2.1 of the License, or
* (at your option) any later version.
*
* libqb is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with libqb. If not, see <http://www.gnu.org/licenses/>.
*/
#include "os_base.h"
#include <check.h>
#include <qb/qbdefs.h>
#include <qb/qbutil.h>
#include <qb/qblog.h>
#define assert_int_between(_c, _lower, _upper) \
_ck_assert_int(_c, >=, _lower); \
_ck_assert_int(_c, <=, _upper);
START_TEST(test_check_overwrite)
{
uint64_t res;
uint32_t last;
qb_util_stopwatch_t *sw = qb_util_stopwatch_create();
qb_util_stopwatch_split_ctl(sw, 5, QB_UTIL_SW_OVERWRITE);
res = qb_util_stopwatch_split(sw);
assert_int_between(res, 0, 100);
usleep(10000);
res = qb_util_stopwatch_split(sw);
assert_int_between(res, 9000, 11000);
usleep(20000);
res = qb_util_stopwatch_split(sw);
assert_int_between(res, 19000, 21000);
usleep(30000);
res = qb_util_stopwatch_split(sw);
assert_int_between(res, 29000, 31000);
usleep(40000);
res = qb_util_stopwatch_split(sw);
assert_int_between(res, 39000, 41000);
/*
* window should be 100000 (40000 + 30000 + 20000 + 10000) usec
*/
last = qb_util_stopwatch_split_last(sw);
res = qb_util_stopwatch_time_split_get(sw, last, last - 4);
assert_int_between(res, 95000, 105000);
usleep(50000);
res = qb_util_stopwatch_split(sw);
assert_int_between(res, 49000, 52000);
/*
* window should be 140000 (50000 + 40000 + 30000 + 20000) usec
*/
last = qb_util_stopwatch_split_last(sw);
res = qb_util_stopwatch_time_split_get(sw, last, last - 4);
assert_int_between(res, 135000, 145000);
usleep(25000);
qb_util_stopwatch_split(sw);
/* ask for a split that has been overwritten.
*/
res = qb_util_stopwatch_time_split_get(sw, last, 1);
ck_assert_int_eq(res, 0);
/* iterating
*/
last = qb_util_stopwatch_split_last(sw);
do {
res = qb_util_stopwatch_time_split_get(sw, last, last);
qb_log(LOG_INFO, "overwrite split %d is %"PRIu64"", last, res);
last--;
} while (res > 0);
qb_util_stopwatch_free(sw);
}
END_TEST
START_TEST(test_check_normal)
{
uint64_t res;
uint32_t last;
qb_util_stopwatch_t *sw = qb_util_stopwatch_create();
qb_util_stopwatch_split_ctl(sw, 3, 0);
qb_util_stopwatch_start(sw);
usleep(33000);
/* 1 */
res = qb_util_stopwatch_split(sw);
assert_int_between(res, 30000, 36000);
last = qb_util_stopwatch_split_last(sw);
ck_assert_int_eq(last, 0);
usleep(10000);
/* 2 */
res = qb_util_stopwatch_split(sw);
assert_int_between(res, 9000, 11000);
usleep(20000);
/* 3 */
res = qb_util_stopwatch_split(sw);
assert_int_between(res, 19000, 21000);
/* no more space */
res = qb_util_stopwatch_split(sw);
ck_assert_int_eq(res, 0);
/*
* split should be 30000 (10000 + 20000) usec
*/
last = qb_util_stopwatch_split_last(sw);
ck_assert_int_eq(last, 2);
res = qb_util_stopwatch_time_split_get(sw, last, 0);
assert_int_between(res, 25000, 35000);
/* ask for a split that has beyond the max.
*/
res = qb_util_stopwatch_time_split_get(sw, 3, 2);
ck_assert_int_eq(res, 0);
/* iterating
*/
last = qb_util_stopwatch_split_last(sw);
do {
res = qb_util_stopwatch_time_split_get(sw, last, last);
qb_log(LOG_INFO, "normal split %d is %"PRIu64"", last, res);
last--;
} while (res > 0);
qb_util_stopwatch_free(sw);
}
END_TEST
static Suite *util_suite(void)
{
TCase *tc;
Suite *s = suite_create("qb_util");
tc = tcase_create("overwrite");
tcase_add_test(tc, test_check_overwrite);
suite_add_tcase(s, tc);
tc = tcase_create("normal");
tcase_add_test(tc, test_check_normal);
suite_add_tcase(s, tc);
return s;
}
int32_t main(void)
{
int32_t number_failed;
Suite *s = util_suite();
SRunner *sr = srunner_create(s);
qb_log_init("check", LOG_USER, LOG_EMERG);
qb_log_ctl(QB_LOG_SYSLOG, QB_LOG_CONF_ENABLED, QB_FALSE);
qb_log_filter_ctl(QB_LOG_STDERR, QB_LOG_FILTER_ADD,
QB_LOG_FILTER_FILE, "*", LOG_INFO);
qb_log_ctl(QB_LOG_STDERR, QB_LOG_CONF_ENABLED, QB_TRUE);
srunner_run_all(sr, CK_VERBOSE);
number_failed = srunner_ntests_failed(sr);
srunner_free(sr);
return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}