From d0f790e166ee4b027f097b637619e11d70790319 Mon Sep 17 00:00:00 2001 From: yinshengkai Date: Mon, 26 Jun 2023 17:30:45 +0800 Subject: [PATCH] testing: add os performance measurement Measure the performance of core system functions such as thread switching and the time required for semaphore execution Signed-off-by: yinshengkai --- testing/osperf/Kconfig | 23 ++ testing/osperf/Make.defs | 23 ++ testing/osperf/Makefile | 30 +++ testing/osperf/osperf.c | 461 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 537 insertions(+) create mode 100644 testing/osperf/Kconfig create mode 100644 testing/osperf/Make.defs create mode 100644 testing/osperf/Makefile create mode 100644 testing/osperf/osperf.c diff --git a/testing/osperf/Kconfig b/testing/osperf/Kconfig new file mode 100644 index 000000000..7520899df --- /dev/null +++ b/testing/osperf/Kconfig @@ -0,0 +1,23 @@ +# +# For a description of the syntax of this configuration file, +# see the file kconfig-language.txt in the NuttX tools repository. +# + +config TESTING_OSPERF + tristate "System performance profiling" + default n + ---help--- + Measure the performance of core system functions, such as thread + switching and the time required for semaphore execution + +if TESTING_OSPERF + +config TESTING_OSPERF_PRIORITY + int "OS profiling task priority" + default 100 + +config TESTING_OSPERF_STACKSIZE + int "OS profiling stack size" + default DEFAULT_TASK_STACKSIZE + +endif diff --git a/testing/osperf/Make.defs b/testing/osperf/Make.defs new file mode 100644 index 000000000..83f4dd8b1 --- /dev/null +++ b/testing/osperf/Make.defs @@ -0,0 +1,23 @@ +############################################################################ +# apps/testing/osperf/Make.defs +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +ifneq ($(CONFIG_TESTING_OSPERF),) +CONFIGURED_APPS += $(APPDIR)/testing/osperf +endif diff --git a/testing/osperf/Makefile b/testing/osperf/Makefile new file mode 100644 index 000000000..3da227093 --- /dev/null +++ b/testing/osperf/Makefile @@ -0,0 +1,30 @@ +############################################################################ +# apps/testing/osperf/Makefile +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. The +# ASF licenses this file to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance with the +# License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# +############################################################################ + +include $(APPDIR)/Make.defs + +PROGNAME = osperf +PRIORITY = $(CONFIG_TESTING_OSPERF_PRIORITY) +STACKSIZE = $(CONFIG_TESTING_OSPERF_STACKSIZE) +MODULE = $(CONFIG_TESTING_OSPERF) + +MAINSRC = osperf.c + +include $(APPDIR)/Application.mk diff --git a/testing/osperf/osperf.c b/testing/osperf/osperf.c new file mode 100644 index 000000000..ad59fbd34 --- /dev/null +++ b/testing/osperf/osperf.c @@ -0,0 +1,461 @@ +/**************************************************************************** + * apps/testing/osperf/osperf.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct performance_time_s +{ + size_t start; + size_t end; +}; + +struct performance_thread_s +{ + sem_t sem; + struct performance_time_s time; +}; + +struct performance_entry_s +{ + const char name[NAME_MAX]; + CODE size_t (*entry)(void); +}; + +/**************************************************************************** + * Private Functions Prototypes + ****************************************************************************/ + +static size_t pthread_create_performance(void); +static size_t pthread_switch_performance(void); +static size_t context_switch_performance(void); +static size_t hpwork_performance(void); +static size_t poll_performance(void); +static size_t semwait_performance(void); +static size_t sempost_performance(void); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct performance_entry_s g_entry_list[] = +{ + {"pthread-create", pthread_create_performance}, + {"pthread-switch", pthread_switch_performance}, + {"context-switch", context_switch_performance}, + {"hpwork", hpwork_performance}, + {"poll-write", poll_performance}, + {"semwait", semwait_performance}, + {"sempost", sempost_performance}, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int performance_thread_create(FAR void *(*entry)(FAR void *), + FAR void *arg, int priority) +{ + struct sched_param param; + pthread_attr_t attr; + pthread_t tid; + + param.sched_priority = priority; + pthread_attr_init(&attr); + pthread_attr_setschedpolicy(&attr, SCHED_FIFO); + pthread_attr_setschedparam(&attr, ¶m); + pthread_create(&tid, &attr, entry, arg); + DEBUGASSERT(tid > 0); + return tid; +} + +static void performance_start(FAR struct performance_time_s *result) +{ + result->start = up_perf_gettime(); +} + +static void performance_end(FAR struct performance_time_s *result) +{ + result->end = up_perf_gettime(); +} + +static size_t performance_gettime(FAR struct performance_time_s *result) +{ + struct timespec ts; + up_perf_convert(result->end - result->start, &ts); + return ts.tv_sec * NSEC_PER_SEC + ts.tv_nsec; +} + +/**************************************************************************** + * Pthread swtich performance + ****************************************************************************/ + +static FAR void *pthread_switch_task(FAR void *arg) +{ + FAR struct performance_thread_s *perf = arg; + irq_t flags = enter_critical_section(); + sem_wait(&perf->sem); + performance_end(&perf->time); + leave_critical_section(flags); + return NULL; +} + +static size_t pthread_switch_performance(void) +{ + struct performance_thread_s perf; + pthread_t tid; + + sem_init(&perf.sem, 0, 0); + tid = performance_thread_create(pthread_switch_task, &perf, + CONFIG_TESTING_OSPERF_PRIORITY + 1); + + performance_start(&perf.time); + sem_post(&perf.sem); + pthread_join(tid, NULL); + + return performance_gettime(&perf.time); +} + +/**************************************************************************** + * Pthread create performance + ****************************************************************************/ + +static FAR void *pthread_create_task(FAR void *arg) +{ + FAR struct performance_time_s *time = arg; + performance_end(time); + return NULL; +} + +static size_t pthread_create_performance(void) +{ + struct performance_time_s result; + struct sched_param param; + pthread_attr_t attr; + pthread_t tid; + + sched_getparam(gettid(), ¶m); + param.sched_priority++; + pthread_attr_init(&attr); + pthread_attr_setschedpolicy(&attr, SCHED_FIFO); + pthread_attr_setschedparam(&attr, ¶m); + + performance_start(&result); + pthread_create(&tid, &attr, pthread_create_task, &result); + pthread_join(tid, NULL); + + return performance_gettime(&result); +} + +/**************************************************************************** + * Contxt create performance + ****************************************************************************/ + +static FAR void *context_swtich_task(FAR void *arg) +{ + FAR struct performance_time_s *time = arg; + sched_yield(); + performance_end(time); + return 0; +} + +static size_t context_switch_performance(void) +{ + struct performance_time_s time; + + performance_thread_create(context_swtich_task, &time, + CONFIG_INIT_PRIORITY); + sched_yield(); + performance_start(&time); + sched_yield(); + + return performance_gettime(&time); +} + +/**************************************************************************** + * wdog performance + ****************************************************************************/ + +static void work_handle(void *arg) +{ + FAR struct performance_time_s *time = (FAR struct performance_time_s *)arg; + performance_end(time); +} + +static size_t hpwork_performance(void) +{ + struct performance_time_s result; + struct work_s work; + int ret; + + memset(&work, 0, sizeof(work)); + performance_start(&result); + ret = work_queue(HPWORK, &work, work_handle, &result, 0); + DEBUGASSERT(ret == 0); + + return performance_gettime(&result); +} + +/**************************************************************************** + * poll-write performance + ****************************************************************************/ + +static FAR void *poll_task(FAR void *arg) +{ + FAR void **argv = arg; + FAR struct performance_time_s *time = argv[0]; + int pipefd = (int)argv[1]; + + performance_start(time); + write(pipefd, "a", 1); + return 0; +} + +static size_t poll_performance(void) +{ + struct performance_time_s result; + struct pollfd fds; + FAR void *argv[2]; + int pipefd[2]; + int ret; + + ret = pipe(pipefd); + DEBUGASSERT(ret == 0); + fds.fd = pipefd[0]; + fds.events = POLLIN; + argv[0] = (FAR char *)&result; + argv[1] = (FAR char *)(uintptr_t)pipefd[1]; + + performance_thread_create(poll_task, argv, CONFIG_INIT_PRIORITY); + + poll(&fds, 1, -1); + performance_end(&result); + + close(pipefd[0]); + close(pipefd[1]); + return performance_gettime(&result); +} + +/**************************************************************************** + * semwait_performance + ****************************************************************************/ + +static size_t semwait_performance(void) +{ + struct performance_time_s result; + sem_t sem; + + sem_init(&sem, 0, 2); + + performance_start(&result); + sem_wait(&sem); + performance_end(&result); + + sem_destroy(&sem); + return performance_gettime(&result); +} + +/**************************************************************************** + * sempost_performance + ****************************************************************************/ + +static size_t sempost_performance(void) +{ + struct performance_time_s result; + sem_t sem; + + sem_init(&sem, 0, 0); + + performance_start(&result); + sem_post(&sem); + performance_end(&result); + + sem_destroy(&sem); + return performance_gettime(&result); +} + +/**************************************************************************** + * performance_help + ****************************************************************************/ + +static void performance_help(void) +{ + printf("Usage: performance [OPTIONS] [name]\n\n"); + printf("OPTIONS:\n"); + printf("\t-c, \tNumber of times to run each test\n"); + printf("\t-d, \tShow detail of each test\n"); + printf("\t-h, \tShow this help message\n"); + printf("\t-l, \tList all tests\n"); +} + +/**************************************************************************** + * performance_run + ****************************************************************************/ + +static void performance_run(const FAR struct performance_entry_s *item, + size_t count, bool detail) +{ + size_t total = 0; + size_t max = 0; + size_t min = 0; + size_t i; + + for (i = 0; i < count; i++) + { + irq_t flags = enter_critical_section(); + size_t time = item->entry(); + leave_critical_section(flags); + + total += time; + if (time > max) + { + max = time; + } + + if (time < min || min == 0) + { + min = time; + } + + if (detail) + { + printf("\t%d: %zu\n", i, time); + } + } + + printf("%-*s %10zu %10zu %10zu\n", NAME_MAX, item->name, max, min, + total / count); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +static const FAR +struct performance_entry_s *find_entry(FAR const char *name) +{ + size_t i; + + for (i = 0; i < nitems(g_entry_list); i++) + { + if (strcmp(name, g_entry_list[i].name) == 0) + { + return &g_entry_list[i]; + } + } + + return NULL; +} + +void performance_list(void) +{ + size_t i; + + for (i = 0; i < nitems(g_entry_list); i++) + { + printf("%s\n", g_entry_list[i].name); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int main(int argc, FAR char *argv[]) +{ + const FAR struct performance_entry_s *item = NULL; + bool detail = false; + size_t count = 100; + size_t i; + int opt; + + while ((opt = getopt(argc, argv, "dc:hl")) != -1) + { + switch (opt) + { + case 'd': + detail = true; + break; + case 'c': + count = strtoul(optarg, NULL, 0); + break; + case 'h': + performance_help(); + return EXIT_SUCCESS; + case 'l': + performance_list(); + return EXIT_SUCCESS; + default: + performance_help(); + return EXIT_FAILURE; + } + } + + if (optind < argc) + { + item = find_entry(argv[optind]); + if (item == NULL) + { + printf("Can't find %s\n", argv[optind]); + return EXIT_FAILURE; + } + } + + printf("OS performance args: count:%d, detail:%s\n", count, + detail ? "true" : "false"); + + printf("==============================================================\n"); + printf("%-*s %10s %10s %10s\n", NAME_MAX, "Describe", "Max", "Min", "Avg"); + + if (item != NULL) + { + performance_run(item, count, detail); + return EXIT_SUCCESS; + } + + for (i = 0; i < nitems(g_entry_list); i++) + { + item = &g_entry_list[i]; + performance_run(item, count, detail); + } + + return EXIT_SUCCESS; +}