/* * Copyright (c) 2012-2015 Wind River Systems, Inc. * Copyright (c) 2023 Intel Corporation. * * SPDX-License-Identifier: Apache-2.0 */ /* * @file measure time for sema lock and release * * 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 #include #include "utils.h" #include "timing_sc.h" static struct k_sem sem; static void alt_thread_entry(void *p1, void *p2, void *p3) { 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 . */ mid = timing_timestamp_get(); k_sem_give(&sem); /* 5. Share the timestamp. */ timestamp.sample = mid; /* 6. Give so resumes execution */ k_sem_give(&sem); } } static void start_thread_entry(void *p1, void *p2, void *p3) { 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 . */ start = timing_timestamp_get(); k_sem_take(&sem, K_FOREVER); /* 3. Get the timestamp. */ finish = timing_timestamp_get(); /* * 4. Let run so it can share its * timestamp. */ k_sem_take(&sem, K_FOREVER); /* 7. Retrieve the 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 tag[50]; char description[120]; int priority; timing_start(); priority = k_thread_priority_get(k_current_get()); 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); 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; cycles -= timestamp_overhead_adjustment(start_options, alt_options); snprintf(tag, sizeof(tag), "semaphore.take.blocking.%c_to_%c", ((start_options & K_USER) == K_USER) ? 'u' : 'k', ((alt_options & K_USER) == K_USER) ? 'u' : 'k'); snprintf(description, sizeof(description), "%-40s - Take a semaphore (context switch)", tag); PRINT_STATS_AVG(description, (uint32_t)cycles, num_iterations, false, ""); /* Unblock */ k_sem_give(&sem); /* Retrieve the number of cycles spent taking the semaphore */ cycles = timestamp.cycles; cycles -= timestamp_overhead_adjustment(start_options, alt_options); snprintf(tag, sizeof(tag), "semaphore.give.wake+ctx.%c_to_%c", ((alt_options & K_USER) == K_USER) ? 'u' : 'k', ((start_options & K_USER) == K_USER) ? 'u' : 'k'); snprintf(description, sizeof(description), "%-40s - Give a semaphore (context switch)", tag); PRINT_STATS_AVG(description, (uint32_t)cycles, num_iterations, false, ""); k_thread_join(&start_thread, K_FOREVER); timing_stop(); 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_timestamp_get(); for (uint32_t i = 0; i < num_iterations; i++) { k_sem_give(&sem); } finish = timing_timestamp_get(); give_cycles = timing_cycles_get(&start, &finish); /* 2. Take a semaphore--no contention */ start = timing_timestamp_get(); for (uint32_t i = 0; i < num_iterations; i++) { k_sem_take(&sem, K_NO_WAIT); } finish = timing_timestamp_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 * * The routine performs unlock the quite amount of semaphores and then * acquires them in order to measure the necessary time. * * @return 0 on success */ int sema_test_signal(uint32_t num_iterations, uint32_t options) { uint64_t cycles; int priority; char tag[50]; char description[120]; timing_start(); k_sem_init(&sem, 0, num_iterations); 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(tag, sizeof(tag), "semaphore.give.immediate.%s", (options & K_USER) == K_USER ? "user" : "kernel"); snprintf(description, sizeof(description), "%-40s - Give a semaphore (no waiters)", tag); PRINT_STATS_AVG(description, (uint32_t)cycles, num_iterations, false, ""); /* 6. Unblock */ k_sem_give(&sem); /* 8. Wait for to finish */ k_thread_join(&start_thread, K_FOREVER); /* 9. Retrieve the number of cycles spent taking the semaphore */ cycles = timestamp.cycles; snprintf(tag, sizeof(tag), "semaphore.take.immediate.%s", (options & K_USER) == K_USER ? "user" : "kernel"); snprintf(description, sizeof(description), "%-40s - Take a semaphore (no blocking)", tag); PRINT_STATS_AVG(description, (uint32_t)cycles, num_iterations, false, ""); timing_stop(); return 0; }