Просмотр исходного кода

ncd: cache a single prepared process for fast allocation, for each kind of process

ambrop7 13 лет назад
Родитель
Сommit
bbfdce4d92
3 измененных файлов с 152 добавлено и 32 удалено
  1. 25 0
      ncd/NCDInterpProcess.c
  2. 3 0
      ncd/NCDInterpProcess.h
  3. 124 32
      ncd/NCDInterpreter.c

+ 25 - 0
ncd/NCDInterpProcess.c

@@ -197,6 +197,7 @@ int NCDInterpProcess_Init (NCDInterpProcess *o, NCDProcess *process, NCDStringIn
     o->num_stmts = 0;
     o->prealloc_size = -1;
     o->is_template = NCDProcess_IsTemplate(process);
+    o->cache = NULL;
     
     for (NCDStatement *s = NCDBlock_FirstStatement(block); s; s = NCDBlock_NextStatement(block, s)) {
         ASSERT(NCDStatement_Type(s) == NCDSTATEMENT_REG)
@@ -469,3 +470,27 @@ int NCDInterpProcess_NumStatements (NCDInterpProcess *o)
     
     return o->num_stmts;
 }
+
+int NCDInterpProcess_CachePush (NCDInterpProcess *o, void *elem)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(elem)
+    
+    if (o->cache) {
+        return 0;
+    }
+    
+    o->cache = elem;
+    
+    return 1;
+}
+
+void * NCDInterpProcess_CachePull (NCDInterpProcess *o)
+{
+    DebugObject_Access(&o->d_obj);
+    
+    void *elem = o->cache;
+    o->cache = NULL;
+    
+    return elem;
+}

+ 3 - 0
ncd/NCDInterpProcess.h

@@ -77,6 +77,7 @@ typedef struct {
     int is_template;
     int *hash_buckets;
     size_t num_hash_buckets;
+    void *cache;
     DebugObject d_obj;
 } NCDInterpProcess;
 
@@ -95,5 +96,7 @@ int NCDInterpProcess_StatementPreallocOffset (NCDInterpProcess *o, int i);
 const char * NCDInterpProcess_Name (NCDInterpProcess *o);
 int NCDInterpProcess_IsTemplate (NCDInterpProcess *o);
 int NCDInterpProcess_NumStatements (NCDInterpProcess *o);
+int NCDInterpProcess_CachePush (NCDInterpProcess *o, void *elem) WARN_UNUSED;
+void * NCDInterpProcess_CachePull (NCDInterpProcess *o);
 
 #endif

+ 124 - 32
ncd/NCDInterpreter.c

@@ -84,6 +84,10 @@ struct process {
 static void start_terminate (NCDInterpreter *interp, int exit_code);
 static char * implode_id_strings (NCDInterpreter *interp, const NCD_string_id_t *names, size_t num_names, char del);
 static int alloc_base_type_strings (NCDInterpreter *interp, const struct NCDModuleGroup *g);
+static void clear_process_cache (NCDInterpreter *interp);
+static struct process * process_allocate (NCDInterpreter *interp, NCDInterpProcess *iprocess);
+static void process_release (struct process *p, int no_push);
+static void process_assert_statements_cleared (struct process *p);
 static int process_new (NCDInterpreter *interp, NCDInterpProcess *iprocess, NCDModuleProcess *module_process);
 static void process_free (struct process *p, NCDModuleProcess **out_mp);
 static void process_set_state (struct process *p, int state);
@@ -264,6 +268,7 @@ int NCDInterpreter_Init (NCDInterpreter *o, const char *program, size_t program_
     return 1;
     
 fail7:;
+    // free processes
     LinkedList1Node *ln;
     while (ln = LinkedList1_GetFirst(&o->processes)) {
         struct process *p = UPPER_OBJECT(ln, struct process, list_node);
@@ -272,6 +277,8 @@ fail7:;
         process_free(p, &mp);
         ASSERT(!mp)
     }
+    // clear process cache (process_free() above may push to cache)
+    clear_process_cache(o);
 fail6:
     // free modules
     while (o->num_inited_modules-- > 0) {
@@ -316,6 +323,9 @@ void NCDInterpreter_Free (NCDInterpreter *o)
         ASSERT(!mp)
     }
     
+    // clear process cache
+    clear_process_cache(o);
+    
     // free modules
     while (o->num_inited_modules-- > 0) {
         const struct NCDModuleGroup **g = &ncd_modules[o->num_inited_modules];
@@ -425,10 +435,29 @@ int alloc_base_type_strings (NCDInterpreter *interp, const struct NCDModuleGroup
     return 1;
 }
 
-int process_new (NCDInterpreter *interp, NCDInterpProcess *iprocess, NCDModuleProcess *module_process)
+void clear_process_cache (NCDInterpreter *interp)
+{
+    for (NCDProcess *p = NCDProgram_FirstProcess(&interp->program); p; p = NCDProgram_NextProcess(&interp->program, p)) {
+        NCD_string_id_t name_id = NCDStringIndex_Lookup(&interp->string_index, NCDProcess_Name(p));
+        NCDInterpProcess *iprocess = NCDInterpProg_FindProcess(&interp->iprogram, name_id);
+        
+        struct process *p;
+        while (p = NCDInterpProcess_CachePull(iprocess)) {
+            process_release(p, 1);
+        }
+    }
+}
+
+struct process * process_allocate (NCDInterpreter *interp, NCDInterpProcess *iprocess)
 {
     ASSERT(iprocess)
     
+    // try to pull from cache
+    struct process *p = NCDInterpProcess_CachePull(iprocess);
+    if (p) {
+        goto allocated;
+    }
+    
     // get number of statements
     int num_statements = NCDInterpProcess_NumStatements(iprocess);
     
@@ -461,7 +490,7 @@ int process_new (NCDInterpreter *interp, NCDInterpProcess *iprocess, NCDModulePr
     }
     
     // allocate memory
-    struct process *p = BAlloc(alloc_size);
+    p = BAlloc(alloc_size);
     if (!p) {
         goto fail0;
     }
@@ -470,20 +499,12 @@ int process_new (NCDInterpreter *interp, NCDInterpProcess *iprocess, NCDModulePr
     p->interp = interp;
     p->reactor = interp->params.reactor;
     p->iprocess = iprocess;
-    p->module_process = module_process;
     p->ap = 0;
     p->fp = 0;
     p->num_statements = num_statements;
     p->error = 0;
     p->have_alloc = 0;
     
-    // set module process handlers
-    if (p->module_process) {
-        NCDModuleProcess_Interp_SetHandlers(p->module_process, p,
-                                            (NCDModuleProcess_interp_func_event)process_moduleprocess_func_event,
-                                            (NCDModuleProcess_interp_func_getobj)process_moduleprocess_func_getobj);
-    }
-    
     // init statements
     char *mem = (char *)p + mem_off;
     for (int i = 0; i < num_statements; i++) {
@@ -497,20 +518,102 @@ int process_new (NCDInterpreter *interp, NCDInterpProcess *iprocess, NCDModulePr
     // init timer
     BSmallTimer_Init(&p->wait_timer, process_wait_timer_handler);
     
-    // set state, init work job
+    // init work job
+    BSmallPending_Init(&p->work_job, BReactor_PendingGroup(p->reactor), NULL, NULL);
+    
+allocated:
+    ASSERT(p->interp == interp)
+    ASSERT(p->reactor == interp->params.reactor)
+    ASSERT(p->iprocess == iprocess)
+    ASSERT(p->ap == 0)
+    ASSERT(p->fp == 0)
+    ASSERT(p->num_statements == NCDInterpProcess_NumStatements(iprocess))
+    ASSERT(p->error == 0)
+    process_assert_statements_cleared(p);
+    ASSERT(!BSmallPending_IsSet(&p->work_job))
+    ASSERT(!BSmallTimer_IsRunning(&p->wait_timer))
+    
+    return p;
+    
+fail0:
+    BLog(BLOG_ERROR, "failed to allocate memory for process %s", NCDInterpProcess_Name(iprocess));
+    return NULL;
+}
+
+void process_release (struct process *p, int no_push)
+{
+    ASSERT(p->ap == 0)
+    ASSERT(p->fp == 0)
+    ASSERT(p->error == 0)
+    process_assert_statements_cleared(p);
+    ASSERT(!BSmallPending_IsSet(&p->work_job))
+    ASSERT(!BSmallTimer_IsRunning(&p->wait_timer))
+    
+    // try to push to cache
+    if (!no_push && !p->have_alloc) {
+        if (NCDInterpProcess_CachePush(p->iprocess, p)) {
+            return;
+        }
+    }
+    
+    // free work job
+    BSmallPending_Free(&p->work_job, BReactor_PendingGroup(p->reactor));
+    
+    // free statement memory
+    if (p->have_alloc) {
+        for (int i = 0; i < p->num_statements; i++) {
+            struct statement *ps = &p->statements[i];
+            if (statement_mem_is_allocated(ps)) {
+                free(ps->inst.mem);
+            }
+        }
+    }
+    
+    // free strucure
+    BFree(p);
+}
+
+void process_assert_statements_cleared (struct process *p)
+{
+#ifndef NDEBUG
+    for (int i = 0; i < p->num_statements; i++) {
+        ASSERT(p->statements[i].i == i)
+        ASSERT(p->statements[i].inst.istate == SSTATE_FORGOTTEN)
+    }
+#endif
+}
+
+int process_new (NCDInterpreter *interp, NCDInterpProcess *iprocess, NCDModuleProcess *module_process)
+{
+    ASSERT(iprocess)
+    
+    // allocate prepared process struct
+    struct process *p = process_allocate(interp, iprocess);
+    if (!p) {
+        return 0;
+    }
+    
+    // set module process pointer
+    p->module_process = module_process;
+    
+    // set module process handlers
+    if (p->module_process) {
+        NCDModuleProcess_Interp_SetHandlers(p->module_process, p,
+                                            (NCDModuleProcess_interp_func_event)process_moduleprocess_func_event,
+                                            (NCDModuleProcess_interp_func_getobj)process_moduleprocess_func_getobj);
+    }
+    
+    // set state
     process_set_state(p, PSTATE_WORKING);
-    BSmallPending_Init(&p->work_job, BReactor_PendingGroup(p->reactor), (BSmallPending_handler)process_work_job_handler_working, p);
+    BSmallPending_SetHandler(&p->work_job, (BSmallPending_handler)process_work_job_handler_working, p);
     
     // insert to processes list
     LinkedList1_Append(&interp->processes, &p->list_node);
     
     // schedule work
-    BSmallPending_Set(&p->work_job, BReactor_PendingGroup(p->reactor));   
-    return 1;
+    BSmallPending_Set(&p->work_job, BReactor_PendingGroup(p->reactor));
     
-fail0:
-    BLog(BLOG_ERROR, "failed to allocate memory for process %s", NCDInterpProcess_Name(iprocess));
-    return 0;
+    return 1;
 }
 
 void process_set_state (struct process *p, int state)
@@ -530,27 +633,16 @@ void process_free (struct process *p, NCDModuleProcess **out_mp)
     // give module process to caller so it can inform the process creator that the process has terminated
     *out_mp = p->module_process;
     
-    // free statement memory
-    if (p->have_alloc) {
-        for (int i = 0; i < p->num_statements; i++) {
-            struct statement *ps = &p->statements[i];
-            if (statement_mem_is_allocated(ps)) {
-                free(ps->inst.mem);
-            }
-        }
-    }
-    
     // remove from processes list
     LinkedList1_Remove(&p->interp->processes, &p->list_node);
     
-    // free work job
-    BSmallPending_Free(&p->work_job, BReactor_PendingGroup(p->reactor));
-    
     // free timer
     BReactor_RemoveSmallTimer(p->reactor, &p->wait_timer);
     
-    // free strucure
-    BFree(p);
+    // clear error
+    p->error = 0;
+    
+    process_release(p, 0);
 }
 
 void process_start_terminating (struct process *p)