llvmpipe: Trim the fragment shader cached based on LLVM IR instruction count.
authorJosé Fonseca <jfonseca@vmware.com>
Wed, 30 Nov 2011 17:12:00 +0000 (17:12 +0000)
committerJosé Fonseca <jfonseca@vmware.com>
Thu, 8 Dec 2011 17:59:33 +0000 (17:59 +0000)
Number of fragment shader variants is not very representative of the
memory used by LLVM, neither is number of shader instructions, as often
texture sampling constitutes most of the generated code.

This change adds an additional trim criteria: least recently used
fragment shader variants will be freed until the total number of LLVM IR
instruction falls below a specified threshold.

Reviewed-by: Brian Paul <brianp@vmware.com>
src/gallium/auxiliary/gallivm/lp_bld_type.c
src/gallium/auxiliary/gallivm/lp_bld_type.h
src/gallium/drivers/llvmpipe/lp_context.h
src/gallium/drivers/llvmpipe/lp_limits.h
src/gallium/drivers/llvmpipe/lp_state_fs.c
src/gallium/drivers/llvmpipe/lp_state_fs.h

index efd159f886912b93431ea21f8694da62e202473d..413e69bedac2e8fefecb2dda00d0a8de106a8dd1 100644 (file)
@@ -411,3 +411,28 @@ lp_build_context_init(struct lp_build_context *bld,
    bld->zero = LLVMConstNull(bld->vec_type);
    bld->one = lp_build_one(gallivm, type);
 }
+
+
+/**
+ * Count the number of instructions in a function.
+ */
+unsigned
+lp_build_count_instructions(LLVMValueRef function)
+{
+   unsigned num_instrs = 0;
+   LLVMBasicBlockRef block;
+
+   block = LLVMGetFirstBasicBlock(function);
+   while (block) {
+      LLVMValueRef instr;
+      instr = LLVMGetFirstInstruction(block);
+      while (instr) {
+         ++num_instrs;
+
+         instr = LLVMGetNextInstruction(instr);
+      }
+      block = LLVMGetNextBasicBlock(block);
+   }
+
+   return num_instrs;
+}
index 5007e83ac5f28a1a0739a653170a3fd813cc390b..f11a190e7ccdc25defc776908e804550b7186738 100644 (file)
@@ -398,4 +398,8 @@ lp_build_context_init(struct lp_build_context *bld,
                       struct lp_type type);
 
 
+unsigned
+lp_build_count_instructions(LLVMValueRef function);
+
+
 #endif /* !LP_BLD_TYPE_H */
index 70fba21309564dcc0625e1e2049260bea1beb3cd..438fc887083b90ce112f2364e4d85b459c028cb7 100644 (file)
@@ -129,6 +129,7 @@ struct llvmpipe_context {
    /** List of all fragment shader variants */
    struct lp_fs_variant_list_item fs_variants_list;
    unsigned nr_fs_variants;
+   unsigned nr_fs_instrs;
 
    /** JIT code generation */
    struct gallivm_state *gallivm;
index 2538164ffaaf3be5785cc270db2f41a21620006c..43b00c56a5feddbeef3eeb8925e8f01db69f6045 100644 (file)
  */
 #define LP_MAX_SHADER_VARIANTS 1024
 
+/**
+ * Max number of instructions (for all fragment shaders combined per context)
+ * that will be kept around.
+ */
+#define LP_MAX_SHADER_INSTRUCTIONS (128*1024)
+
 /**
  * Max number of setup variants that will be kept around.
  *
index 26f8d7ff76578e14c084b0fba98dd9ebb8f08787..5af74fe792597e1f41e9eb4c5722748f9b034716 100644 (file)
@@ -804,6 +804,7 @@ generate_fragment(struct llvmpipe_context *lp,
       LLVMWriteBitcodeToFile(gallivm->module, "llvmpipe.bc");
    }
 
+   variant->nr_instrs += lp_build_count_instructions(function);
    /*
     * Translate the LLVM IR into machine code.
     */
@@ -1129,6 +1130,7 @@ llvmpipe_remove_shader_variant(struct llvmpipe_context *lp,
    /* remove from context's list */
    remove_from_list(&variant->list_item_global);
    lp->nr_fs_variants--;
+   lp->nr_fs_instrs -= variant->nr_instrs;
 
    FREE(variant);
 }
@@ -1350,11 +1352,22 @@ llvmpipe_update_fs(struct llvmpipe_context *lp)
       /* variant not found, create it now */
       int64_t t0, t1, dt;
       unsigned i;
+      unsigned variants_to_cull;
+
+      if (0) {
+         debug_printf("%u variants,\t%u instrs,\t%u instrs/variant\n",
+                      lp->nr_fs_variants,
+                      lp->nr_fs_instrs,
+                      lp->nr_fs_variants ? lp->nr_fs_instrs / lp->nr_fs_variants : 0);
+      }
 
       /* First, check if we've exceeded the max number of shader variants.
        * If so, free 25% of them (the least recently used ones).
        */
-      if (lp->nr_fs_variants >= LP_MAX_SHADER_VARIANTS) {
+      variants_to_cull = lp->nr_fs_variants >= LP_MAX_SHADER_VARIANTS ? LP_MAX_SHADER_VARIANTS / 4 : 0;
+
+      if (variants_to_cull ||
+          lp->nr_fs_instrs >= LP_MAX_SHADER_INSTRUCTIONS) {
          struct pipe_context *pipe = &lp->pipe;
 
          /*
@@ -1370,17 +1383,15 @@ llvmpipe_update_fs(struct llvmpipe_context *lp)
           * pending for destruction on flush.
           */
 
-         if (lp->nr_fs_variants >= LP_MAX_SHADER_VARIANTS) {
-            for (i = 0; i < LP_MAX_SHADER_VARIANTS / 4; i++) {
-               struct lp_fs_variant_list_item *item;
-               if (is_empty_list(&lp->fs_variants_list)) {
-                  break;
-               }
-               item = last_elem(&lp->fs_variants_list);
-               assert(item);
-               assert(item->base);
-               llvmpipe_remove_shader_variant(lp, item->base);
+         for (i = 0; i < variants_to_cull || lp->nr_fs_instrs >= LP_MAX_SHADER_INSTRUCTIONS; i++) {
+            struct lp_fs_variant_list_item *item;
+            if (is_empty_list(&lp->fs_variants_list)) {
+               break;
             }
+            item = last_elem(&lp->fs_variants_list);
+            assert(item);
+            assert(item->base);
+            llvmpipe_remove_shader_variant(lp, item->base);
          }
       }
 
@@ -1401,6 +1412,7 @@ llvmpipe_update_fs(struct llvmpipe_context *lp)
          insert_at_head(&shader->variants, &variant->list_item_local);
          insert_at_head(&lp->fs_variants_list, &variant->list_item_global);
          lp->nr_fs_variants++;
+         lp->nr_fs_instrs += variant->nr_instrs;
          shader->variants_cached++;
       }
    }
index 98410c69359c1ce02825392be3a516f7ef298ba8..273d241d8fc1cbc16c0fbcc0c0ad1f0ce612a430 100644 (file)
@@ -88,6 +88,9 @@ struct lp_fragment_shader_variant
 
    lp_jit_frag_func jit_function[2];
 
+   /* Total number of LLVM instructions generated */
+   unsigned nr_instrs;
+
    struct lp_fs_variant_list_item list_item_global, list_item_local;
    struct lp_fragment_shader *shader;