tests: latency_measure: Update thread ops
Update the thread ops (create/start/suspend/resume/abort) to include support for userspace. Signed-off-by: Peter Mitsis <peter.mitsis@intel.com>
This commit is contained in:
parent
4e7bb482b1
commit
13ac14f2bb
2 changed files with 278 additions and 54 deletions
|
@ -49,7 +49,8 @@ extern void int_to_thread(uint32_t num_iterations);
|
||||||
extern void sema_test_signal(void);
|
extern void sema_test_signal(void);
|
||||||
extern void mutex_lock_unlock(void);
|
extern void mutex_lock_unlock(void);
|
||||||
extern int sema_context_switch(void);
|
extern int sema_context_switch(void);
|
||||||
extern int suspend_resume(void);
|
extern int thread_ops(uint32_t num_iterations, uint32_t start_options,
|
||||||
|
uint32_t alt_options);
|
||||||
extern void heap_malloc_free(void);
|
extern void heap_malloc_free(void);
|
||||||
|
|
||||||
static void test_thread(void *arg1, void *arg2, void *arg3)
|
static void test_thread(void *arg1, void *arg2, void *arg3)
|
||||||
|
@ -82,7 +83,14 @@ static void test_thread(void *arg1, void *arg2, void *arg3)
|
||||||
|
|
||||||
int_to_thread(NUM_ITERATIONS);
|
int_to_thread(NUM_ITERATIONS);
|
||||||
|
|
||||||
suspend_resume();
|
/* Thread creation, starting, suspending, resuming and aborting. */
|
||||||
|
|
||||||
|
thread_ops(NUM_ITERATIONS, 0, 0);
|
||||||
|
#ifdef CONFIG_USERSPACE
|
||||||
|
thread_ops(NUM_ITERATIONS, 0, K_USER);
|
||||||
|
thread_ops(NUM_ITERATIONS, K_USER, K_USER);
|
||||||
|
thread_ops(NUM_ITERATIONS, K_USER, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
sema_test_signal();
|
sema_test_signal();
|
||||||
|
|
||||||
|
|
|
@ -1,86 +1,302 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020 Intel Corporation
|
* Copyright (c) 2020, 2023 Intel Corporation
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @file measure time for various thread operations
|
||||||
|
*
|
||||||
|
* This file contains the tests that measures the times for the following
|
||||||
|
* thread operations from both kernel threads and user threads:
|
||||||
|
* 1. Creating a thread
|
||||||
|
* 2. Starting a thread
|
||||||
|
* 3. Suspending a thread
|
||||||
|
* 4. Resuming a thread
|
||||||
|
* 5. Aborting a thread
|
||||||
|
*
|
||||||
|
* It is worthwhile to note that there is no measurement for creating a kernel
|
||||||
|
* thread from a user thread as that is an invalid operation.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <zephyr/kernel.h>
|
#include <zephyr/kernel.h>
|
||||||
#include <zephyr/timing/timing.h>
|
#include <zephyr/timing/timing.h>
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
#define STACK_SIZE (512 + CONFIG_TEST_EXTRA_STACK_SIZE)
|
#define STACK_SIZE (512 + CONFIG_TEST_EXTRA_STACK_SIZE)
|
||||||
/* stack used by the threads */
|
|
||||||
static K_THREAD_STACK_DEFINE(t1_stack, STACK_SIZE);
|
|
||||||
|
|
||||||
static struct k_thread t1;
|
#define START_ALT 0x01
|
||||||
|
#define ALT_USER 0x02
|
||||||
|
|
||||||
timing_t timestamp_start_create_c;
|
static void alt_thread_entry(void *p1, void *p2, void *p3)
|
||||||
timing_t timestamp_end_create_c;
|
|
||||||
timing_t timestamp_start_start_c;
|
|
||||||
timing_t timestamp_end_start_c;
|
|
||||||
timing_t timestamp_start_suspend_c;
|
|
||||||
timing_t timestamp_end_suspend_c;
|
|
||||||
timing_t timestamp_start_resume_c;
|
|
||||||
timing_t timestamp_end_resume_c;
|
|
||||||
|
|
||||||
timing_t timestamp_start_abort_1;
|
|
||||||
timing_t timestamp_end_abort_1;
|
|
||||||
|
|
||||||
void thread_suspend_resume(void *p1, void *p2, void *p3)
|
|
||||||
{
|
{
|
||||||
timestamp_start_suspend_c = timing_counter_get();
|
int priority;
|
||||||
k_thread_suspend(_current);
|
|
||||||
|
|
||||||
/* comes to this line once its resumed*/
|
ARG_UNUSED(p1);
|
||||||
timestamp_start_resume_c = timing_counter_get();
|
ARG_UNUSED(p2);
|
||||||
|
ARG_UNUSED(p3);
|
||||||
|
|
||||||
|
/* 3. Finish measuring time to start <alt_thread> */
|
||||||
|
|
||||||
|
timestamp.sample = timing_counter_get();
|
||||||
|
|
||||||
|
/* 4. Let <start_thread> process the time measurement. */
|
||||||
|
|
||||||
|
k_sem_take(&pause_sem, K_FOREVER); /* Let 'start_thread' execute */
|
||||||
|
|
||||||
|
/* 7. Begin measuring time to suspend active thread (self/alt_thread) */
|
||||||
|
|
||||||
|
timestamp.sample = timing_counter_get();
|
||||||
|
k_thread_suspend(&alt_thread);
|
||||||
|
|
||||||
|
/* 10. Finish measuring time to resume <alt_thread> (self) */
|
||||||
|
|
||||||
|
timestamp.sample = timing_counter_get();
|
||||||
|
|
||||||
|
/* 11. Lower the priority so <start_thread> can terminate us. */
|
||||||
|
|
||||||
|
priority = k_thread_priority_get(&alt_thread);
|
||||||
|
k_thread_priority_set(&alt_thread, priority + 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
int suspend_resume(void)
|
static void start_thread_entry(void *p1, void *p2, void *p3)
|
||||||
{
|
{
|
||||||
uint32_t diff;
|
uint32_t num_iterations = (uint32_t)(uintptr_t)p1;
|
||||||
|
uint32_t bit_options = (uint32_t)(uintptr_t)p2;
|
||||||
|
timing_t start;
|
||||||
|
timing_t finish;
|
||||||
|
uint64_t thread_create_sum = 0ull;
|
||||||
|
uint64_t thread_start_sum = 0ull;
|
||||||
|
uint64_t thread_suspend_sum = 0ull;
|
||||||
|
uint64_t thread_resume_sum = 0ull;
|
||||||
|
uint64_t thread_abort_sum = 0ull;
|
||||||
|
int priority;
|
||||||
|
|
||||||
|
ARG_UNUSED(p3);
|
||||||
|
|
||||||
|
priority = k_thread_priority_get(&start_thread);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < num_iterations; i++) {
|
||||||
|
|
||||||
|
/* 1. Measure time to create, but not start <alt_thread> */
|
||||||
|
|
||||||
|
if ((bit_options & START_ALT) == START_ALT) {
|
||||||
|
start = timing_counter_get();
|
||||||
|
k_thread_create(&alt_thread, alt_stack,
|
||||||
|
K_THREAD_STACK_SIZEOF(alt_stack),
|
||||||
|
alt_thread_entry, NULL, NULL, NULL,
|
||||||
|
priority,
|
||||||
|
(bit_options & ALT_USER) == ALT_USER ?
|
||||||
|
K_USER : 0, K_FOREVER);
|
||||||
|
finish = timing_counter_get();
|
||||||
|
|
||||||
|
thread_create_sum += timing_cycles_get(&start, &finish);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wait for the "main" thread to create <alt_thread>
|
||||||
|
* as this thread can not do it.
|
||||||
|
*/
|
||||||
|
|
||||||
|
k_sem_take(&pause_sem, K_FOREVER);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((bit_options & ALT_USER) == ALT_USER) {
|
||||||
|
k_thread_access_grant(&alt_thread, &pause_sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Let the main thread change the priority of <alt_thread>
|
||||||
|
* to a higher priority level as user threads may not create
|
||||||
|
* a thread of higher priority than itself.
|
||||||
|
*/
|
||||||
|
|
||||||
|
k_sem_take(&pause_sem, K_FOREVER);
|
||||||
|
|
||||||
|
|
||||||
|
/* 2. Begin measuring time to start <alt_thread> */
|
||||||
|
|
||||||
|
start = timing_counter_get();
|
||||||
|
k_thread_start(&alt_thread);
|
||||||
|
|
||||||
|
/* 5. Process the time to start <alt_thread> */
|
||||||
|
|
||||||
|
finish = timestamp.sample;
|
||||||
|
thread_start_sum += timing_cycles_get(&start, &finish);
|
||||||
|
|
||||||
|
/* 6. Allow <alt_thread> to continue */
|
||||||
|
|
||||||
|
k_sem_give(&pause_sem);
|
||||||
|
|
||||||
|
/* 8. Finish measuring time to suspend <alt_thread> */
|
||||||
|
|
||||||
|
start = timestamp.sample;
|
||||||
|
finish = timing_counter_get();
|
||||||
|
thread_suspend_sum += timing_cycles_get(&start, &finish);
|
||||||
|
|
||||||
|
/* 9. Being measuring time to resume <alt_thread> */
|
||||||
|
|
||||||
|
start = timing_counter_get();
|
||||||
|
k_thread_resume(&alt_thread);
|
||||||
|
|
||||||
|
/* 12. Process the time it took to resume <alt_thread> */
|
||||||
|
|
||||||
|
finish = timestamp.sample;
|
||||||
|
thread_resume_sum += timing_cycles_get(&start, &finish);
|
||||||
|
|
||||||
|
/* 13. Process the time to terminate <alt_thread> */
|
||||||
|
|
||||||
|
start = timing_counter_get();
|
||||||
|
k_thread_abort(&alt_thread);
|
||||||
|
finish = timing_counter_get();
|
||||||
|
thread_abort_sum += timing_cycles_get(&start, &finish);
|
||||||
|
}
|
||||||
|
|
||||||
|
timestamp.cycles = thread_create_sum;
|
||||||
|
k_sem_take(&pause_sem, K_FOREVER);
|
||||||
|
|
||||||
|
timestamp.cycles = thread_start_sum;
|
||||||
|
k_sem_take(&pause_sem, K_FOREVER);
|
||||||
|
|
||||||
|
timestamp.cycles = thread_suspend_sum;
|
||||||
|
k_sem_take(&pause_sem, K_FOREVER);
|
||||||
|
|
||||||
|
timestamp.cycles = thread_resume_sum;
|
||||||
|
k_sem_take(&pause_sem, K_FOREVER);
|
||||||
|
|
||||||
|
timestamp.cycles = thread_abort_sum;
|
||||||
|
k_sem_take(&pause_sem, K_FOREVER);
|
||||||
|
}
|
||||||
|
|
||||||
|
int thread_ops(uint32_t num_iterations, uint32_t start_options, uint32_t alt_options)
|
||||||
|
{
|
||||||
|
int priority;
|
||||||
|
uint64_t cycles;
|
||||||
|
uint32_t bit_options = START_ALT;
|
||||||
|
char description[80];
|
||||||
|
|
||||||
|
priority = k_thread_priority_get(k_current_get());
|
||||||
|
|
||||||
timing_start();
|
timing_start();
|
||||||
|
|
||||||
timestamp_start_create_c = timing_counter_get();
|
/*
|
||||||
|
* Determine if <start_thread> is allowed to start <alt_thread>.
|
||||||
|
* If it can not, then <alt_thread) must be created by the current
|
||||||
|
* thread.
|
||||||
|
*/
|
||||||
|
|
||||||
k_tid_t t1_tid = k_thread_create(&t1, t1_stack, STACK_SIZE,
|
if (((start_options & K_USER) == K_USER) &&
|
||||||
thread_suspend_resume, NULL, NULL,
|
((alt_options & K_USER) == 0)) {
|
||||||
NULL, K_PRIO_PREEMPT(6), 0, K_FOREVER);
|
bit_options = 0;
|
||||||
|
}
|
||||||
|
|
||||||
timestamp_end_create_c = timing_counter_get();
|
if ((alt_options & K_USER) == K_USER) {
|
||||||
k_thread_name_set(t1_tid, "t1");
|
bit_options |= ALT_USER;
|
||||||
|
}
|
||||||
|
|
||||||
timestamp_start_start_c = timing_counter_get();
|
k_thread_create(&start_thread, start_stack,
|
||||||
k_thread_start(t1_tid);
|
K_THREAD_STACK_SIZEOF(start_stack),
|
||||||
|
start_thread_entry,
|
||||||
|
(void *)(uintptr_t)num_iterations,
|
||||||
|
(void *)(uintptr_t)bit_options, NULL,
|
||||||
|
priority - 1, start_options, K_FOREVER);
|
||||||
|
|
||||||
timestamp_end_suspend_c = timing_counter_get();
|
if ((start_options & K_USER) == K_USER) {
|
||||||
k_thread_resume(t1_tid);
|
k_thread_access_grant(&start_thread, &alt_thread, &alt_stack,
|
||||||
timestamp_end_resume_c = timing_counter_get();
|
&pause_sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
k_thread_start(&start_thread);
|
||||||
|
|
||||||
diff = timing_cycles_get(×tamp_start_create_c,
|
for (uint32_t i = 0; i < num_iterations; i++) {
|
||||||
×tamp_end_create_c);
|
if ((bit_options & START_ALT) == 0) {
|
||||||
PRINT_STATS("Time to create a thread (without start)", diff, false, "");
|
|
||||||
|
|
||||||
diff = timing_cycles_get(×tamp_start_start_c,
|
/*
|
||||||
×tamp_start_suspend_c);
|
* <start_thread> can not create <alt_thread> as it
|
||||||
PRINT_STATS("Time to start a thread", diff, false, "");
|
* would be a user thread trying to create a kernel
|
||||||
|
* thread. Instead, create <alt_thread> here.
|
||||||
|
*/
|
||||||
|
|
||||||
diff = timing_cycles_get(×tamp_start_suspend_c,
|
k_thread_create(&alt_thread, alt_stack,
|
||||||
×tamp_end_suspend_c);
|
K_THREAD_STACK_SIZEOF(alt_stack),
|
||||||
PRINT_STATS("Time to suspend a thread", diff, false, "");
|
alt_thread_entry,
|
||||||
|
NULL, NULL, NULL,
|
||||||
|
priority - 1, alt_options, K_FOREVER);
|
||||||
|
|
||||||
diff = timing_cycles_get(×tamp_start_resume_c,
|
/* Give <pause_sem> sends us back to <start_thread> */
|
||||||
×tamp_end_resume_c);
|
|
||||||
PRINT_STATS("Time to resume a thread", diff, false, "");
|
|
||||||
|
|
||||||
timestamp_start_abort_1 = timing_counter_get();
|
k_sem_give(&pause_sem);
|
||||||
k_thread_abort(t1_tid);
|
}
|
||||||
timestamp_end_abort_1 = timing_counter_get();
|
|
||||||
|
|
||||||
diff = timing_cycles_get(×tamp_start_abort_1,
|
/*
|
||||||
×tamp_end_abort_1);
|
* <alt_thread> needs to be of higher priority than
|
||||||
PRINT_STATS("Time to abort a thread (not running)", diff, false, "");
|
* <start_thread>, which can not always be done in
|
||||||
|
* <start_thread> as sometimes it is a user thread.
|
||||||
|
*/
|
||||||
|
|
||||||
|
k_thread_priority_set(&alt_thread, priority - 2);
|
||||||
|
k_sem_give(&pause_sem);
|
||||||
|
}
|
||||||
|
|
||||||
|
cycles = timestamp.cycles;
|
||||||
|
k_sem_give(&pause_sem);
|
||||||
|
|
||||||
|
if ((bit_options & START_ALT) == START_ALT) {
|
||||||
|
|
||||||
|
/* Only report stats if <start_thread> created <alt_thread> */
|
||||||
|
|
||||||
|
snprintf(description, sizeof(description),
|
||||||
|
"Create %s thread from %s thread",
|
||||||
|
(alt_options & K_USER) != 0 ? "user" : "kernel",
|
||||||
|
(start_options & K_USER) != 0 ? "user" : "kernel");
|
||||||
|
|
||||||
|
PRINT_STATS_AVG(description, (uint32_t)cycles,
|
||||||
|
num_iterations, false, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
cycles = timestamp.cycles;
|
||||||
|
k_sem_give(&pause_sem);
|
||||||
|
|
||||||
|
snprintf(description, sizeof(description),
|
||||||
|
"Start %s thread from %s thread",
|
||||||
|
(alt_options & K_USER) != 0 ? "user" : "kernel",
|
||||||
|
(start_options & K_USER) != 0 ? "user" : "kernel");
|
||||||
|
|
||||||
|
PRINT_STATS_AVG(description, (uint32_t)cycles,
|
||||||
|
num_iterations, false, "");
|
||||||
|
|
||||||
|
cycles = timestamp.cycles;
|
||||||
|
k_sem_give(&pause_sem);
|
||||||
|
|
||||||
|
snprintf(description, sizeof(description),
|
||||||
|
"Suspend %s thread from %s thread",
|
||||||
|
(alt_options & K_USER) != 0 ? "user" : "kernel",
|
||||||
|
(start_options & K_USER) != 0 ? "user" : "kernel");
|
||||||
|
|
||||||
|
PRINT_STATS_AVG(description, (uint32_t)cycles,
|
||||||
|
num_iterations, false, "");
|
||||||
|
|
||||||
|
cycles = timestamp.cycles;
|
||||||
|
k_sem_give(&pause_sem);
|
||||||
|
|
||||||
|
snprintf(description, sizeof(description),
|
||||||
|
"Resume %s thread from %s thread",
|
||||||
|
(alt_options & K_USER) != 0 ? "user" : "kernel",
|
||||||
|
(start_options & K_USER) != 0 ? "user" : "kernel");
|
||||||
|
|
||||||
|
PRINT_STATS_AVG(description, (uint32_t)cycles,
|
||||||
|
num_iterations, false, "");
|
||||||
|
|
||||||
|
cycles = timestamp.cycles;
|
||||||
|
k_sem_give(&pause_sem);
|
||||||
|
|
||||||
|
snprintf(description, sizeof(description),
|
||||||
|
"Abort %s thread from %s thread",
|
||||||
|
(alt_options & K_USER) != 0 ? "user" : "kernel",
|
||||||
|
(start_options & K_USER) != 0 ? "user" : "kernel");
|
||||||
|
|
||||||
|
PRINT_STATS_AVG(description, (uint32_t)cycles,
|
||||||
|
num_iterations, false, "");
|
||||||
|
|
||||||
timing_stop();
|
timing_stop();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue