From: Nick Pelly Date: Thu, 26 Aug 2010 18:21:29 +0000 (-0700) Subject: Add a couple of performance debug utilities. X-Git-Tag: android-x86-4.4-r1~278 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=6ca26ef8a78cc46dd8c779dcd7c611a247dd22c7;p=android-x86%2Fsystem-extras.git Add a couple of performance debug utilities. micro_bench provides very simple tests for o sleep accuracy o cpu o memcpy o memset sane_schedstat is a front-end for /proc/schedstat to diff and format the counters. Change-Id: I6e178fe37fcfc9bf0a83ec17852e31146a91e7a4 Signed-off-by: Nick Pelly --- diff --git a/micro_bench/Android.mk b/micro_bench/Android.mk new file mode 100644 index 00000000..0e819c37 --- /dev/null +++ b/micro_bench/Android.mk @@ -0,0 +1,10 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := micro_bench.c + +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := debug +LOCAL_MODULE := micro_bench + +include $(BUILD_EXECUTABLE) diff --git a/micro_bench/micro_bench.c b/micro_bench/micro_bench.c new file mode 100644 index 00000000..c67a52fa --- /dev/null +++ b/micro_bench/micro_bench.c @@ -0,0 +1,188 @@ +/* +** Copyright 2010 The Android Open Source Project +** +** Licensed 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. +*/ + +/* + * Some quick and dirty micro-benchmarks + */ + +#include +#include +#include +#include +#include +#include + +/* tv2 -= tv1 */ +static void tv_sub(struct timeval *tv2, struct timeval *tv1) { + tv2->tv_sec -= tv1->tv_sec; + tv2->tv_usec -= tv1->tv_usec; + while (tv2->tv_usec < 0) { + tv2->tv_usec += 1000000; + tv2->tv_sec -= 1; + } +} + +static int do_sleep(int delay) { + struct timeval tv1; + struct timeval tv2; + + while (1) { + gettimeofday(&tv1, NULL); + sleep(delay); + gettimeofday(&tv2, NULL); + + tv_sub(&tv2, &tv1); + + printf("sleep(%d) took %ld.%06ld seconds\n", delay, tv2.tv_sec, tv2.tv_usec); + } + + return 0; +} + +int cpu_foo; + +static int do_cpu(int a) { + struct timeval tv1; + struct timeval tv2; + + while (1) { + gettimeofday(&tv1, NULL); + for (cpu_foo = 0; cpu_foo < 100000000; cpu_foo++); + gettimeofday(&tv2, NULL); + + tv_sub(&tv2, &tv1); + + printf("cpu took %ld.%06ld seconds\n", tv2.tv_sec, tv2.tv_usec); + } + return 0; +} + +static double mb_sec(unsigned long bytes, struct timeval *delta) { + unsigned long us = delta->tv_sec * 1000000 + delta->tv_usec; + return (double)bytes * 1000000.0 / 1048576.0 / (double)us; +} + +static int do_memset(int sz) { + struct timeval tv1; + struct timeval tv2; + int i; + + uint8_t *b = malloc(sz); + if (!b) return -1; + int c = 1000000000/sz; + + while (1) { + gettimeofday(&tv1, NULL); + for (i = 0; i < c; i++) + memset(b, 0, sz); + + gettimeofday(&tv2, NULL); + + tv_sub(&tv2, &tv1); + + printf("memset %dx%d bytes took %ld.%06ld seconds (%f MB/s)\n", c, sz, tv2.tv_sec, tv2.tv_usec, mb_sec(c*sz, &tv2)); + } + return 0; +} + +static int do_memcpy(int sz) { + struct timeval tv1; + struct timeval tv2; + int i; + + uint8_t *a = malloc(sz); + if (!a) return -1; + uint8_t *b = malloc(sz); + if (!b) return -1; + int c = 1000000000/sz; + + while (1) { + gettimeofday(&tv1, NULL); + for (i = 0; i < c; i++) + memcpy(b, a, sz); + + gettimeofday(&tv2, NULL); + + tv_sub(&tv2, &tv1); + + printf("memcpy %dx%d bytes took %ld.%06ld seconds (%f MB/s)\n", c, sz, tv2.tv_sec, tv2.tv_usec, mb_sec(c*sz, &tv2)); + } + return 0; +} + +int foo; + +static int do_memread(int sz) { + struct timeval tv1; + struct timeval tv2; + int i, j; + + int *b = malloc(sz); + if (!b) return -1; + int c = 1000000000/sz; + + while (1) { + gettimeofday(&tv1, NULL); + for (i = 0; i < c; i++) + for (j = 0; j < sz/4; j++) + foo = b[j]; + + gettimeofday(&tv2, NULL); + + tv_sub(&tv2, &tv1); + + printf("read %dx%d bytes took %ld.%06ld seconds (%f MB/s)\n", c, sz, tv2.tv_sec, tv2.tv_usec, mb_sec(c*sz, &tv2)); + } + return 0; +} + +struct { + char *name; + int (*ptr)(int); +} function_table[] = { + {"sleep", do_sleep}, + {"cpu", do_cpu}, + {"memset", do_memset}, + {"memcpy", do_memcpy}, + {"memread", do_memread}, + {NULL, NULL}, +}; + +static void usage() { + int i; + + printf("Usage:\n"); + for (i = 0; function_table[i].name; i++) { + printf("\tmicro_bench %s ARG\n", function_table[i].name); + } +} + +int main(int argc, char **argv) { + int i; + + if (argc != 3) { + usage(); + return -1; + } + for (i = 0; function_table[i].name; i++) { + if (!strcmp(argv[1], function_table[i].name)) { + printf("%s\n", function_table[i].name); + return (*function_table[i].ptr)(atoi(argv[2])); + } + } + usage(); + return -1; +} diff --git a/sane_schedstat/Android.mk b/sane_schedstat/Android.mk new file mode 100644 index 00000000..5843c43f --- /dev/null +++ b/sane_schedstat/Android.mk @@ -0,0 +1,11 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := sane_schedstat.c + +LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) +LOCAL_MODULE_TAGS := debug +LOCAL_MODULE := sane_schedstat + +include $(BUILD_EXECUTABLE) diff --git a/sane_schedstat/sane_schedstat.c b/sane_schedstat/sane_schedstat.c new file mode 100644 index 00000000..29d10633 --- /dev/null +++ b/sane_schedstat/sane_schedstat.c @@ -0,0 +1,157 @@ +/* +** Copyright 2010 The Android Open Source Project +** +** Licensed 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. +*/ + + +/* Opens /proc/sched_stat and diff's the counters. + Currently support version 15, modify parse() to support other + versions +*/ + +#include +#include +#include +#include +#include +#include +#include + +#define MAX_CPU 2 + +struct cpu_stat { + /* sched_yield() stats */ + unsigned int yld_count; /* sched_yield() called */ + + /* schedule() stats */ + unsigned int sched_switch; /* switched to expired queue and reused it */ + unsigned int sched_count; /* schedule() called */ + unsigned int sched_goidle; /* schedule() left the cpu idle */ + + /* try_to_wake_up() stats */ + unsigned int ttwu_count; /* try_to_wake_up() called */ + /* try_to_wake_up() called and found the process being awakened last ran on + * the waking cpu */ + unsigned int ttwu_local; + + /* latency stats */ + unsigned long long cpu_time; /* time spent running by tasks (ms) */ + unsigned long long run_delay; /* time spent waiting to run by tasks (ms) */ + unsigned long pcount; /* number of tasks (not necessarily unique) given */ +}; + +struct cpu_stat cpu_prev[MAX_CPU]; +struct cpu_stat cpu_delta[MAX_CPU]; +struct cpu_stat tmp; + +static const char *next_line(const char *b) { + while (1) { + switch (*b) { + case '\n': + return b + 1; + case '\0': + return NULL; + } + b++; + } +} +static int print() { + int i; + + printf("CPU yield() schedule() switch idle ttwu() local cpu_time wait_time timeslices\n"); + for (i=0; i