tests: latency_measure: Update semaphores

Updates the semaphore benchmarking in the latency_benchmark code
to support user threads.

Signed-off-by: Peter Mitsis <peter.mitsis@intel.com>
This commit is contained in:
Peter Mitsis 2023-09-15 13:43:54 -04:00 committed by Carles Cufí
commit 4c4453d697
2 changed files with 246 additions and 89 deletions

View file

@ -46,9 +46,10 @@ int error_count; /* track number of errors */
extern void thread_switch_yield(uint32_t num_iterations, bool is_cooperative);
extern void int_to_thread(uint32_t num_iterations);
extern void sema_test_signal(void);
extern void sema_test_signal(uint32_t num_iterations, uint32_t options);
extern void mutex_lock_unlock(void);
extern int sema_context_switch(void);
extern void sema_context_switch(uint32_t num_iterations,
uint32_t start_options, uint32_t alt_options);
extern int thread_ops(uint32_t num_iterations, uint32_t start_options,
uint32_t alt_options);
extern void heap_malloc_free(void);
@ -92,9 +93,17 @@ static void test_thread(void *arg1, void *arg2, void *arg3)
thread_ops(NUM_ITERATIONS, K_USER, 0);
#endif
sema_test_signal();
sema_test_signal(NUM_ITERATIONS, 0);
#ifdef CONFIG_USERSPACE
sema_test_signal(NUM_ITERATIONS, K_USER);
#endif
sema_context_switch();
sema_context_switch(NUM_ITERATIONS, 0, 0);
#ifdef CONFIG_USERSPACE
sema_context_switch(NUM_ITERATIONS, 0, K_USER);
sema_context_switch(NUM_ITERATIONS, K_USER, 0);
sema_context_switch(NUM_ITERATIONS, K_USER, K_USER);
#endif
mutex_lock_unlock();

View file

@ -1,5 +1,6 @@
/*
* Copyright (c) 2012-2015 Wind River Systems, Inc.
* Copyright (c) 2023 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
@ -7,68 +8,215 @@
/*
* @file measure time for sema lock and release
*
* This file contains the test that measures semaphore and mutex lock and
* release time in the kernel. There is no contention on the sema nor the
* mutex being tested.
* This file contains the test that measures semaphore give and take time
* in the kernel. There is no contention on the semaphore being tested.
*/
#include <zephyr/kernel.h>
#include <zephyr/timing/timing.h>
#include "utils.h"
/* the number of semaphore give/take cycles */
#define N_TEST_SEMA 1000
static struct k_sem sem;
#define STACK_SIZE (512 + CONFIG_TEST_EXTRA_STACK_SIZE)
/* stack used by the threads */
static K_THREAD_STACK_DEFINE(thread_one_stack, STACK_SIZE);
static struct k_thread thread_one_data;
K_SEM_DEFINE(lock_unlock_sema, 0, N_TEST_SEMA);
K_SEM_DEFINE(sem_bench, 0, 1);
timing_t timestamp_start_sema_t_c;
timing_t timestamp_end_sema_t_c;
timing_t timestamp_start_sema_g_c;
timing_t timestamp_end_sema_g_c;
void thread_sema_test1(void *p1, void *p2, void *p3)
static void alt_thread_entry(void *p1, void *p2, void *p3)
{
timestamp_start_sema_t_c = timing_counter_get();
k_sem_take(&sem_bench, K_FOREVER);
timestamp_end_sema_g_c = timing_counter_get();
uint32_t num_iterations = (uint32_t)(uintptr_t)p1;
timing_t mid;
ARG_UNUSED(p2);
ARG_UNUSED(p3);
for (uint32_t i = 0; i < num_iterations; i++) {
/*
* 2. Give the semaphore, thereby forcing a context switch back
* to <start_thread>.
*/
mid = timing_counter_get();
k_sem_give(&sem);
/* 5. Share the <mid> timestamp. */
timestamp.sample = mid;
/* 6. Give <sem> so <start_thread> resumes execution */
k_sem_give(&sem);
}
}
int sema_context_switch(void)
static void start_thread_entry(void *p1, void *p2, void *p3)
{
uint32_t diff;
uint32_t num_iterations = (uint32_t)(uintptr_t)p1;
timing_t start;
timing_t mid;
timing_t finish;
uint32_t i;
uint64_t take_sum = 0ull;
uint64_t give_sum = 0ull;
ARG_UNUSED(p2);
ARG_UNUSED(p3);
k_thread_start(&alt_thread);
for (i = 0; i < num_iterations; i++) {
/*
* 1. Block on taking the semaphore and force a context switch
* to <alt_thread>.
*/
start = timing_counter_get();
k_sem_take(&sem, K_FOREVER);
/* 3. Get the <finish> timestamp. */
finish = timing_counter_get();
/*
* 4. Let <alt_thread> run so it can share its <mid>
* timestamp.
*/
k_sem_take(&sem, K_FOREVER);
/* 7. Retrieve the <mid> timestamp */
mid = timestamp.sample;
take_sum += timing_cycles_get(&start, &mid);
give_sum += timing_cycles_get(&mid, &finish);
}
k_thread_join(&alt_thread, K_FOREVER);
/* Share the totals with the main thread */
timestamp.cycles = take_sum;
k_sem_take(&sem, K_FOREVER);
timestamp.cycles = give_sum;
}
void sema_context_switch(uint32_t num_iterations,
uint32_t start_options, uint32_t alt_options)
{
uint64_t cycles;
char description[80];
int priority;
timing_start();
k_thread_create(&thread_one_data, thread_one_stack,
STACK_SIZE, thread_sema_test1,
NULL, NULL, NULL,
K_PRIO_PREEMPT(3), 0, K_FOREVER);
k_thread_name_set(&thread_one_data, "sema_test1");
k_thread_start(&thread_one_data);
priority = k_thread_priority_get(k_current_get());
timestamp_end_sema_t_c = timing_counter_get();
diff = timing_cycles_get(&timestamp_start_sema_t_c, &timestamp_end_sema_t_c);
PRINT_STATS("Semaphore take time (context switch)", diff, false, "");
k_thread_create(&start_thread, start_stack,
K_THREAD_STACK_SIZEOF(start_stack),
start_thread_entry,
(void *)(uintptr_t)num_iterations, NULL, NULL,
priority - 2, start_options, K_FOREVER);
k_thread_create(&alt_thread, alt_stack,
K_THREAD_STACK_SIZEOF(alt_stack),
alt_thread_entry,
(void *)(uintptr_t)num_iterations, NULL, NULL,
priority - 1, alt_options, K_FOREVER);
timestamp_start_sema_g_c = timing_counter_get();
k_sem_give(&sem_bench);
diff = timing_cycles_get(&timestamp_start_sema_g_c, &timestamp_end_sema_g_c);
PRINT_STATS("Semaphore give time (context switch)", diff, false, "");
k_thread_access_grant(&start_thread, &sem, &alt_thread);
k_thread_access_grant(&alt_thread, &sem);
/* Start the test threads */
k_thread_start(&start_thread);
/* Retrieve the number of cycles spent taking the semaphore */
cycles = timestamp.cycles;
snprintf(description, sizeof(description),
"Take a semaphore (context switch %c -> %c)",
((start_options & K_USER) == K_USER) ? 'U' : 'K',
((alt_options & K_USER) == K_USER) ? 'U' : 'K');
PRINT_STATS_AVG(description, (uint32_t)cycles,
num_iterations, false, "");
/* Unblock <start_thread> */
k_sem_give(&sem);
/* Retrieve the number of cycles spent taking the semaphore */
cycles = timestamp.cycles;
snprintf(description, sizeof(description),
"Give a semaphore (context switch %c -> %c)",
((alt_options & K_USER) == K_USER) ? 'U' : 'K',
((start_options & K_USER) == K_USER) ? 'U' : 'K');
PRINT_STATS_AVG(description, (uint32_t)cycles,
num_iterations, false, "");
k_thread_join(&start_thread, K_FOREVER);
timing_stop();
return 0;
return;
}
/**
* This is the entry point for the test that performs uncontested operations
* on the semaphore. It gives the semaphore many times, takes the semaphore
* many times and then sends the results back to the main thread.
*/
static void immediate_give_take(void *p1, void *p2, void *p3)
{
uint32_t num_iterations = (uint32_t)(uintptr_t)p1;
timing_t start;
timing_t finish;
uint64_t give_cycles;
uint64_t take_cycles;
ARG_UNUSED(p2);
ARG_UNUSED(p3);
/* 1. Give a semaphore. No threads are waiting on it */
start = timing_counter_get();
for (uint32_t i = 0; i < num_iterations; i++) {
k_sem_give(&sem);
}
finish = timing_counter_get();
give_cycles = timing_cycles_get(&start, &finish);
/* 2. Take a semaphore--no contention */
start = timing_counter_get();
for (uint32_t i = 0; i < num_iterations; i++) {
k_sem_take(&sem, K_NO_WAIT);
}
finish = timing_counter_get();
take_cycles = timing_cycles_get(&start, &finish);
/* 3. Post the number of cycles spent giving the semaphore */
timestamp.cycles = give_cycles;
/* 4. Wait for the main thread to retrieve the data */
k_sem_take(&sem, K_FOREVER);
/* 7. Post the number of cycles spent taking the semaphore */
timestamp.cycles = take_cycles;
}
/**
*
* @brief The function tests semaphore test/signal time
@ -78,58 +226,58 @@ int sema_context_switch(void)
*
* @return 0 on success
*/
int sema_test_signal(void)
int sema_test_signal(uint32_t num_iterations, uint32_t options)
{
int i;
uint32_t diff;
timing_t timestamp_start;
timing_t timestamp_end;
const char *notes = "";
int end;
uint64_t cycles;
int priority;
char description[80];
bench_test_start();
timing_start();
timestamp_start = timing_counter_get();
k_sem_init(&sem, 0, num_iterations);
for (i = 0; i < N_TEST_SEMA; i++) {
k_sem_give(&lock_unlock_sema);
}
priority = k_thread_priority_get(k_current_get());
k_thread_create(&start_thread, start_stack,
K_THREAD_STACK_SIZEOF(start_stack),
immediate_give_take,
(void *)(uintptr_t)num_iterations, NULL, NULL,
priority - 1, options, K_FOREVER);
k_thread_access_grant(&start_thread, &sem);
k_thread_start(&start_thread);
/* 5. Retrieve the number of cycles spent giving the semaphore */
cycles = timestamp.cycles;
snprintf(description, sizeof(description),
"Give a semaphore (no waiters) from %s thread",
(options & K_USER) == K_USER ? "user" : "kernel");
PRINT_STATS_AVG(description, (uint32_t)cycles,
num_iterations, false, "");
/* 6. Unblock <start_thread> */
k_sem_give(&sem);
/* 8. Wait for <start_thread> to finish */
k_thread_join(&start_thread, K_FOREVER);
/* 9. Retrieve the number of cycles spent taking the semaphore */
cycles = timestamp.cycles;
snprintf(description, sizeof(description),
"Take a semaphore (no blocking) from %s thread",
(options & K_USER) == K_USER ? "user" : "kernel");
PRINT_STATS_AVG(description, (uint32_t)cycles,
num_iterations, false, "");
timestamp_end = timing_counter_get();
end = bench_test_end();
timing_stop();
if (end != 0) {
error_count++;
notes = TICK_OCCURRENCE_ERROR;
}
diff = timing_cycles_get(&timestamp_start, &timestamp_end);
PRINT_STATS_AVG("Average semaphore signal time", diff, N_TEST_SEMA,
false, notes);
bench_test_start();
timing_start();
timestamp_start = timing_counter_get();
for (i = 0; i < N_TEST_SEMA; i++) {
k_sem_take(&lock_unlock_sema, K_FOREVER);
}
timestamp_end = timing_counter_get();
end = bench_test_end();
timing_stop();
if (end != 0) {
error_count++;
notes = TICK_OCCURRENCE_ERROR;
}
diff = timing_cycles_get(&timestamp_start, &timestamp_end);
PRINT_STATS_AVG("Average semaphore test time", diff, N_TEST_SEMA,
false, notes);
return 0;
}