From 0858590216ba36cd5e4f69be84f643fd00a85e87 Mon Sep 17 00:00:00 2001 From: Konstantin Osipov <kostja@tarantool.org> Date: Mon, 2 Feb 2015 15:16:51 +0300 Subject: [PATCH] Add a fiber benchmark. Remove explicit fiber call stack from the cord. Fix backtrace() to work correclty with an optimized (-O2) build. --- src/fiber.cc | 15 ++++++++------- src/fiber.h | 14 +++++++------- src/util.cc | 17 ++++++++++++++--- test/unit/fiber.cc | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 61 insertions(+), 17 deletions(-) diff --git a/src/fiber.cc b/src/fiber.cc index d1d64f6d1e..c396d240e5 100644 --- a/src/fiber.cc +++ b/src/fiber.cc @@ -58,16 +58,16 @@ fiber_call(struct fiber *callee) struct fiber *caller = fiber(); struct cord *cord = cord(); - assert(cord->sp + 1 - cord->stack < FIBER_CALL_STACK); + assert(cord->call_stack_depth < FIBER_CALL_STACK); assert(caller); + cord->call_stack_depth++; cord->fiber = callee; - *cord->sp++ = caller; + callee->caller = caller; update_last_stack_frame(caller); callee->csw++; - coro_transfer(&caller->coro.ctx, &callee->coro.ctx); } @@ -82,8 +82,7 @@ fiber_start(struct fiber *callee, ...) bool fiber_checkstack() { - struct cord *cord = cord(); - if (cord->sp + 1 - cord->stack >= FIBER_CALL_STACK) + if (cord()->call_stack_depth + 1 >= FIBER_CALL_STACK) return true; return false; } @@ -192,8 +191,10 @@ void fiber_yield(void) { struct cord *cord = cord(); - struct fiber *callee = *(--cord->sp); struct fiber *caller = cord->fiber; + struct fiber *callee = caller->caller; + cord->call_stack_depth--; + caller->caller = &cord->sched; /** By convention, these triggers must not throw. */ if (! rlist_empty(&caller->on_yield)) @@ -413,6 +414,7 @@ fiber_new(const char *name, void (*f) (va_list)) region_create(&fiber->gc, &cord->slabc); rlist_add_entry(&cord->fibers, fiber, link); + fiber->caller = &cord->sched; } fiber->f = f; @@ -483,7 +485,6 @@ cord_create(struct cord *cord, const char *name) region_create(&cord->sched.gc, &cord->slabc); fiber_set_name(&cord->sched, "sched"); - cord->sp = cord->stack; cord->max_fid = 100; Exception::init(cord); diff --git a/src/fiber.h b/src/fiber.h index ecb332166b..d78dcbaac8 100644 --- a/src/fiber.h +++ b/src/fiber.h @@ -101,6 +101,11 @@ struct fiber { #ifdef ENABLE_BACKTRACE void *last_stack_frame; #endif + /** + * The fiber which should be scheduled when + * this fiber yields. + */ + struct fiber *caller; int csw; struct tarantool_coro coro; /* A garbage-collected memory pool. */ @@ -145,13 +150,8 @@ struct cord { /** The "main" fiber of this cord, the scheduler. */ struct fiber sched; struct ev_loop *loop; - /** Call stack - in case one fiber decides to call - * another with fiber_call(). This is not used - * currently, all fibers are called by the sched - */ - struct fiber *stack[FIBER_CALL_STACK]; - /** Stack pointer in fiber call stack. */ - struct fiber **sp; + /** Depth of the fiber call stack. */ + int call_stack_depth; /** * Every new fiber gets a new monotonic id. Ids 1-100 are * reserved. diff --git a/src/util.cc b/src/util.cc index 84c021b48b..0e2165f650 100644 --- a/src/util.cc +++ b/src/util.cc @@ -231,7 +231,8 @@ fmemopen(void *buf, size_t size, const char *mode) static char backtrace_buf[4096 * 4]; /* - * note, stack unwinding code assumes that binary is compiled with frame pointers + * note, stack unwinding code assumes that binary is compiled with + * frame pointers */ struct frame { @@ -250,6 +251,11 @@ backtrace(void *frame_, void *stack, size_t stack_size) char *end = p + sizeof(backtrace_buf) - 1; int frameno = 0; while (stack_bottom <= (void *)frame && (void *)frame < stack_top) { + /** + * The stack may be overwritten by the callback + * in case of optimized builds. + */ + struct frame *prev_frame = frame->rbp; p += snprintf(p, end - p, "#%-2d %p in ", frameno++, frame->ret); if (p >= end) @@ -275,7 +281,7 @@ backtrace(void *frame_, void *stack, size_t stack_size) break; #endif - frame = frame->rbp; + frame = prev_frame; } out: *p = '\0'; @@ -293,6 +299,11 @@ backtrace_foreach(backtrace_cb cb, void *frame_, void *stack, size_t stack_size, const char *sym = NULL; size_t offset = 0; while (stack_bottom <= (void *)frame && (void *)frame < stack_top) { + /** + * The stack may be overwritten by the callback + * in case of optimized builds. + */ + struct frame *prev_frame = frame->rbp; #ifdef HAVE_BFD struct symbol *s = addr2symbol(frame->ret); if (s != NULL) { @@ -307,7 +318,7 @@ backtrace_foreach(backtrace_cb cb, void *frame_, void *stack, size_t stack_size, int rc = cb(frameno, frame->ret, sym, offset, cb_ctx); if (rc != 0) return; - frame = frame->rbp; + frame = prev_frame; sym = NULL; offset = 0; frameno++; diff --git a/test/unit/fiber.cc b/test/unit/fiber.cc index d851007323..1672b3be54 100644 --- a/test/unit/fiber.cc +++ b/test/unit/fiber.cc @@ -1,9 +1,41 @@ #include "memory.h" #include "fiber.h" + +enum { + ITERATIONS = 5000, + FIBERS = 10 +}; + +void yield_f(va_list ap) +{ + for (int i = 0; i < ITERATIONS; i++) { + fiber_wakeup(fiber()); + fiber_yield(); + } +} + +void benchmark_f(va_list ap) +{ + struct fiber *fibers[FIBERS]; + for (int i = 0; i < FIBERS; i++) { + fibers[i] = fiber_new("yield-wielder", yield_f); + fiber_wakeup(fibers[i]); + } + /** Wait for fibers to die. */ + for (int i = 0; i < FIBERS; i++) { + while (fibers[i]->fid > 0) + fiber_sleep(0.001); + } + ev_break(loop(), EVBREAK_ALL); +} + int main() { memory_init(); fiber_init(); + struct fiber *benchmark = fiber_new("benchmark", benchmark_f); + fiber_wakeup(benchmark); + ev_run(loop(), 0); fiber_free(); memory_free(); return 0; -- GitLab