---
title: "Kernel benchmark"
canonical_url: https://developer.repebble.com/pebbleos/docs/samples/benchmarks/kernel/
last_updated: 2026-11-13
sdk_version: 4.44
---
# Kernel benchmark

Source: https://developer.repebble.com/pebbleos/docs/samples/benchmarks/kernel/

This sample times the kernel’s basic operations with the CPU cycle counter, a thousand times each: uncontended semaphore, mutex and queue operations, an allocation, switching to another thread and back, creating a thread, and getting work to run on a queue.

On the native build a cycle is a nanosecond of the host’s clock and only says the sample runs; the ones worth comparing between commits come from a watch. `empty measurement` is the cost of reading the counter, included in every other line. The emulator’s cycle counter does not count, so the sample is not run there.

Run:

`pbl samples samples/benchmarks/kernel`

Boards:

every board but qemu\_emery, qemu\_flint, qemu\_gabbro

## Sources

src/main.c

```
#include \<errno.h\>#include \<inttypes.h\>#include \<pbl/kernel/delay.h\>#include \<pbl/kernel/malloc.h\>#include \<pbl/kernel/msgq.h\>#include \<pbl/kernel/mutex.h\>#include \<pbl/kernel/sem.h\>#include \<pbl/kernel/thread.h\>#include \<pbl/kernel/time.h\>#include \<pbl/kernel/work.h\>#include \<pbl/logging/logging.h\>#define ITERATIONS 1000U/\* Runs body ITERATIONS times and reports the average and the best time of one. \*/#define BENCH(name, body) \ do { \ uint32\_t \_total = 0; \ uint32\_t \_best = UINT32\_MAX; \ for (unsigned int \_i = 0; \_i \< ITERATIONS; \_i++) { \ uint32\_t \_start = pbl\_cycle\_get\_32(); \ body; \ uint32\_t \_cycles = pbl\_cycle\_get\_32() - \_start; \ \_total += \_cycles; \ \_best = \_cycles \< \_best ? \_cycles : \_best; \ } \ prv\_report(name, \_total / ITERATIONS, \_best); \ } while (0)static PBL\_SEM\_DEFINE(s\_sem, 0, 1);static PBL\_MUTEX\_DEFINE(s\_mutex);static PBL\_MSGQ\_DEFINE(s\_msgq, sizeof(uint32\_t), 4);/\* For the switches to a thread of higher priority and back. \*/static PBL\_SEM\_DEFINE(s\_ping, 0, 1);static volatile uint32\_t s\_work\_started;PBL\_THREAD\_STACK\_DEFINE(s\_peer\_stack, 1024);PBL\_THREAD\_STACK\_DEFINE(s\_waiter\_stack, 1024);PBL\_THREAD\_STACK\_DEFINE(s\_short\_stack, 1024);static struct pbl\_thread s\_peer;static struct pbl\_thread s\_waiter;static struct pbl\_thread s\_short;static void prv\_report(const char \*name, uint32\_t average, uint32\_t best) {uint32\_t ns = (uint32\_t)((uint64\_t)average \* 1000000000U / pbl\_cycles\_per\_sec());/\* No padding: hashed logs take plain conversions only. \*/PBL\_LOG\_INFO("bench: %s: avg %" PRIu32 " cycles (%" PRIu32 " ns), best %" PRIu32 " cycles", name,average, ns, best);}static void prv\_peer(void \*arg) {for (;;) {pbl\_thread\_yield();}}static void prv\_waiter(void \*arg) {for (;;) {pbl\_sem\_take(&s\_ping, PBL\_FOREVER);}}static void prv\_short(void \*arg) {}static void prv\_work(struct pbl\_work \*work) {s\_work\_started = pbl\_cycle\_get\_32();}static PBL\_WORK\_DEFINE(s\_work, prv\_work);static void prv\_start(struct pbl\_thread \*t, const char \*name, pbl\_thread\_entry\_t entry,pbl\_prio\_t prio, void \*stack, size\_t stack\_size) {const struct pbl\_thread\_attr attr = {.name = name,.entry = entry,.prio = prio,.privileged = true,.stack = stack,.stack\_size = stack\_size,};pbl\_thread\_create(t, &attr);}int main(void) {uint32\_t msg = 0;uint32\_t start;start = pbl\_cycle\_get\_32();pbl\_sleep\_ms(10);if (pbl\_cycle\_get\_32() == start) {PBL\_LOG\_ERR("bench: the cycle counter does not count on this board");return -ENOTSUP;}PBL\_LOG\_INFO("bench: %u iterations each, %" PRIu32 " cycles per second", ITERATIONS,pbl\_cycles\_per\_sec());BENCH("empty measurement", {});BENCH("sem give + take", {pbl\_sem\_give(&s\_sem);pbl\_sem\_take(&s\_sem, PBL\_NO\_WAIT);});BENCH("mutex lock + unlock", {pbl\_mutex\_lock(&s\_mutex, PBL\_NO\_WAIT);pbl\_mutex\_unlock(&s\_mutex);});BENCH("msgq put + get", {pbl\_msgq\_put(&s\_msgq, &msg, PBL\_NO\_WAIT);pbl\_msgq\_get(&s\_msgq, &msg, PBL\_NO\_WAIT);});BENCH("malloc + free, 64 bytes", { pbl\_free(pbl\_malloc(64)); });/\* A thread of main's priority that only yields: each yield here is two switches. \*/prv\_start(&s\_peer, "peer", prv\_peer, CONFIG\_KERNEL\_MAIN\_THREAD\_PRIO, s\_peer\_stack,sizeof(s\_peer\_stack));BENCH("yield, there and back", { pbl\_thread\_yield(); });pbl\_thread\_abort(&s\_peer);/\* A waiter above main: the give switches to it, and it blocks again at once. \*/prv\_start(&s\_waiter, "waiter", prv\_waiter, CONFIG\_KERNEL\_MAIN\_THREAD\_PRIO + 1, s\_waiter\_stack,sizeof(s\_waiter\_stack));BENCH("sem wake, there and back", { pbl\_sem\_give(&s\_ping); });pbl\_thread\_abort(&s\_waiter);BENCH("thread create, run, end", {prv\_start(&s\_short, "short", prv\_short, CONFIG\_KERNEL\_MAIN\_THREAD\_PRIO + 1, s\_short\_stack,sizeof(s\_short\_stack));});/\* The high priority queue runs above main, so its work starts before submit returns. \*/{uint32\_t total = 0;uint32\_t best = UINT32\_MAX;for (unsigned int i = 0; i \< ITERATIONS; i++) {uint32\_t submitted = pbl\_cycle\_get\_32();pbl\_work\_submit(&pbl\_sys\_workq\_high, &s\_work);uint32\_t cycles = s\_work\_started - submitted;total += cycles;best = cycles \< best ? cycles : best;}prv\_report("work submit to start", total / ITERATIONS, best);}PBL\_LOG\_INFO("Sample done");return 0;}
```

## Output

```
bench: 1000 iterations each, \d+ cycles per second
bench: empty measurement: avg \d+ cycles \(\d+ ns\), best \d+ cycles
bench: sem give \+ take: avg \d+ cycles \(\d+ ns\), best \d+ cycles
bench: mutex lock \+ unlock: avg \d+ cycles \(\d+ ns\), best \d+ cycles
bench: msgq put \+ get: avg \d+ cycles \(\d+ ns\), best \d+ cycles
bench: malloc \+ free, 64 bytes: avg \d+ cycles \(\d+ ns\), best \d+ cycles
bench: yield, there and back: avg \d+ cycles \(\d+ ns\), best \d+ cycles
bench: sem wake, there and back: avg \d+ cycles \(\d+ ns\), best \d+ cycles
bench: thread create, run, end: avg \d+ cycles \(\d+ ns\), best \d+ cycles
bench: work submit to start: avg \d+ cycles \(\d+ ns\), best \d+ cycles
Sample done
```

Generated from PebbleOS [f4926c952](https://github.com/coredevices/pebbleos/commit/f4926c952bf9b82bce9e3cdb44645a59531c0ee9). This page is maintained in the pebbleos repository: [docs/samples/benchmarks/kernel.md](https://github.com/coredevices/pebbleos/blob/f4926c952bf9b82bce9e3cdb44645a59531c0ee9/docs/samples/benchmarks/kernel.md).
