Compare commits
3 Commits
b7737fba39
...
5d109f5f06
Author | SHA1 | Date | |
---|---|---|---|
5d109f5f06 | |||
e1b5725e74 | |||
15a6195bf0 |
@ -19,3 +19,4 @@ endfunction()
|
|||||||
add_subdirectory(lib)
|
add_subdirectory(lib)
|
||||||
add_subdirectory(tests)
|
add_subdirectory(tests)
|
||||||
add_subdirectory(demo)
|
add_subdirectory(demo)
|
||||||
|
add_subdirectory(benchmarks)
|
||||||
|
20
benchmarks/CMakeLists.txt
Normal file
20
benchmarks/CMakeLists.txt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
add_library(benchmarking benchmarking.c)
|
||||||
|
set_default_target_options(benchmarking)
|
||||||
|
target_include_directories(benchmarking PUBLIC include)
|
||||||
|
|
||||||
|
function(add_benchmark_suite source)
|
||||||
|
string(REGEX REPLACE ".c$" "" name ${source})
|
||||||
|
add_executable(${name} ${source})
|
||||||
|
set_default_target_options(${name})
|
||||||
|
target_link_libraries(${name} PRIVATE lib benchmarking)
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
function(add_benchmark_suites)
|
||||||
|
foreach(source ${ARGN})
|
||||||
|
add_benchmark_suite(${source})
|
||||||
|
endforeach()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
add_benchmark_suites(
|
||||||
|
matching_benchmarks.c
|
||||||
|
)
|
85
benchmarks/benchmarking.c
Normal file
85
benchmarks/benchmarking.c
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Camden Dixie O'Brien
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "benchmarking.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define SWAP(x, y) \
|
||||||
|
do { \
|
||||||
|
const double tmp = x; \
|
||||||
|
x = y; \
|
||||||
|
y = tmp; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
clock_t benchmark_start, benchmark_end;
|
||||||
|
|
||||||
|
static void sort(double *xs, int n)
|
||||||
|
{
|
||||||
|
if (n <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const double pivot = xs[(n - 1) / 2];
|
||||||
|
int lt = 0;
|
||||||
|
int eq = 0;
|
||||||
|
int gt = n - 1;
|
||||||
|
while (eq <= gt) {
|
||||||
|
if (xs[eq] < pivot) {
|
||||||
|
SWAP(xs[eq], xs[lt]);
|
||||||
|
++lt;
|
||||||
|
++eq;
|
||||||
|
} else if (xs[eq] > pivot) {
|
||||||
|
SWAP(xs[eq], xs[gt]);
|
||||||
|
--gt;
|
||||||
|
} else {
|
||||||
|
++eq;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort(xs, lt);
|
||||||
|
sort(xs + gt + 1, n - (gt + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
void benchmark_summarise(double *res, int reps, benchmark_summary_t *out)
|
||||||
|
{
|
||||||
|
assert(reps > 0);
|
||||||
|
|
||||||
|
sort(res, reps);
|
||||||
|
const double median = res[reps / 2];
|
||||||
|
|
||||||
|
double sum = 0;
|
||||||
|
for (int i = 0; i < reps; ++i)
|
||||||
|
sum += res[i];
|
||||||
|
const double mean = sum / reps;
|
||||||
|
|
||||||
|
double diff_sum = 0;
|
||||||
|
for (int i = 0; i < reps; ++i)
|
||||||
|
diff_sum += pow(res[i] - mean, 2);
|
||||||
|
const double variance = diff_sum / (reps - 1);
|
||||||
|
|
||||||
|
out->reps = reps;
|
||||||
|
out->total = sum;
|
||||||
|
out->median = median;
|
||||||
|
out->mean = mean;
|
||||||
|
out->min = res[0];
|
||||||
|
out->max = res[reps - 1];
|
||||||
|
out->stddev = sqrt(variance);
|
||||||
|
}
|
||||||
|
|
||||||
|
void benchmark_print_header(void)
|
||||||
|
{
|
||||||
|
printf(
|
||||||
|
"%-12s %13s %13s %13s %13s %12s\n", "benchmark", "median (µs)",
|
||||||
|
"mean (µs)", "min (µs)", "max (µs)", "stddev");
|
||||||
|
}
|
||||||
|
|
||||||
|
void benchmark_print(const char *name, const benchmark_summary_t *s)
|
||||||
|
{
|
||||||
|
printf(
|
||||||
|
"%-12s %12.2f %12.2f %12.2f %12.2f %12.2f\n", name, s->median,
|
||||||
|
s->mean, s->min, s->max, s->stddev);
|
||||||
|
}
|
50
benchmarks/include/benchmarking.h
Normal file
50
benchmarks/include/benchmarking.h
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Camden Dixie O'Brien
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BENCHMARKING_H
|
||||||
|
#define BENCHMARKING_H
|
||||||
|
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
int reps;
|
||||||
|
double total, median, mean, min, max, stddev;
|
||||||
|
} benchmark_summary_t;
|
||||||
|
|
||||||
|
#define CLOCK_MICROS(c) (1000000 * (double)c / CLOCKS_PER_SEC)
|
||||||
|
|
||||||
|
#define BENCHMARKING_BEGIN() benchmark_print_header()
|
||||||
|
#define BENCHMARKING_END() 0
|
||||||
|
|
||||||
|
#define START_CLOCK() \
|
||||||
|
do { \
|
||||||
|
benchmark_start = clock(); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define STOP_CLOCK() \
|
||||||
|
do { \
|
||||||
|
benchmark_end = clock(); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define RUN_BENCHMARK(reps, name, fn, ...) \
|
||||||
|
do { \
|
||||||
|
double res[reps]; \
|
||||||
|
for (int i = 0; i < reps; ++i) { \
|
||||||
|
fn(__VA_ARGS__); \
|
||||||
|
res[i] = CLOCK_MICROS(benchmark_end) \
|
||||||
|
- CLOCK_MICROS(benchmark_start); \
|
||||||
|
} \
|
||||||
|
benchmark_summary_t summary; \
|
||||||
|
benchmark_summarise(res, reps, &summary); \
|
||||||
|
benchmark_print(name, &summary); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
extern clock_t benchmark_start, benchmark_end;
|
||||||
|
|
||||||
|
void benchmark_summarise(double *res, int reps, benchmark_summary_t *out);
|
||||||
|
void benchmark_print_header(void);
|
||||||
|
void benchmark_print(const char *name, const benchmark_summary_t *summary);
|
||||||
|
|
||||||
|
#endif
|
52
benchmarks/matching_benchmarks.c
Normal file
52
benchmarks/matching_benchmarks.c
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Camden Dixie O'Brien
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "benchmarking.h"
|
||||||
|
#include "compile.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <sys/time.h>
|
||||||
|
|
||||||
|
#define LEN 100
|
||||||
|
#define RANGE_FIRST 'a'
|
||||||
|
#define RANGE_LAST 'z'
|
||||||
|
|
||||||
|
#define CLAMP_CHAR(x) (RANGE_FIRST + x % (RANGE_LAST - RANGE_FIRST + 1))
|
||||||
|
|
||||||
|
#define RUN_MATCHING_BENCHMARK(reps, name, regex) \
|
||||||
|
do { \
|
||||||
|
fsa_t fsa; \
|
||||||
|
compile(regex, strlen(regex), &fsa); \
|
||||||
|
RUN_BENCHMARK(reps, name, matching_benchmark, &fsa); \
|
||||||
|
fsa_free(&fsa); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
static void matching_benchmark(const fsa_t *fsa)
|
||||||
|
{
|
||||||
|
char s[LEN];
|
||||||
|
for (int j = 0; j < LEN; ++j)
|
||||||
|
s[j] = CLAMP_CHAR(rand());
|
||||||
|
|
||||||
|
START_CLOCK();
|
||||||
|
fsa_accepts(fsa, s, LEN);
|
||||||
|
STOP_CLOCK();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
struct timeval tv;
|
||||||
|
gettimeofday(&tv, NULL);
|
||||||
|
srand(tv.tv_usec);
|
||||||
|
|
||||||
|
BENCHMARKING_BEGIN();
|
||||||
|
|
||||||
|
RUN_MATCHING_BENCHMARK(10000, "foo or bar", ".*(foo|bar).*");
|
||||||
|
RUN_MATCHING_BENCHMARK(10000, "regex #1", ".*(abc!?)*|dd+.*");
|
||||||
|
RUN_MATCHING_BENCHMARK(10000, "regex #2", ".*(l|wh)?[aeiou]+.*");
|
||||||
|
|
||||||
|
return BENCHMARKING_END();
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
cd "$(git rev-parse --show-toplevel)"
|
cd "$(git rev-parse --show-toplevel)"
|
||||||
find . -not \( -path './.git' -prune \) -not \( -path './build' -prune \) \
|
find . -not \( -path './.git' -prune \) -not \( -path './build' -prune \) \
|
||||||
| entr -s 'clear && scripts/build.sh && scripts/test.sh'
|
| entr -cs 'scripts/build.sh && scripts/test.sh'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user