protoverse

A metaverse protocol
git clone git://jb55.com/protoverse
Log | Files | Refs | README | LICENSE

commit 3ed8d231a81ec6ef4bd3ad4d2d6e17e8ce036952
parent f2398d4032d3b2e27eff4a2d9aad00b1a8b1a3f8
Author: William Casarin <jb55@jb55.com>
Date:   Mon,  5 Jul 2021 08:52:50 -0700

bench

Diffstat:
M.gitignore | 1+
MMakefile | 11++++++++---
Asrc/bench.c | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/wasm.c | 153+++++++++++++++++++++++++++++++++++++------------------------------------------
Msrc/wasm.h | 51++++++++++++++++++++++++++++++++++++++++-----------
5 files changed, 205 insertions(+), 95 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,4 +1,5 @@ *.o +bench .buildcmd .build-result /protoverse diff --git a/Makefile b/Makefile @@ -1,5 +1,5 @@ -CFLAGS = -Wno-error=unused-function -O1 -g -std=gnu90 -Wall -Wextra -Werror \ +CFLAGS = -Wno-error=unused-function -O2 -g -std=gnu90 -Wall -Wextra -Werror \ -Wstrict-prototypes -Wold-style-definition -Wmissing-prototypes \ -Wmissing-declarations -Wdeclaration-after-statement @@ -17,7 +17,7 @@ OBJS = src/io.o \ WASMS = wasm/hello-c.wasm \ wasm/hello.wasm -all: protoverse libprotoverse.a +all: protoverse bench test libprotoverse.a wasm: $(WASMS) @@ -38,11 +38,16 @@ protoverse: src/protoverse.c $(OBJS) libprotoverse.a: $(OBJS) ar rcs $@ $^ +bench: src/bench.c $(OBJS) + @echo "ld $@" + @$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@ + clean: rm -f protoverse test $(OBJS) libprotoverse.a $(WASMS) test: src/test.c $(OBJS) - $(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@ + @echo "ld $@" + @$(CC) $(CFLAGS) $^ $(LDFLAGS) -o $@ check: test @./test diff --git a/src/bench.c b/src/bench.c @@ -0,0 +1,84 @@ +#include <time.h> +#include <stdlib.h> +#include <assert.h> +#include <sys/mman.h> + +#include "io.h" +#include "wasm.h" + +static int bench_wasm(unsigned char *wasm, unsigned long len, int times) +{ + struct wasm_parser p; + void *mem; + int ok, arena_size, i, ops = 0; + struct wasm_interp interp; + struct timespec t1, t2; + long nanos, ms; + + arena_size = len * 16; + memset(&p, 0, sizeof(p)); + mem = malloc(arena_size); + assert(mem); + + make_cursor(wasm, wasm + len, &p.cur); + make_cursor(mem, mem + arena_size, &p.mem); + + if (!parse_wasm(&p)) { + free(mem); + return 0; + } + + wasm_interp_init(&interp); + + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t1); + for (i = 0; i < times; i++) { + if (!interp_wasm_module(&interp, &p.module)) { + printf("bench: interp_wasm_module failed\n"); + break; + } + ops += interp.ops; + } + clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &t2); + + nanos = (t2.tv_sec - t1.tv_sec) * (long)1e9 + (t2.tv_nsec - t1.tv_nsec); + ms = nanos / 1e6; + printf("ns/run\t%ld\nms/run\t%f\nns\t%ld\nms\t%ld\nops\t%d\nns/op\t%ld\n", + nanos/times, (double)ms/(double)times, nanos, ms, ops, nanos/ops); + + wasm_interp_free(&interp); + + free(mem); + return ok; +} + +int main(int argc, char *argv[]) +{ + unsigned char *wasm_data; + const char *code_file; + size_t len; + int times; + + if (argc >= 2) + code_file = argv[1]; + else + code_file = "wasm/hello.wasm"; + + if (argc >= 3) + times = atoi(argv[2]); + else + times = 10000; + + if (!map_file(code_file, &wasm_data, &len)) { + perror("mmap"); + return 1; + } + fprintf(stderr, "executing %s %d times\n", code_file, times); + if (!bench_wasm(wasm_data, len, times)) { + return 2; + } + + munmap(wasm_data, len); + + return 0; +} + diff --git a/src/wasm.c b/src/wasm.c @@ -1,6 +1,7 @@ #include "wasm.h" #include "parser.h" +#include "debug.h" #include <stdarg.h> #include <stdio.h> @@ -31,26 +32,6 @@ struct val { }; }; -struct wasm_parser { - struct module module; - struct cursor cur; - struct cursor mem; - struct parse_error *errors; -}; - -struct wasm_interp { - struct module *module; - - struct cursor cur; /* code */ - struct cursor code_stack; /* struct cursor */ - struct cursor stack; /* struct val */ - struct cursor mem; /* u8/mixed */ - struct cursor locals; /* struct val */ - struct cursor locals_offsets; /* int */ - - struct parser_error *errors; -}; - #ifdef DEBUG static void log_dbg_(const char *fmt, ...) { @@ -108,12 +89,12 @@ static const char *valtype_name(enum valtype valtype) static void print_val(struct val *val) { switch (val->type) { - case i32: printf("%d", val->i32); break; - case i64: printf("%ld", val->i64); break; - case f32: printf("%f", val->f32); break; - case f64: printf("%f", val->f64); break; + case i32: debug("%d", val->i32); break; + case i64: debug("%ld", val->i64); break; + case f32: debug("%f", val->f32); break; + case f64: debug("%f", val->f64); break; } - printf(":%s\n", valtype_name(val->type)); + debug(":%s\n", valtype_name(val->type)); } static inline int cursor_popdata(struct cursor *cur, unsigned char *dest, int len) @@ -153,7 +134,7 @@ static void print_stack(struct cursor *stack) for (i = 0; stack->p > stack->start; i++) { cursor_popval(stack, &val); - printf("[%d] ", i); + debug("[%d] ", i); print_val(&val); } @@ -275,21 +256,21 @@ static void print_functype(struct functype *ft) int i; for (i = 0; i < ft->params.num_valtypes; i++) { - printf("%s ", valtype_name(ft->params.valtypes[i])); + debug("%s ", valtype_name(ft->params.valtypes[i])); } - printf("-> "); + debug("-> "); for (i = 0; i < ft->result.num_valtypes; i++) { - printf("%s ", valtype_name(ft->result.valtypes[i])); + debug("%s ", valtype_name(ft->result.valtypes[i])); } - printf("\n"); + debug("\n"); } static void print_type_section(struct typesec *typesec) { int i; - printf("%d functypes:\n", typesec->num_functypes); + debug("%d functypes:\n", typesec->num_functypes); for (i = 0; i < typesec->num_functypes; i++) { - printf(" "); + debug(" "); print_functype(&typesec->functypes[i]); } } @@ -307,7 +288,8 @@ static void print_func_section(struct funcsec *funcsec) } */ -static const char *exportdesc_name(enum exportdesc desc) +__attribute__((unused)) +static const char *exportdesc_name(enum exportdesc desc) { switch (desc) { case export_func: return "function"; @@ -321,15 +303,16 @@ static const char *exportdesc_name(enum exportdesc desc) static void print_import(struct import *import) { - printf("%s %s\n", import->module_name, import->name); + (void)import; + debug("%s %s\n", import->module_name, import->name); } static void print_import_section(struct importsec *importsec) { int i; - printf("%d imports:\n", importsec->num_imports); + debug("%d imports:\n", importsec->num_imports); for (i = 0; i < importsec->num_imports; i++) { - printf(" "); + debug(" "); print_import(&importsec->imports[i]); } } @@ -337,10 +320,10 @@ static void print_import_section(struct importsec *importsec) static void print_export_section(struct exportsec *exportsec) { int i; - printf("%d exports:\n", exportsec->num_exports); + debug("%d exports:\n", exportsec->num_exports); for (i = 0; i < exportsec->num_exports; i++) { - printf(" "); - printf("%s %s %d\n", exportdesc_name(exportsec->exports[i].desc), + debug(" "); + debug("%s %s %d\n", exportdesc_name(exportsec->exports[i].desc), exportsec->exports[i].name, exportsec->exports[i].index); } @@ -349,18 +332,18 @@ static void print_export_section(struct exportsec *exportsec) /* static void print_local(struct local *local) { - printf("%d %s\n", local->n, valtype_name(local->valtype)); + debug("%d %s\n", local->n, valtype_name(local->valtype)); } static void print_func(struct func *func) { int i; - printf("func locals (%d): \n", func->num_locals); + debug("func locals (%d): \n", func->num_locals); for (i = 0; i < func->num_locals; i++) { print_local(&func->locals[i]); } - printf("%d bytes of code\n", func->code_len); + debug("%d bytes of code\n", func->code_len); } static void print_code_section(struct codesec *codesec) @@ -1052,7 +1035,7 @@ static int parse_section(struct wasm_parser *p) return 1; } -static int parse_wasm(struct wasm_parser *p) +int parse_wasm(struct wasm_parser *p) { if (!consume_bytes(&p->cur, WASM_MAGIC, sizeof(WASM_MAGIC))) { note_error(p, "magic"); @@ -1074,14 +1057,14 @@ static int parse_wasm(struct wasm_parser *p) } } - printf("module parse success!\n\n"); + debug("module parse success!\n\n"); print_module(&p->module); return 1; fail: - printf("parse failure backtrace:\n"); + debug("parse failure backtrace:\n"); print_parse_backtrace(p); - printf("\npartially parsed module:\n"); + debug("\npartially parsed module:\n"); print_module(&p->module); return 0; } @@ -1172,7 +1155,7 @@ static int set_local(struct wasm_interp *interp, int ind, struct val *val) } if (ind < nlocals) { - printf("memsetting local %d\n", ind); + debug("memsetting local %d\n", ind); if (!(local = get_local(interp, ind))) { return 0; } @@ -1180,7 +1163,7 @@ static int set_local(struct wasm_interp *interp, int ind, struct val *val) return 1; } - printf("pushing local %d\n", ind); + debug("pushing local %d\n", ind); cursor_pushval(&interp->locals, val); assert(count_locals(interp) > 0); return 1; @@ -1383,7 +1366,8 @@ static int interp_call(struct wasm_interp *interp) static int interp_instr(struct wasm_interp *interp, unsigned char tag) { - printf("executing 0x%x\n", tag); + interp->ops++; + switch (tag) { case i_unreachable: return 1; case i_nop: return 1; @@ -1458,65 +1442,73 @@ static int cursor_slice(struct cursor *mem, struct cursor *slice, size_t size) return 1; } -static int interp_module(struct module *module) +void wasm_interp_init(struct wasm_interp *interp) { - int ok, func; - struct wasm_interp interp; static unsigned char *stack, *mem; - interp.module = module; + interp->ops = 0; stack = malloc(STACK_SPACE); mem = malloc(MEM_SPACE); + make_cursor(stack, stack + STACK_SPACE, &interp->stack); + make_cursor(mem, mem + MEM_SPACE, &interp->mem); +} + +void wasm_interp_free(struct wasm_interp *interp) +{ + free(interp->stack.start); + free(interp->mem.start); +} + +int interp_wasm_module(struct wasm_interp *interp, struct module *module) +{ + int ok, func; + + interp->module = module; + interp->ops = 0; + if (module->code_section.num_funcs == 0) { - interp_error(&interp, "empty module"); + interp_error(interp, "empty module"); return 0; } - make_cursor(stack, stack + STACK_SPACE, &interp.stack); - make_cursor(mem, mem + MEM_SPACE, &interp.mem); - if (!cursor_slice(&interp.mem, &interp.locals, sizeof(struct val) * NUM_LOCALS)) { - interp_error(&interp, "not enough memory for locals"); - return 0; - } + // reset cursors + interp->stack.p = interp->stack.start; + interp->mem.p = interp->mem.start; - if (!(cursor_slice(&interp.mem, &interp.locals_offsets, sizeof(int) * 255))) { - interp_error(&interp, "not enough memory for local offsets"); - return 0; - } + ok = + cursor_slice(&interp->mem, &interp->locals, sizeof(struct val) * NUM_LOCALS) && + cursor_slice(&interp->mem, &interp->locals_offsets, sizeof(int) * 255) && + cursor_slice(&interp->mem, &interp->code_stack, sizeof(struct cursor) * 255); - if (!(cursor_slice(&interp.mem, &interp.code_stack, sizeof(struct cursor) * 255))) { - interp_error(&interp, "not enough memory for code stack"); - return 0; - } + assert(ok); func = find_function(module, "start"); if (func == -1) { - interp_error(&interp, "no start function found"); + interp_error(interp, "no start function found"); return 0; } - if (!prepare_call(&interp, func)) { - interp_error(&interp, "preparing start function"); + if (!prepare_call(interp, func)) { + interp_error(interp, "preparing start function"); return 0; } - if (interp_code(&interp)) { - printf("interp success!!\n"); + if (interp_code(interp)) { + debug("interp success!!\n"); } - printf("stack:\n"); - print_stack(&interp.stack); + debug("ops: %ld\nstack:\n", interp->ops); + print_stack(&interp->stack); - free(stack); - free(mem); return ok; } int run_wasm(unsigned char *wasm, unsigned long len) { struct wasm_parser p; + struct wasm_interp interp; void *mem; int ok, arena_size; @@ -1534,10 +1526,9 @@ int run_wasm(unsigned char *wasm, unsigned long len) return 0; } - if (!interp_module(&p.module)) { - free(mem); - return 0; - } + wasm_interp_init(&interp); + ok = interp_wasm_module(&interp, &p.module); + wasm_interp_free(&interp); free(mem); return ok; diff --git a/src/wasm.h b/src/wasm.h @@ -1,14 +1,17 @@ -#ifndef WASM -#define WASM +#ifndef PROTOVERSE_WASM_H +#define PROTOVERSE_WASM_H static const unsigned char WASM_MAGIC[] = {0,'a','s','m'}; + #define WASM_VERSION 0x01 #define MAX_U32_LEB128_BYTES 5 #define MAX_U64_LEB128_BYTES 10 #define FUNC_TYPE_TAG 0x60 +#include "cursor.h" + enum valtype { i32 = 0x7F, i64 = 0x7E, @@ -142,14 +145,6 @@ struct exportsec { int num_exports; }; -struct module { - struct typesec type_section; - struct funcsec func_section; - struct importsec import_section; - struct exportsec export_section; - struct codesec code_section; -}; - struct section { enum section_tag tag; }; @@ -287,6 +282,40 @@ struct instr { }; }; +struct module { + struct typesec type_section; + struct funcsec func_section; + struct importsec import_section; + struct exportsec export_section; + struct codesec code_section; +}; + +struct wasm_interp { + struct module *module; + size_t ops; + + struct cursor cur; /* code */ + struct cursor code_stack; /* struct cursor */ + struct cursor stack; /* struct val */ + struct cursor mem; /* u8/mixed */ + struct cursor locals; /* struct val */ + struct cursor locals_offsets; /* int */ + + struct parser_error *errors; +}; + +struct wasm_parser { + struct module module; + struct cursor cur; + struct cursor mem; + struct parse_error *errors; +}; + + int run_wasm(unsigned char *wasm, unsigned long len); +int parse_wasm(struct wasm_parser *p); +void wasm_interp_init(struct wasm_interp *interp); +void wasm_interp_free(struct wasm_interp *interp); +int interp_wasm_module(struct wasm_interp *interp, struct module *module); -#endif /* WASM */ +#endif /* PROTOVERSE_WASM_H */