Explorar o código

ncd: implement memory preallocation for statements

ambrop7 %!s(int64=13) %!d(string=hai) anos
pai
achega
288834b8bd
Modificáronse 5 ficheiros con 210 adicións e 13 borrados
  1. 70 0
      ncd/NCDInterpBlock.c
  2. 7 0
      ncd/NCDInterpBlock.h
  3. 9 3
      ncd/NCDModule.c
  4. 46 9
      ncd/NCDModule.h
  5. 78 1
      ncd/ncd.c

+ 70 - 0
ncd/NCDInterpBlock.c

@@ -35,6 +35,7 @@
 #include <misc/balloc.h>
 #include <misc/split_string.h>
 #include <misc/hashfun.h>
+#include <misc/maxalign.h>
 #include <base/BLog.h>
 
 #include "NCDInterpBlock.h"
@@ -44,6 +45,29 @@
 #include "NCDInterpBlock_hash.h"
 #include <structure/CHash_impl.h>
 
+static int compute_prealloc (NCDInterpBlock *o)
+{
+    int size = 0;
+    
+    for (int i = 0; i < o->num_stmts; i++) {
+        int mod = size % BMAX_ALIGN;
+        int align_size = (mod == 0 ? 0 : BMAX_ALIGN - mod);
+        
+        if (align_size + o->stmts[i].alloc_size > INT_MAX - size) {
+            return 0;
+        }
+        
+        o->stmts[i].prealloc_offset = size + align_size;
+        size += align_size + o->stmts[i].alloc_size;
+    }
+    
+    ASSERT(size >= 0)
+    
+    o->prealloc_size = size;
+    
+    return 1;
+}
+
 int NCDInterpBlock_Init (NCDInterpBlock *o, NCDBlock *block)
 {
     if (NCDBlock_NumStatements(block) > INT_MAX) {
@@ -63,6 +87,7 @@ int NCDInterpBlock_Init (NCDInterpBlock *o, NCDBlock *block)
     }
     
     o->num_stmts = 0;
+    o->prealloc_size = -1;
     
     for (NCDStatement *s = NCDBlock_FirstStatement(block); s; s = NCDBlock_NextStatement(block, s)) {
         ASSERT(NCDStatement_Type(s) == NCDSTATEMENT_REG)
@@ -71,6 +96,7 @@ int NCDInterpBlock_Init (NCDInterpBlock *o, NCDBlock *block)
         e->name = NCDStatement_Name(s);
         e->cmdname = NCDStatement_RegCmdName(s);
         e->objnames = NULL;
+        e->alloc_size = 0;
         
         if (!NCDInterpValue_Init(&e->ivalue, NCDStatement_RegArgs(s))) {
             BLog(BLOG_ERROR, "NCDInterpValue_Init failed");
@@ -185,3 +211,47 @@ NCDInterpValue * NCDInterpBlock_StatementInterpValue (NCDInterpBlock *o, int i)
     
     return &o->stmts[i].ivalue;
 }
+
+void NCDInterpBlock_StatementBumpAllocSize (NCDInterpBlock *o, int i, int alloc_size)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(i >= 0)
+    ASSERT(i < o->num_stmts)
+    ASSERT(alloc_size >= 0)
+    
+    if (alloc_size > o->stmts[i].alloc_size) {
+        o->stmts[i].alloc_size = alloc_size;
+        o->prealloc_size = -1;
+    }
+}
+
+int NCDInterpBlock_StatementPreallocSize (NCDInterpBlock *o, int i)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(i >= 0)
+    ASSERT(i < o->num_stmts)
+    
+    return o->stmts[i].alloc_size;
+}
+
+int NCDInterpBlock_PreallocSize (NCDInterpBlock *o)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(o->prealloc_size == -1 || o->prealloc_size >= 0)
+    
+    if (o->prealloc_size < 0 && !compute_prealloc(o)) {
+        return -1;
+    }
+    
+    return o->prealloc_size;
+}
+
+int NCDInterpBlock_StatementPreallocOffset (NCDInterpBlock *o, int i)
+{
+    DebugObject_Access(&o->d_obj);
+    ASSERT(i >= 0)
+    ASSERT(i < o->num_stmts)
+    ASSERT(o->prealloc_size >= 0)
+    
+    return o->stmts[i].prealloc_offset;
+}

+ 7 - 0
ncd/NCDInterpBlock.h

@@ -41,6 +41,8 @@ struct NCDInterpBlock__stmt {
     const char *cmdname;
     char **objnames;
     NCDInterpValue ivalue;
+    int alloc_size;
+    int prealloc_offset;
     int hash_next;
 };
 
@@ -54,6 +56,7 @@ typedef struct NCDInterpBlock__stmt *NCDInterpBlock__hasharg;
 typedef struct {
     struct NCDInterpBlock__stmt *stmts;
     int num_stmts;
+    int prealloc_size;
     NCDInterpBlock__Hash hash;
     DebugObject d_obj;
 } NCDInterpBlock;
@@ -64,5 +67,9 @@ int NCDInterpBlock_FindStatement (NCDInterpBlock *o, int from_index, const char
 const char * NCDInterpBlock_StatementCmdName (NCDInterpBlock *o, int i);
 char ** NCDInterpBlock_StatementObjNames (NCDInterpBlock *o, int i);
 NCDInterpValue * NCDInterpBlock_StatementInterpValue (NCDInterpBlock *o, int i);
+void NCDInterpBlock_StatementBumpAllocSize (NCDInterpBlock *o, int i, int alloc_size);
+int NCDInterpBlock_StatementPreallocSize (NCDInterpBlock *o, int i);
+int NCDInterpBlock_PreallocSize (NCDInterpBlock *o);
+int NCDInterpBlock_StatementPreallocOffset (NCDInterpBlock *o, int i);
 
 #endif

+ 9 - 3
ncd/NCDModule.c

@@ -78,7 +78,11 @@ static void job_handler (NCDModuleInst *n)
         case STATE_INIT: {
             n->state = STATE_DOWN_CLEAN;
             
-            n->m->func_new(n);
+            if (n->m->func_new2) {
+                n->m->func_new2(n->inst_user, n);
+            } else {
+                n->m->func_new(n);
+            }
             return;
         } break;
         
@@ -158,9 +162,11 @@ static void inst_assert_backend (NCDModuleInst *n)
            n->state == STATE_DYING)
 }
 
-void NCDModuleInst_Init (NCDModuleInst *n, const struct NCDModule *m, const NCDObject *method_object, NCDValRef args, void *user, const struct NCDModuleInst_params *params, const struct NCDModuleInst_iparams *iparams)
+void NCDModuleInst_Init (NCDModuleInst *n, const struct NCDModule *m, void *mem, const NCDObject *method_object, NCDValRef args, void *user, const struct NCDModuleInst_params *params, const struct NCDModuleInst_iparams *iparams)
 {
     ASSERT(m)
+    ASSERT(m->alloc_size >= 0)
+    ASSERT(!!mem == m->alloc_size > 0)
     ASSERT(NCDVal_IsList(args))
     ASSERT(params)
     ASSERT(params->func_event)
@@ -181,7 +187,7 @@ void NCDModuleInst_Init (NCDModuleInst *n, const struct NCDModule *m, const NCDO
     n->iparams = iparams;
     
     // set initial instance argument
-    n->inst_user = NULL;
+    n->inst_user = mem;
     
     // init job
     BPending_Init(&n->job, BReactor_PendingGroup(iparams->reactor), (BPending_handler)job_handler, n);

+ 46 - 9
ncd/NCDModule.h

@@ -329,6 +329,9 @@ typedef struct NCDModuleProcess_s {
  * 
  * @param n the instance
  * @param m structure of module functions implementing the module backend
+ * @param mem preallocated memory for the instance. If m->prealloc_size == 0, this must be NULL;
+ *            if it is >0, it must point to a block of memory with at least that many bytes available,
+ *            and properly aligned for any object.
  * @param method_object the base object if the module being initialized is a method, otherwise NULL.
  *                      The caller must ensure that this object is of the type expected by the module
  *                      being initialized.
@@ -338,7 +341,7 @@ typedef struct NCDModuleProcess_s {
  * @param params more parameters, see {@link NCDModuleInst_params}
  * @param iparams more parameters, see {@link NCDModuleInst_iparams}
  */
-void NCDModuleInst_Init (NCDModuleInst *n, const struct NCDModule *m, const NCDObject *method_object, NCDValRef args, void *user, const struct NCDModuleInst_params *params, const struct NCDModuleInst_iparams *iparams);
+void NCDModuleInst_Init (NCDModuleInst *n, const struct NCDModule *m, void *mem, const NCDObject *method_object, NCDValRef args, void *user, const struct NCDModuleInst_params *params, const struct NCDModuleInst_iparams *iparams);
 
 /**
  * Frees the instance.
@@ -386,6 +389,9 @@ int NCDModuleInst_HaveError (NCDModuleInst *n);
 
 /**
  * Sets the argument passed to handlers of a module backend instance.
+ * If the alloc_size member in the {@link NCDModule} structure is >0,
+ * the argument will automatically be set to the preallocated memory,
+ * in which case there is no need to call this.
  * 
  * @param n backend instance handle
  * @param user value to pass to future handlers for this backend instance
@@ -393,9 +399,8 @@ int NCDModuleInst_HaveError (NCDModuleInst *n);
 void NCDModuleInst_Backend_SetUser (NCDModuleInst *n, void *user);
 
 /**
- * Retuns the argument passed to handlers of a module backend instance,
- * i.e. what was set in {@link NCDModuleInst_Backend_SetUser} (or NULL
- * by default).
+ * Retuns the argument passed to handlers of a module backend instance;
+ * see {@link NCDModuleInst_Backend_SetUser}.
  * 
  * @param n backend instance handle
  * @return argument passed to handlers
@@ -639,7 +644,11 @@ typedef void (*NCDModule_func_globalfree) (void);
  * The backend is initialized in down state.
  * 
  * This handler should call {@link NCDModuleInst_Backend_SetUser} to provide
- * an argument for handlers of this backend instance.
+ * an argument for handlers of this backend instance if it needs to keep any
+ * kind of state. Alternatively, the module can have the interpreter preallocate
+ * a predefined amount of memory for each instance, in which case the
+ * {@link NCDModule_func_new2} init function should be used instead of this one
+ * (see alloc_size in the {@link NCDModule} structure).
  * 
  * If the backend fails initialization, this function should report the backend
  * instance to have died with error by calling {@link NCDModuleInst_Backend_SetError}
@@ -650,12 +659,19 @@ typedef void (*NCDModule_func_globalfree) (void);
  */
 typedef void (*NCDModule_func_new) (NCDModuleInst *i);
 
+/**
+ * Like {@link NCDModule_func_new}, but with the extra user argument, as in other module
+ * instance handlers. The initial value of the argument, as used here, is a pointer to
+ * preallocated memory of alloc_size bytes (from {@link NCDModule}), or NULL if alloc_size==0.
+ */
+typedef void (*NCDModule_func_new2) (void *o, NCDModuleInst *i);
+
 /**
  * Handler called to request termination of a backend instance.
  * The backend instance was in down or up state.
  * The backend instance enters dying state.
  * 
- * @param o as in {@link NCDModuleInst_Backend_SetUser}, or NULL by default
+ * @param o see {@link NCDModuleInst_Backend_SetUser}
  */
 typedef void (*NCDModule_func_die) (void *o);
 
@@ -664,7 +680,7 @@ typedef void (*NCDModule_func_die) (void *o);
  * The backend instance is in up state, or in up or down state if can_resolve_when_down=1.
  * This function must not have any side effects.
  * 
- * @param o as in {@link NCDModuleInst_Backend_SetUser}, or NULL by default
+ * @param o see {@link NCDModuleInst_Backend_SetUser}
  * @param name name of the variable to resolve
  * @param mem value memory to use
  * @param out on success, the backend should initialize the value here
@@ -678,7 +694,7 @@ typedef int (*NCDModule_func_getvar) (void *o, const char *name, NCDValMem *mem,
  * The backend instance is in up state, or in up or down state if can_resolve_when_down=1.
  * This function must not have any side effects.
  * 
- * @param o as in {@link NCDModuleInst_Backend_SetUser}, or NULL by default
+ * @param o see {@link NCDModuleInst_Backend_SetUser}
  * @param name name of the object to resolve
  * @param out_object the object will be returned here
  * @return 1 on success, 0 on failure
@@ -694,7 +710,7 @@ typedef int (*NCDModule_func_getobj) (void *o, const char *name, NCDObject *out_
  * termination will be requested with {@link NCDModule_func_die}.
  * The backend instance was in down state.
  * 
- * @param o as in {@link NCDModuleInst_Backend_SetUser}, or NULL by default
+ * @param o see {@link NCDModuleInst_Backend_SetUser}
  */
 typedef void (*NCDModule_func_clean) (void *o);
 
@@ -720,9 +736,17 @@ struct NCDModule {
     
     /**
      * Function called to create an new backend instance.
+     * Only one of the two possible init functions must be set.
      */
     NCDModule_func_new func_new;
     
+    /**
+     * Function called to create an new backend instance, to be used with memory
+     * preallocation.
+     * Only one of the two possible init functions must be set.
+     */
+    NCDModule_func_new2 func_new2;
+    
     /**
      * Function called to request termination of a backend instance.
      * May be NULL, in which case the default is to call NCDModuleInst_Backend_Dead().
@@ -756,6 +780,19 @@ struct NCDModule {
      *   in up state.
      */
     int flags;
+    
+    /**
+     * The amount of memory to preallocate for each module instance.
+     * Preallocation can be used to avoid having to allocate memory from
+     * module initialization. To make use of preallocated memory, use the
+     * {@link NCDModule_func_new2} variant of the init function.
+     * The memory will be available from the point the init function is
+     * called to the point the instance calls {@link NCDModuleInst_Backend_Dead}.
+     * If alloc_size is >0, there is no need to call
+     * {@link NCDModuleInst_Backend_SetUser}, as the the user argument will
+     * automatically be set to a pointer to the preallocated memory.
+     */
+    int alloc_size;
 };
 
 /**

+ 78 - 1
ncd/ncd.c

@@ -43,6 +43,7 @@
 #include <misc/open_standard_streams.h>
 #include <misc/expstring.h>
 #include <misc/split_string.h>
+#include <misc/maxalign.h>
 #include <structure/LinkedList1.h>
 #include <base/BLog.h>
 #include <base/BLog_syslog.h>
@@ -83,6 +84,8 @@ struct statement {
     btime_t error_until;
     NCDModuleInst inst;
     NCDValMem args_mem;
+    char *mem;
+    int mem_size;
     int i;
     int state;
     int have_error;
@@ -95,6 +98,8 @@ struct process {
     BTimer wait_timer;
     BPending work_job;
     LinkedList1Node list_node; // node in processes
+    char *mem;
+    int mem_size;
     int state;
     int ap;
     int fp;
@@ -158,6 +163,7 @@ static void start_terminate (int exit_code);
 static char * names_tostring (char **names);
 static int process_new (NCDProcess *proc_ast, NCDInterpBlock *iblock, NCDModuleProcess *module_process);
 static void process_free (struct process *p);
+static int process_mem_is_preallocated (struct process *p, char *mem);
 static void process_start_terminating (struct process *p);
 static int process_rap (struct process *p);
 static void process_assert_pointers (struct process *p);
@@ -173,6 +179,7 @@ static int process_resolve_variable_expr (struct process *p, int pos, char **nam
 static void statement_logfunc (struct statement *ps);
 static void statement_log (struct statement *ps, int level, const char *fmt, ...);
 static void statement_set_error (struct statement *ps);
+static int statement_allocate_memory (struct statement *ps, int alloc_size, void **out_mem);
 static int statement_resolve_argument (struct statement *ps, NCDInterpValue *arg, NCDValMem *mem, NCDValRef *out);
 static void statement_instance_func_event (struct statement *ps, int event);
 static int statement_instance_func_getobj (struct statement *ps, const char *objname, NCDObject *out_object);
@@ -681,6 +688,16 @@ static int process_new (NCDProcess *proc_ast, NCDInterpBlock *iblock, NCDModuleP
                                             (NCDModuleProcess_interp_func_getobj)process_moduleprocess_func_getobj);
     }
     
+    // preallocate statement memory
+    if ((p->mem_size = NCDInterpBlock_PreallocSize(iblock)) < 0) {
+        BLog(BLOG_ERROR, "NCDInterpBlock_PreallocSize");
+        goto fail1;
+    }
+    if (!(p->mem = BAllocSize(bsize_fromint(p->mem_size)))) {
+        BLog(BLOG_ERROR, "BAllocSize failed");
+        goto fail1;
+    }
+    
     // init statements
     for (int i = 0; i < num_statements; i++) {
         struct statement *ps = &p->statements[i];
@@ -688,6 +705,8 @@ static int process_new (NCDProcess *proc_ast, NCDInterpBlock *iblock, NCDModuleP
         ps->i = i;
         ps->state = SSTATE_FORGOTTEN;
         ps->have_error = 0;
+        ps->mem_size = NCDInterpBlock_StatementPreallocSize(iblock, i);
+        ps->mem = (ps->mem_size == 0 ? NULL : p->mem + NCDInterpBlock_StatementPreallocOffset(iblock, i));
     }
     
     // set state working
@@ -713,6 +732,8 @@ static int process_new (NCDProcess *proc_ast, NCDInterpBlock *iblock, NCDModuleP
     
     return 1;
     
+fail1:
+    BFree(p);
 fail0:
     BLog(BLOG_ERROR, "failed to initialize process %s", NCDProcess_Name(proc_ast));
     return 0;
@@ -728,6 +749,14 @@ void process_free (struct process *p)
         NCDModuleProcess_Interp_Terminated(p->module_process);
     }
     
+    // free statement memory
+    for (int i = 0; i < p->num_statements; i++) {
+        struct statement *ps = &p->statements[i];
+        if (ps->mem && !process_mem_is_preallocated(p, ps->mem)) {
+            free(ps->mem);
+        }
+    }
+    
     // remove from processes list
     LinkedList1_Remove(&processes, &p->list_node);
     
@@ -737,10 +766,20 @@ void process_free (struct process *p)
     // free timer
     BReactor_RemoveTimer(&ss, &p->wait_timer);
     
+    // free preallocated memory
+    BFree(p->mem);
+    
     // free strucure
     BFree(p);
 }
 
+int process_mem_is_preallocated (struct process *p, char *mem)
+{
+    ASSERT(mem)
+    
+    return (mem >= p->mem && mem < p->mem + p->mem_size);
+}
+
 void process_start_terminating (struct process *p)
 {
     ASSERT(p->state != PSTATE_TERMINATING)
@@ -988,6 +1027,9 @@ void process_advance (struct process *p)
         goto fail0;
     }
     
+    // register alloc size for future preallocations
+    NCDInterpBlock_StatementBumpAllocSize(p->iblock, p->ap, module->alloc_size);
+    
     // init args mem
     NCDValMem_Init(&ps->args_mem);
     
@@ -999,8 +1041,15 @@ void process_advance (struct process *p)
         goto fail1;
     }
     
+    // allocate memory
+    void *mem;
+    if (!statement_allocate_memory(ps, module->alloc_size, &mem)) {
+        statement_log(ps, BLOG_ERROR, "failed to allocate memory");
+        goto fail1;
+    }
+    
     // initialize module instance
-    NCDModuleInst_Init(&ps->inst, module, object_ptr, args, ps, &module_params, &module_iparams);
+    NCDModuleInst_Init(&ps->inst, module, mem, object_ptr, args, ps, &module_params, &module_iparams);
     
     // set statement state CHILD
     ps->state = SSTATE_CHILD;
@@ -1150,6 +1199,34 @@ void statement_set_error (struct statement *ps)
     ps->error_until = btime_add(btime_gettime(), options.retry_time);
 }
 
+int statement_allocate_memory (struct statement *ps, int alloc_size, void **out_mem)
+{
+    ASSERT(alloc_size >= 0)
+    ASSERT(out_mem)
+    
+    if (alloc_size == 0) {
+        *out_mem = NULL;
+        return 1;
+    }
+    
+    if (alloc_size > ps->mem_size) {
+        if (ps->mem && !process_mem_is_preallocated(ps->p, ps->mem)) {
+            free(ps->mem);
+        }
+        
+        if (!(ps->mem = malloc(alloc_size))) {
+            statement_log(ps, BLOG_ERROR, "malloc failed");
+            ps->mem_size = 0;
+            return 0;
+        }
+        
+        ps->mem_size = alloc_size;
+    }
+    
+    *out_mem = ps->mem;
+    return 1;
+}
+
 int statement_resolve_argument (struct statement *ps, NCDInterpValue *arg, NCDValMem *mem, NCDValRef *out)
 {
     ASSERT(ps->i <= process_rap(ps->p))