Changeset 3bd74758 in mainline for uspace/app/perf/perf.c


Ignore:
Timestamp:
2018-12-28T09:32:11Z (5 years ago)
Author:
Vojtech Horky <vojtech.horky@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
c7de81b
Parents:
8ee106b
Message:

perf: huge refactoring

The overall aim of this refactoring was to remove duplicate code in all
benchmarks that was responsible (a) for computing proper workload size
and (b) for computing final statistics.

After the refactoring, the actual benchmark code is quite short and
takes care of the actual work only.

The harness code was factored out into perf.c that is now responsible
for computing the workload size and then runs the actual benchmark.

As an extra feature, we pass stopwatch_t into the benchmark code that is
only responsible for starting/stopping. Duration is then queried outside
the benchmark code.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • uspace/app/perf/perf.c

    r8ee106b r3bd74758  
    11/*
    22 * Copyright (c) 2018 Jiri Svoboda
     3 * Copyright (c) 2018 Vojtech Horky
    34 * All rights reserved.
    45 *
     
    3839#include <stdlib.h>
    3940#include <str.h>
     41#include <time.h>
     42#include <errno.h>
     43#include <perf.h>
    4044#include "perf.h"
     45
     46#define MIN_DURATION_SECS 10
     47#define NUM_SAMPLES 10
     48#define MAX_ERROR_STR_LENGTH 1024
    4149
    4250benchmark_t benchmarks[] = {
     
    4553#include "malloc/malloc1.def"
    4654#include "malloc/malloc2.def"
    47         { NULL, NULL, NULL }
     55        { NULL, NULL, NULL, NULL, NULL }
    4856};
    4957
     58static void short_report(stopwatch_t *stopwatch, int run_index,
     59    benchmark_t *bench, size_t workload_size)
     60{
     61        usec_t duration_usec = NSEC2USEC(stopwatch_get_nanos(stopwatch));
     62
     63        printf("Completed %zu operations in %llu us",
     64            workload_size, duration_usec);
     65        if (duration_usec > 0) {
     66                double cycles = workload_size * 1000 * 1000 / duration_usec;
     67                printf(", %.0f cycles/s.\n", cycles);
     68        } else {
     69                printf(".\n");
     70        }
     71}
     72
     73static void summary_stats(stopwatch_t *stopwatch, size_t stopwatch_count,
     74    benchmark_t *bench, size_t workload_size)
     75{
     76        double sum = 0.0;
     77        double sum_square = 0.0;
     78
     79        for (size_t i = 0; i < stopwatch_count; i++) {
     80                double nanos = stopwatch_get_nanos(&stopwatch[i]);
     81                double thruput = (double) workload_size / (nanos / 1000000000.0l);
     82                sum += thruput;
     83                sum_square += thruput * thruput;
     84        }
     85
     86        double avg = sum / stopwatch_count;
     87
     88        double sd_numer = sum_square + stopwatch_count * avg * avg - 2 * sum * avg;
     89        double sd_square = sd_numer / ((double) stopwatch_count - 1);
     90
     91        printf("Average: %.0f cycles/s Std.dev^2: %.0f cycles/s Samples: %zu\n",
     92            avg, sd_square, stopwatch_count);
     93}
     94
    5095static bool run_benchmark(benchmark_t *bench)
    5196{
    52         /* Execute the benchmarl */
    53         const char *ret = bench->entry();
    54 
    55         if (ret == NULL) {
    56                 printf("\nBenchmark completed\n");
    57                 return true;
    58         }
    59 
    60         printf("\n%s\n", ret);
    61         return false;
     97        printf("Warm up and determine workload size...\n");
     98
     99        char *error_msg = malloc(MAX_ERROR_STR_LENGTH + 1);
     100        if (error_msg == NULL) {
     101                printf("Out of memory!\n");
     102                return false;
     103        }
     104        str_cpy(error_msg, MAX_ERROR_STR_LENGTH, "");
     105
     106        bool ret = true;
     107
     108        if (bench->setup != NULL) {
     109                ret = bench->setup(error_msg, MAX_ERROR_STR_LENGTH);
     110                if (!ret) {
     111                        goto leave_error;
     112                }
     113        }
     114
     115        size_t workload_size = 1;
     116
     117        while (true) {
     118                stopwatch_t stopwatch = STOPWATCH_INITIALIZE_STATIC;
     119
     120                bool ok = bench->entry(&stopwatch, workload_size,
     121                    error_msg, MAX_ERROR_STR_LENGTH);
     122                if (!ok) {
     123                        goto leave_error;
     124                }
     125                short_report(&stopwatch, -1, bench, workload_size);
     126
     127                nsec_t duration = stopwatch_get_nanos(&stopwatch);
     128                if (duration > SEC2NSEC(MIN_DURATION_SECS)) {
     129                        break;
     130                }
     131                workload_size *= 2;
     132        }
     133
     134        printf("Workload size set to %zu, measuring %d samples.\n", workload_size, NUM_SAMPLES);
     135
     136        stopwatch_t *stopwatch = calloc(NUM_SAMPLES, sizeof(stopwatch_t));
     137        if (stopwatch == NULL) {
     138                snprintf(error_msg, MAX_ERROR_STR_LENGTH, "failed allocating memory");
     139                goto leave_error;
     140        }
     141        for (int i = 0; i < NUM_SAMPLES; i++) {
     142                stopwatch_init(&stopwatch[i]);
     143
     144                bool ok = bench->entry(&stopwatch[i], workload_size,
     145                    error_msg, MAX_ERROR_STR_LENGTH);
     146                if (!ok) {
     147                        free(stopwatch);
     148                        goto leave_error;
     149                }
     150                short_report(&stopwatch[i], i, bench, workload_size);
     151        }
     152
     153        summary_stats(stopwatch, NUM_SAMPLES, bench, workload_size);
     154        printf("\nBenchmark completed\n");
     155
     156        free(stopwatch);
     157
     158        goto leave;
     159
     160leave_error:
     161        printf("Error: %s\n", error_msg);
     162        ret = false;
     163
     164leave:
     165        if (bench->teardown != NULL) {
     166                bool ok = bench->teardown(error_msg, MAX_ERROR_STR_LENGTH);
     167                if (!ok) {
     168                        printf("Error: %s\n", error_msg);
     169                        ret = false;
     170                }
     171        }
     172
     173        free(error_msg);
     174
     175        return ret;
    62176}
    63177
Note: See TracChangeset for help on using the changeset viewer.