/* * Copyright (c) 2012-2014 Wind River Systems, Inc. * Copyright (c) 2023 Intel Corporation. * * SPDX-License-Identifier: Apache-2.0 */ /** * @file * This file contains the benchmarking code that measures the average time it * takes to perform context switches between threads using k_yield(). * * When user threads are supported, there are four cases to consider. These are * 1. Kernel thread -> Kernel thread * 2. User thread -> User thread * 3. Kernel thread -> User thread * 4. User thread -> Kernel thread */ #include #include #include #include #include "utils.h" #include "timing_sc.h" static void alt_thread_entry(void *p1, void *p2, void *p3) { uint32_t num_iterations; ARG_UNUSED(p2); ARG_UNUSED(p2); num_iterations = (uint32_t)(uintptr_t)p1; for (uint32_t i = 0; i < num_iterations; i++) { /* 3. Obtain the 'finish' timestamp */ timestamp.sample = timing_timestamp_get(); /* 4. Switch to */ k_yield(); } } static void start_thread_entry(void *p1, void *p2, void *p3) { uint64_t sum = 0ull; uint32_t num_iterations; timing_t start; timing_t finish; ARG_UNUSED(p2); ARG_UNUSED(p2); num_iterations = (uint32_t)(uintptr_t)p1; k_thread_start(&alt_thread); for (uint32_t i = 0; i < num_iterations; i++) { /* 1. Get 'start' timestamp */ start = timing_timestamp_get(); /* 2. Switch to */ k_yield(); /* 5. Get the 'finish' timestamp obtained in */ finish = timestamp.sample; /* 6. Track the sum of elapsed times */ sum += timing_cycles_get(&start, &finish); } /* Wait for to complete */ k_thread_join(&alt_thread, K_FOREVER); /* Record the number of cycles for use by the main thread */ timestamp.cycles = sum; } static void thread_switch_yield_common(const char *description, uint32_t num_iterations, uint32_t start_options, uint32_t alt_options, int priority) { uint64_t sum; char tag[50]; char summary[120]; /* Create the two threads */ k_thread_create(&start_thread, start_stack, K_THREAD_STACK_SIZEOF(start_stack), start_thread_entry, (void *)(uintptr_t)num_iterations, NULL, NULL, priority - 1, 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); /* Grant access rights if necessary */ if ((start_options & K_USER) == K_USER) { k_thread_access_grant(&start_thread, &alt_thread); } k_thread_start(&start_thread); /* Wait until finishes */ k_thread_join(&start_thread, K_FOREVER); /* Get the sum total of measured cycles */ sum = timestamp.cycles; sum -= timestamp_overhead_adjustment(start_options, alt_options); snprintf(tag, sizeof(tag), "%s.%c_to_%c", description, (start_options & K_USER) == K_USER ? 'u' : 'k', (alt_options & K_USER) == K_USER ? 'u' : 'k'); snprintf(summary, sizeof(summary), "%-40s - Context switch via k_yield", tag); PRINT_STATS_AVG(summary, (uint32_t)sum, num_iterations, 0, ""); } void thread_switch_yield(uint32_t num_iterations, bool is_cooperative) { int priority; char description[40]; priority = is_cooperative ? K_PRIO_COOP(6) : k_thread_priority_get(k_current_get()) - 1; snprintf(description, sizeof(description), "thread.yield.%s.ctx", is_cooperative ? "cooperative" : "preemptive"); /* Kernel -> Kernel */ thread_switch_yield_common(description, num_iterations, 0, 0, priority); #if CONFIG_USERSPACE /* User -> User */ thread_switch_yield_common(description, num_iterations, K_USER, K_USER, priority); /* Kernel -> User */ thread_switch_yield_common(description, num_iterations, 0, K_USER, priority); /* User -> Kernel */ thread_switch_yield_common(description, num_iterations, K_USER, 0, priority); #endif }