tests/bench_xtimer: initial import

This commit is contained in:
Kaspar Schleiser 2019-12-10 10:24:01 +01:00
parent 26a1348a9a
commit d89debd183
4 changed files with 464 additions and 0 deletions

View File

@ -0,0 +1,76 @@
include ../Makefile.tests_common
USEMODULE += xtimer
# this test uses 1000 timers by default. for boards that boards don't have
# enough memory, reduce that to 100 or 20, unless NUMOF_TIMERS has been overridden.
LOW_MEMORY_BOARDS += \
airfy-beacon \
arduino-mega2560 \
b-l072z-lrwan1 \
blackpill \
blackpill-128kib \
bluepill \
bluepill-128kib \
calliope-mini \
cc1352-launchpad \
cc2650-launchpad \
cc2650stk \
chronos \
hifive1 \
hifive1b \
i-nucleo-lrwan1 \
lsn50 \
maple-mini \
microbit \
msb-430 \
msb-430h \
nrf51dongle \
nrf6310 \
nucleo-f030r8 \
nucleo-f042k6 \
nucleo-f070rb \
nucleo-f072rb \
nucleo-f103rb \
nucleo-f303k8 \
nucleo-f334r8 \
nucleo-l031k6 \
nucleo-l053r8 \
nucleo-l073rz \
opencm904 \
saml10-xpro \
saml11-xpro \
spark-core \
stm32f0discovery \
stm32l0538-disco \
telosb \
waspmote-pro \
wsn430-v1_3b \
wsn430-v1_4 \
yunjia-nrf51822 \
z1 \
#
SUPER_LOW_MEMORY_BOARDS += \
arduino-duemilanove \
arduino-leonardo \
arduino-nano \
arduino-uno \
atmega328p \
nucleo-f031k6 \
stm32f030f4-demo \
#
ifneq (, $(filter $(BOARD), $(LOW_MEMORY_BOARDS)))
NUMOF_TIMERS ?= 100
endif
ifneq (, $(filter $(BOARD), $(SUPER_LOW_MEMORY_BOARDS)))
NUMOF_TIMERS ?= 20
endif
NUMOF_TIMERS ?= 1000
CFLAGS += -DNUMOF_TIMERS=$(NUMOF_TIMERS)
include $(RIOTBASE)/Makefile.include

View File

@ -0,0 +1,77 @@
# Introduction
This test executes some benchmarks for xtimer's set() / remove() / now()
operations.
# Details
This set of benchmarks measures xtimer's list operation efficiency.
Depending on the available memory, the individual benchmarks that are using
multiple timers are run with either 1000 (the default), 100 or 20 timers.
Each benchmark is repeated REPEAT times (default 1000).
As only the operations are benchmarked, it is asserted that no timer ever
actually triggers.
### set() one
This repeatedly sets one timer in an otherwise emtpy list.
All but the first iteration will cause xtimer to implicitly remove the timer
first.
All iterations will cause the underlying periph timer to be updated.
### remove() one
This repeatedly removes one timer from the list. In all but the first iteration,
this list will be empty.
### set() + remove() one
This repeatedly first sets, then removes one timer. The list is empty
before and after each iteration.
All iterations will cause the underlying periph timer to be updated.
### set() many increasing targets
This adds NUMOF timers with increasing target times. Each iteration will add a
timer at the end of xtimer's timer list.
Only the first iteration will cause the underlying periph timer to be updated.
After this test, the list is populated with NUMOF timers.
### re-set() first
This repatedly re-sets the first timer in the list (to the same target time).
All iterations will cause the underlying periph timer to be updated.
### re-set() middle
This repatedly re-sets the timer in the middle of the list (to the same target
time).
### re-set() last
This repatedly re-sets the last timer in the list (to the same target time).
### remove() + set() first, middle, last
Same as the previous three, but does a remove() before set().
### remove() many decreasing
This removes all timers from the list, starting with the last.
### xtimer_now()
This simply calls xtimer_now() in a loop.
# How to interpret results
The aim is to measure the time spent in xtimer's list operations.
Lower values are better.
The first/middle/last tests give an idea of the best case / average case /
worst case when running the operation with NUMOF timers.
Note that every set() on an already set timer will trigger an implicit remove(),
thus the timer list has to be iterated twice.
The tests that do a remove() before set() show whether xtimer correctly
identifies an unset timer.

289
tests/bench_xtimer/main.c Normal file
View File

@ -0,0 +1,289 @@
/*
* Copyright (C) 2019 Kaspar Schleiser <kaspar@schleiser.de>
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @ingroup tests
* @{
*
* @file
* @brief xtimer set / remove / now benchmark application
*
* @author Kaspar Schleiser <kaspar@schleiser.de>
*
* @}
*/
#include <stdio.h>
#include <assert.h>
#include "msg.h"
#include "thread.h"
#include "xtimer.h"
#ifndef NUMOF_TIMERS
#define NUMOF_TIMERS (1000U)
#endif
#ifndef REPEAT
#define REPEAT (1000U)
#endif
#ifndef BASE
#define BASE (100000000LU)
#endif
#ifndef SPREAD
#define SPREAD (10000LU)
#endif
static xtimer_t _timers[NUMOF_TIMERS];
/* This variable is set by any timer that actually triggers. As the test is
* only testing set/remove/now operations, timers are not supposed to trigger.
* Thus, after every test there's an 'assert(!_triggers)'
*/
static unsigned _triggers;
/*
* The test assumes that first, middle and last will always end up in at the
* same index within the timer queue. In order to compensate for the time that
* previous operations take themselves, the interval is corrected. The
* variables "start" and "_base" are used for that.
*/
uint32_t _base;
static void _callback(void *arg) {
unsigned *triggers = arg;
*triggers += 1;
}
/* returns the interval for timer 'n' that has to be set in order to insert it
* into position n */
static uint32_t _timer_val(unsigned n)
{
return _base + (SPREAD * n);
}
/* set timer 'n' to its intended position 'n' */
static void _timer_set(unsigned n)
{
xtimer_set(&_timers[n], _timer_val(n));
}
/* remove timer 'n' */
static void _timer_remove(unsigned n)
{
xtimer_remove(&_timers[n]);
}
static void _print_result(const char *desc, unsigned n, uint32_t total)
{
printf("%30s %8"PRIu32" / %u = %"PRIu32"\n", desc, total, n, total/n);
}
int main(void)
{
puts("xtimer benchmark application.\n");
unsigned n;
uint32_t before, diff, start;
/* initializing timer structs */
for (unsigned int n = 0; n < NUMOF_TIMERS; n++) {
_timers[n].callback = _callback;
_timers[n].arg = &_triggers;
}
start = xtimer_now_usec();
/*
* test setting one set timer REPEAT times
*
*/
_base = BASE;
before = xtimer_now_usec();
for (n = 0; n < REPEAT; n++) {
_timer_set(0);
}
diff = xtimer_now_usec() - before;
_print_result("set() one", REPEAT, diff);
assert(!_triggers);
/*
* test removing one unset timer REPEAT times
*
*/
before = xtimer_now_usec();
for (n = 0; n < REPEAT; n++) {
_timer_remove(0);
}
diff = xtimer_now_usec() - before;
_print_result("remove() one", REPEAT, diff);
assert(!_triggers);
/*
* test setting / removing one timer REPEAT times
*
*/
before = xtimer_now_usec();
_base = BASE - (before - start);
for (n = 0; n < REPEAT; n++) {
_timer_set(0);
_timer_remove(0);
}
diff = xtimer_now_usec() - before;
_print_result("set() + remove() one", REPEAT, diff);
assert(!_triggers);
/*
* test setting NUMOF_TIMERS timers with increasing targets
*
*/
before = xtimer_now_usec();
_base = BASE - (before - start);
for (unsigned int n = 0; n < NUMOF_TIMERS; n++) {
_timer_set(n);
}
diff = xtimer_now_usec() - before;
_print_result("set() many increasing target", NUMOF_TIMERS, diff);
assert(!_triggers);
/*
* test re-setting first timer REPEAT times
*
*/
before = xtimer_now_usec();
_base = BASE - (before - start);
for (n = 0; n < REPEAT; n++) {
_timer_set(0);
}
diff = xtimer_now_usec() - before;
_print_result("re-set() first", REPEAT, diff);
assert(!_triggers);
/*
* test setting middle timer REPEAT times
*
*/
before = xtimer_now_usec();
_base = BASE - (before - start);
for (n = 0; n < REPEAT; n++) {
_timer_set(NUMOF_TIMERS/2);
}
diff = xtimer_now_usec() - before;
_print_result("re-set() middle", REPEAT, diff);
assert(!_triggers);
/*
* test setting last timer REPEAT times
*
*/
before = xtimer_now_usec();
_base = BASE - (before - start);
for (n = 0; n < REPEAT; n++) {
_timer_set(NUMOF_TIMERS - 1);
}
diff = xtimer_now_usec() - before;
_print_result("re-set() last", REPEAT, diff);
assert(!_triggers);
/*
* test removing / setting first timer REPEAT times
*
*/
before = xtimer_now_usec();
_base = BASE - (before - start);
for (n = 0; n < REPEAT; n++) {
_timer_remove(0);
_timer_set(0);
}
diff = xtimer_now_usec() - before;
_print_result("remove() + set() first", REPEAT, diff);
assert(!_triggers);
/*
* test removing / setting middle timer REPEAT times
*
*/
before = xtimer_now_usec();
_base = BASE - (before - start);
for (n = 0; n < REPEAT; n++) {
_timer_remove(NUMOF_TIMERS/2);
_timer_set(NUMOF_TIMERS/2);
}
diff = xtimer_now_usec() - before;
_print_result("remove() + set() middle", REPEAT, diff);
assert(!_triggers);
/*
* test removing / setting last timer REPEAT times
*
*/
before = xtimer_now_usec();
_base = BASE - (before - start);
for (n = 0; n < REPEAT; n++) {
_timer_remove(NUMOF_TIMERS - 1);
_timer_set(NUMOF_TIMERS - 1);
}
diff = xtimer_now_usec() - before;
_print_result("remove() + set() last", REPEAT, diff);
assert(!_triggers);
/*
* test removing NUMOF_TIMERS timers (latest first)
*
*/
before = xtimer_now_usec();
for (n = 0; n < NUMOF_TIMERS; n++) {
_timer_remove(NUMOF_TIMERS - n - 1);
}
diff = xtimer_now_usec() - before;
_print_result("remove() many decreasing", NUMOF_TIMERS, diff);
assert(!_triggers);
/*
* test xtimer_now()
*
*/
before = xtimer_now_usec();
n = REPEAT;
while (n--) {
xtimer_now_usec();
}
diff = xtimer_now_usec() - before;
_print_result("xtimer_now()", REPEAT, diff);
assert(!_triggers);
puts("done.");
return 0;
}

View File

@ -0,0 +1,22 @@
#!/usr/bin/env python3
# Copyright (C) 2019 Freie Universität Berlin
#
# This file is subject to the terms and conditions of the GNU Lesser
# General Public License v2.1. See the file LICENSE in the top level
# directory for more details.
import sys
from testrunner import run
def testfunc(child):
child.expect_exact("xtimer benchmark application.\r\n")
for i in range(12):
child.expect(r"\s+[\w() _\+]+\s+\d+ / \d+ = \d+\r\n")
child.expect_exact("done.\r\n")
if __name__ == "__main__":
sys.exit(run(testfunc))