commit 3ed8d231a81ec6ef4bd3ad4d2d6e17e8ce036952
parent f2398d4032d3b2e27eff4a2d9aad00b1a8b1a3f8
Author: William Casarin <jb55@jb55.com>
Date: Mon, 5 Jul 2021 08:52:50 -0700
bench
Diffstat:
M | .gitignore | | | 1 | + |
M | Makefile | | | 11 | ++++++++--- |
A | src/bench.c | | | 84 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
M | src/wasm.c | | | 153 | +++++++++++++++++++++++++++++++++++++------------------------------------------ |
M | src/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 */