Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@ endif

CFLAGS = $(CFLAGS_LANG) -g -Werror=return-type -fsanitize=undefined,alignment -fno-sanitize-recover=all $(CFLAGS_PLATFORM)
CFLAGS_TEST = -DSP_IMPLEMENTATION -DSP_TEST_IMPLEMENTATION -I. -Itest/tools -Itest
CFLAGS_BENCH = $(CFLAGS_LANG) -g -Werror=return-type -O2 -DSP_IMPLEMENTATION -DUBENCH_ENABLE_PERF_COUNTERS -I. -Itest/bench -Itest/tools

TESTS = amalg app array asset etc cv env format fmon fs glob ht io math process ps rb str thread time mem prompt leak
BENCHES = glob heap
EXAMPLES = app array format hash_table io zero_copy ls palette prompt prompt_fancy signal wc
TRIPLES = \
x86_64-linux-none x86_64-linux-gnu x86_64-linux-musl \
Expand All @@ -54,23 +56,29 @@ TRIPLES = \

TEST_DIR = $(BUILD_DIR)/test
EXAMPLE_DIR = $(BUILD_DIR)/example
BENCH_DIR = $(BUILD_DIR)/bench
TEST_BINARIES = $(addsuffix $(EXE),$(addprefix $(TEST_DIR)/,$(TESTS)))
EXAMPLE_BINARIES = $(addsuffix $(EXE),$(addprefix $(EXAMPLE_DIR)/,$(EXAMPLES)))
BENCH_BINARIES = $(addsuffix $(EXE),$(addprefix $(BENCH_DIR)/,$(BENCHES)))

SP_HEADERS = sp.h $(wildcard sp/*.h)
TEST_SOURCES = $(wildcard test/*/*.c) $(wildcard test/*/*.h) $(wildcard test/*/*/*.c) $(wildcard test/*/*/*.h)

.PHONY: all clean tests examples smoke big c cpp gcc tcc $(TRIPLES)
.PHONY: all clean tests examples bench smoke big c cpp gcc tcc $(TRIPLES)
all: examples tests
tests: $(TEST_BINARIES)
examples: $(EXAMPLE_BINARIES)
bench: $(BENCH_BINARIES)

$(EXAMPLE_DIR)/%$(EXE): example/%.c $(SP_HEADERS) | $(EXAMPLE_DIR)
$(CC) $(CFLAGS) -I. -o $@ $<

$(TEST_DIR)/%$(EXE): test/%.c $(SP_HEADERS) $(TEST_SOURCES) | $(TEST_DIR)
$(CC) $(CFLAGS) $(CFLAGS_TEST) -o $@ $<

$(BENCH_DIR)/%$(EXE): test/bench/%.c $(SP_HEADERS) test/bench/ubench.h test/tools/table.h | $(BENCH_DIR)
$(CC) $(CFLAGS_BENCH) -o $@ $<

$(TRIPLES):
+$(MAKE) TRIPLE=$@ examples tests

Expand All @@ -83,7 +91,7 @@ wasm:
+$(MAKE) wasm32-wasi wasm32-freestanding
+$(MAKE) MODE=cpp wasm32-wasi wasm32-freestanding

$(BUILD_DIR) $(EXAMPLE_DIR) $(TEST_DIR):
$(BUILD_DIR) $(EXAMPLE_DIR) $(TEST_DIR) $(BENCH_DIR):
mkdir -p $@

clean:
Expand Down
512 changes: 441 additions & 71 deletions sp.h

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion sp/sp_prompt.h
Original file line number Diff line number Diff line change
Expand Up @@ -1014,7 +1014,7 @@ void sp_prompt_ctx_init(sp_prompt_ctx_t* ctx, sp_mem_t mem, u32 cols, u32 rows)
sp_da_init(ctx->mem, ctx->frames);

sp_mutex_init(&ctx->channel.lock, SP_MUTEX_PLAIN);
ctx->channel.arena = sp_mem_arena_new_ex(mem, SP_MEM_ARENA_BLOCK_SIZE, SP_MEM_ARENA_MODE_DEFAULT, SP_MEM_ALIGNMENT);
ctx->channel.arena = sp_mem_arena_new_ex(mem, 4096, SP_MEM_ARENA_MODE_DEFAULT, SP_MEM_ALIGNMENT);

// Write buffering is really important, because our rendering algorithm is extremely
// naive. It's not much more than this:
Expand Down
226 changes: 105 additions & 121 deletions test/bench/glob.c
Original file line number Diff line number Diff line change
@@ -1,144 +1,128 @@
#define SP_APP
#include "sp.h"
#include "ubench.h"

#include "sp/sp_glob.h"

#define BENCH_ITERATIONS 1000000
#define GLOB_BENCH_MAX_PATTERNS 16

typedef struct {
const c8* pattern;
const c8* path;
} bench_case_t;
bool match;
} glob_bench_expect_t;

typedef struct {
sp_str_t name;
f64 ns_per_op;
} bench_result_t;

// From original rust globset benchmarks
static bench_case_t bench_cases[] = {
{.pattern = "*.txt", .path = "some/a/bigger/path/to/the/crazy/needle.txt"},
{.pattern = "some/**/needle.txt", .path = "some/needle.txt"},
{.pattern = "some/**/needle.txt", .path = "some/a/bigger/path/to/the/crazy/needle.txt"},
};

static const c8* case_names[] = {
"ext", "short", "long",
};

// From original rust globset benchmarks
static const c8* many_short_patterns[] = {
".*.swp",
"tags",
"target",
"*.lock",
"tmp",
"*.csv",
"*.fst",
"*-got",
"*.csv.idx",
"words",
"98m*",
"dict",
"test",
"months",
};

static const c8* many_short_path = "98m-blah.csv.idx";

static f64 run_glob_bench(sp_glob_t* g, sp_str_t path) {
for (u32 i = 0; i < 1000; i++) {
sp_glob_match(g, path);
}

volatile bool result;
sp_tm_point_t start = sp_tm_now_point();
for (u32 i = 0; i < BENCH_ITERATIONS; i++) {
result = sp_glob_match(g, path);
const c8* patterns[GLOB_BENCH_MAX_PATTERNS];
const c8* path;
glob_bench_expect_t expect;
} glob_bench_t;

static void run_glob_bench(ubench_run_state_t* ubench_run_state, glob_bench_t bench) {
sp_mem_arena_marker_t scratch = sp_mem_begin_scratch();
sp_glob_t* glob = sp_glob_new(scratch.mem, bench.patterns[0]);
sp_str_t path = sp_str_view(bench.path);

SP_ASSERT(glob != SP_NULLPTR);
SP_ASSERT(sp_glob_match(glob, path) == bench.expect.match);

UBENCH_DO_BENCHMARK() {
UBENCH_LOOP {
bool matched = sp_glob_match(glob, path);
UBENCH_DO_NOT_OPTIMIZE(matched);
}
}
sp_tm_point_t end = sp_tm_now_point();
(void)result;

return (f64)sp_tm_point_diff(end, start) / (f64)BENCH_ITERATIONS;
sp_mem_end_scratch(scratch);
}

static f64 run_glob_set_bench(sp_glob_set_t* set, sp_str_t path) {
for (u32 i = 0; i < 1000; i++) {
sp_glob_set_match(set, path);
static void run_glob_set_bench(ubench_run_state_t* ubench_run_state, glob_bench_t bench) {
sp_mem_arena_marker_t scratch = sp_mem_begin_scratch();
sp_glob_set_t* set = sp_glob_set_new(scratch.mem);
sp_carr_for(bench.patterns, it) {
if (!bench.patterns[it]) break;
sp_glob_set_add(set, bench.patterns[it]);
}
sp_glob_set_build(set);
sp_str_t path = sp_str_view(bench.path);

SP_ASSERT(sp_glob_set_match(set, path) == bench.expect.match);

volatile bool result;
sp_tm_point_t start = sp_tm_now_point();
for (u32 i = 0; i < BENCH_ITERATIONS; i++) {
result = sp_glob_set_match(set, path);
UBENCH_DO_BENCHMARK() {
UBENCH_LOOP {
bool matched = sp_glob_set_match(set, path);
UBENCH_DO_NOT_OPTIMIZE(matched);
}
}
sp_tm_point_t end = sp_tm_now_point();
(void)result;

return (f64)sp_tm_point_diff(end, start) / (f64)BENCH_ITERATIONS;
sp_mem_end_scratch(scratch);
}

int main(int argc, char** argv) {
(void)argc;
(void)argv;

sp_mem_arena_t* arena = sp_mem_arena_new_ex(sp_mem_os_new(), 4 * 1024 * 1024, SP_MEM_ARENA_MODE_DEFAULT, SP_MEM_ALIGNMENT);
sp_mem_t allocator = sp_mem_arena_as_allocator(arena);
(void)allocator;

u32 num_cases = sizeof(bench_cases) / sizeof(bench_cases[0]);
sp_da(bench_result_t) results = sp_da_new(allocator, bench_result_t);

// Pre-compile all globs
sp_da(sp_glob_t*) globs = sp_da_new(allocator, sp_glob_t*);
sp_da(sp_glob_set_t*) globsets = sp_da_new(allocator, sp_glob_set_t*);
sp_carr_for(bench_cases, i) {
sp_glob_t* g = sp_glob_new(allocator, bench_cases[i].pattern);
SP_ASSERT(g != SP_NULLPTR);
sp_da_push(globs, g);

sp_glob_set_t* set = sp_glob_set_new(allocator);
sp_glob_set_add(set, bench_cases[i].pattern);
sp_glob_set_build(set);
sp_da_push(globsets, set);
}
UBENCH_EX(glob, ext) {
run_glob_bench(ubench_run_state, (glob_bench_t) {
.patterns = { "*.txt" },
.path = "some/a/bigger/path/to/the/crazy/needle.txt",
.expect = { .match = true },
});
}

// Pre-compile many_short globset
sp_glob_set_t* many_short_set = sp_glob_set_new(allocator);
sp_carr_for(many_short_patterns, i) {
sp_glob_set_add(many_short_set, many_short_patterns[i]);
}
sp_glob_set_build(many_short_set);

// Single glob benchmarks
for (u32 i = 0; i < num_cases; i++) {
sp_str_t path = sp_str_view(bench_cases[i].path);
SP_ASSERT(sp_glob_match(globs[i], path));
f64 ns = run_glob_bench(globs[i], path);
sp_str_t name = sp_fmt(sp_mem_get_scratch(), "{}_glob", sp_fmt_cstr(case_names[i])).value;
sp_da_push(results, ((bench_result_t){.name = name, .ns_per_op = ns}));
}
UBENCH_EX(glob, short) {
run_glob_bench(ubench_run_state, (glob_bench_t) {
.patterns = { "some/**/needle.txt" },
.path = "some/needle.txt",
.expect = { .match = true },
});
}

// GlobSet single pattern benchmarks
for (u32 i = 0; i < num_cases; i++) {
sp_str_t path = sp_str_view(bench_cases[i].path);
SP_ASSERT(sp_glob_set_match(globsets[i], path));
f64 ns = run_glob_set_bench(globsets[i], path);
sp_str_t name = sp_fmt(sp_mem_get_scratch(), "{}_globset", sp_fmt_cstr(case_names[i])).value;
sp_da_push(results, ((bench_result_t){.name = name, .ns_per_op = ns}));
}
UBENCH_EX(glob, long) {
run_glob_bench(ubench_run_state, (glob_bench_t) {
.patterns = { "some/**/needle.txt" },
.path = "some/a/bigger/path/to/the/crazy/needle.txt",
.expect = { .match = true },
});
}

// many_short benchmark (14 patterns, 2 matches expected)
{
sp_str_t path = sp_str_view(many_short_path);
f64 ns = run_glob_set_bench(many_short_set, path);
sp_da_push(results, ((bench_result_t){.name = sp_str_lit("many_short_globset"), .ns_per_op = ns}));
}
UBENCH_EX(globset, ext) {
run_glob_set_bench(ubench_run_state, (glob_bench_t) {
.patterns = { "*.txt" },
.path = "some/a/bigger/path/to/the/crazy/needle.txt",
.expect = { .match = true },
});
}

// Print space-separated pairs
sp_da_for(results, i) {
sp_log("{} {}", sp_fmt_str(results[i].name), sp_fmt_float(results[i].ns_per_op));
}
UBENCH_EX(globset, short) {
run_glob_set_bench(ubench_run_state, (glob_bench_t) {
.patterns = { "some/**/needle.txt" },
.path = "some/needle.txt",
.expect = { .match = true },
});
}

UBENCH_EX(globset, long) {
run_glob_set_bench(ubench_run_state, (glob_bench_t) {
.patterns = { "some/**/needle.txt" },
.path = "some/a/bigger/path/to/the/crazy/needle.txt",
.expect = { .match = true },
});
}

return 0;
UBENCH_EX(globset, many_short) {
run_glob_set_bench(ubench_run_state, (glob_bench_t) {
.patterns = {
".*.swp",
"tags",
"target",
"*.lock",
"tmp",
"*.csv",
"*.fst",
"*-got",
"*.csv.idx",
"words",
"98m*",
"dict",
"test",
"months",
},
.path = "98m-blah.csv.idx",
.expect = { .match = true },
});
}

UBENCH_MAIN()
Loading
Loading