From 76ebde4cd25b97dab224ecb28ed0fb333cac407e Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Fri, 8 Jan 2010 21:46:32 -0800 Subject: [PATCH] Blend time stamp into other work items. The time change is usually a trivial operation, so instead carry a timestamp on all the work items and let the work thread decide on its own when to do a SET_TIME operation. This reduces some pthread overhead and thus gets us some better performance. --- vpi/sys_lxt2.c | 14 ++++++++++---- vpi/vcd_priv.h | 9 +++++++-- vpi/vcd_priv2.cc | 36 +++++++++++++++++++++++++++--------- 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/vpi/sys_lxt2.c b/vpi/sys_lxt2.c index 599a81d9e..91fb76cb4 100644 --- a/vpi/sys_lxt2.c +++ b/vpi/sys_lxt2.c @@ -795,10 +795,18 @@ static PLI_INT32 sys_dumpvars_calltf(PLI_BYTE8*name) static void* lxt2_thread(void*arg) { + /* Keep track of the current time, and only call the set_time + function when the time changes. */ + uint64_t cur_time = 0; int run_flag = 1; while (run_flag) { struct vcd_work_item_s*cell = vcd_work_thread_peek(); + if (cell->time != cur_time) { + cur_time = cell->time; + lxt2_wr_set_time64(dump_file, cur_time); + } + switch (cell->type) { case WT_NONE: break; @@ -811,16 +819,13 @@ static void* lxt2_thread(void*arg) case WT_DUMPOFF: lxt2_wr_set_dumpoff(dump_file); break; - case WT_SET_TIME: - lxt2_wr_set_time64(dump_file, cell->op_.val_u64); - break; case WT_EMIT_DOUBLE: lxt2_wr_emit_value_double(dump_file, cell->sym_.lxt2, 0, cell->op_.val_double); + break; case WT_EMIT_BITS: lxt2_wr_emit_value_bit_string(dump_file, cell->sym_.lxt2, 0, cell->op_.val_char); - free(cell->op_.val_char); break; case WT_TERMINATE: run_flag = 0; @@ -829,6 +834,7 @@ static void* lxt2_thread(void*arg) vcd_work_thread_pop(); } + return 0; } diff --git a/vpi/vcd_priv.h b/vpi/vcd_priv.h index b5bbe98c1..70b445d62 100644 --- a/vpi/vcd_priv.h +++ b/vpi/vcd_priv.h @@ -69,7 +69,6 @@ EXTERN void vcd_scope_names_delete(void); typedef enum vcd_work_item_type_e { WT_NONE, - WT_SET_TIME, WT_EMIT_BITS, WT_EMIT_DOUBLE, WT_DUMPON, @@ -80,15 +79,21 @@ typedef enum vcd_work_item_type_e { struct lxt2_wr_symbol; +# define VAL_CHAR_ARRAY_SIZE 64 struct vcd_work_item_s { vcd_work_item_type_t type; + uint64_t time; union { struct lxt2_wr_symbol*lxt2; } sym_; + union { double val_double; +#ifdef VAL_CHAR_ARRAY_SIZE + char val_char[VAL_CHAR_ARRAY_SIZE]; +#else char*val_char; - uint64_t val_u64; +#endif } op_; }; diff --git a/vpi/vcd_priv2.cc b/vpi/vcd_priv2.cc index 0670fe43b..ae22a0d03 100644 --- a/vpi/vcd_priv2.cc +++ b/vpi/vcd_priv2.cc @@ -21,6 +21,7 @@ # include # include # include +# include /* Nexus Id cache @@ -81,7 +82,7 @@ extern "C" void vcd_scope_names_delete(void) static pthread_t work_thread; -static const unsigned WORK_QUEUE_SIZE = 512*1024; +static const unsigned WORK_QUEUE_SIZE = 128*1024; static struct vcd_work_item_s work_queue[WORK_QUEUE_SIZE]; static volatile unsigned work_queue_next = 0; static volatile unsigned work_queue_fill = 0; @@ -91,6 +92,8 @@ static pthread_cond_t work_queue_is_empty_sig = PTHREAD_COND_INITIALIZER; static pthread_cond_t work_queue_notempty_sig = PTHREAD_COND_INITIALIZER; static pthread_cond_t work_queue_notfull_sig = PTHREAD_COND_INITIALIZER; +static uint64_t work_queue_next_time = 0; + struct vcd_work_item_s* vcd_work_thread_peek(void) { // There must always only be 1 vcd work thread, and only the @@ -115,7 +118,14 @@ void vcd_work_thread_pop(void) unsigned use_fill = work_queue_fill - 1; work_queue_fill = use_fill; - unsigned use_next = work_queue_next + 1; + unsigned use_next = work_queue_next; +#ifndef VAL_CHAR_ARRAY_SIZE + struct vcd_work_item_s*cell = work_queue + use_next; + if (cell->type == WT_EMIT_BITS) { + free(cell->op_.val_char); + } +#endif + use_next += 1; if (use_next >= WORK_QUEUE_SIZE) use_next = 0; work_queue_next = use_next; @@ -153,13 +163,17 @@ static struct vcd_work_item_s* grab_item(void) if (cur >= WORK_QUEUE_SIZE) cur -= WORK_QUEUE_SIZE; - return work_queue + cur; + // Write the new timestamp into the work item. + struct vcd_work_item_s*cell = work_queue + cur; + cell->time = work_queue_next_time; + return cell; } static void unlock_item(void) { - work_queue_fill += 1; - if (work_queue_fill == 1) + unsigned use_fill = work_queue_fill + 1; + work_queue_fill = use_fill; + if (use_fill == 1) pthread_cond_signal(&work_queue_notempty_sig); pthread_mutex_unlock(&work_queue_mutex); @@ -188,10 +202,7 @@ void vcd_work_dumpoff(void) void vcd_work_set_time(uint64_t val) { - struct vcd_work_item_s*cell = grab_item(); - cell->type = WT_SET_TIME; - cell->op_.val_u64 = val; - unlock_item(); + work_queue_next_time = val; } void vcd_work_emit_double(struct lxt2_wr_symbol*sym, double val) @@ -205,10 +216,17 @@ void vcd_work_emit_double(struct lxt2_wr_symbol*sym, double val) void vcd_work_emit_bits(struct lxt2_wr_symbol*sym, const char* val) { + struct vcd_work_item_s*cell = grab_item(); cell->type = WT_EMIT_BITS; cell->sym_.lxt2 = sym; +#ifdef VAL_CHAR_ARRAY_SIZE + size_t need_len = strlen(val) + 1; + assert(need_len <= VAL_CHAR_ARRAY_SIZE); + memcpy(cell->op_.val_char, val, need_len); +#else cell->op_.val_char = strdup(val); +#endif unlock_item(); }