[Mesa-dev] [PATCH 05/12] glsl: Replace the linear search in get_intrinsic_opcode with a radix trie

Ian Romanick idr at freedesktop.org
Tue Jul 19 20:13:07 UTC 2016


From: Ian Romanick <ian.d.romanick at intel.com>

If there is a way to do this cleanly in mako, I'm very interested to
hear about it.

   text	   data	    bss	    dec	    hex	filename
7529003	 273096	  28584	7830683	 777c9b /tmp/i965_dri-64bit-before.so
7528883	 273096	  28584	7830563	 777c23	/tmp/i965_dri-64bit-after.so

v2: Replace list.sort() with sorted() for a pretty dramatic code clean
up.  Suggested by Dylan.

Signed-off-by: Ian Romanick <ian.d.romanick at intel.com>
---
 src/compiler/glsl/nir_intrinsic_map.py | 126 +++++++++++++++++++++++++++++----
 1 file changed, 114 insertions(+), 12 deletions(-)

diff --git a/src/compiler/glsl/nir_intrinsic_map.py b/src/compiler/glsl/nir_intrinsic_map.py
index f6e9241..337f1e9 100644
--- a/src/compiler/glsl/nir_intrinsic_map.py
+++ b/src/compiler/glsl/nir_intrinsic_map.py
@@ -67,6 +67,115 @@ intrinsics = [("__intrinsic_atomic_read", ("nir_intrinsic_atomic_counter_read_va
               ("__intrinsic_atomic_exchange_shared", ("nir_intrinsic_shared_atomic_exchange", None)),
               ("__intrinsic_atomic_comp_swap_shared", ("nir_intrinsic_shared_atomic_comp_swap", None))]
 
+def remove_prefix(table, prefix_length):
+    """Strip prefix_length characters off the name of each entry in table."""
+
+    return [(s[prefix_length:], d) for (s, d) in table]
+
+
+def generate_trie(table):
+    """table is a list of (string, data) tuples.  It is assumed to be sorted by
+    string.
+
+    A radix trie (or compact prefix trie) is recursively generated from the
+    list of names.  Names are paritioned into groups that have at least
+    prefix_thresh (tunable parameter) common prefix characters.  Each of these
+    groups becomes the branches at the current level of the tree.  The
+    matching prefix characters from each group is removed, and the group is
+    recursively operated on in the same fashion.
+
+    The recursion terminates when no groups can be formed with at least
+    prefix_thresh matching characters.
+
+    Each node in the trie is a 3-element tuple:
+
+        (prefix_string, [child_nodes], client_data)
+
+    One of [child_nodes] or client_data will be None.
+
+    See https://en.wikipedia.org/wiki/Radix_tree for more background details
+    on the data structure.
+
+    """
+
+    # Threshold for considering two strings to have the same prefix.
+    prefix_thresh = 1
+
+    if len(table) == 1 and table[0][0] == "":
+        return [("", None, table[0][1])]
+
+    trie_level = []
+
+    (s, d) = table[0]
+    candidates = [(s, d)]
+    base = s
+    prefix_length = len(s)
+
+    for (s, d) in table[1:]:
+        if s[:prefix_thresh] == base[:prefix_thresh]:
+            candidates.append((s, d))
+
+            l = len(s[:([x[0]==x[1] for x in zip(s, base)]+[0]).index(0)])
+            if l < prefix_length:
+                prefix_length = l
+        else:
+            trie_level.append((base[:prefix_length], generate_trie(remove_prefix(candidates, prefix_length)), None))
+
+            candidates = [(s, d)]
+            base = s
+            prefix_length = len(s)
+
+    trie_level.append((base[:prefix_length], generate_trie(remove_prefix(candidates, prefix_length)), None))
+
+    return trie_level
+
+
+def emit_trie_leaf(indent, d):
+    if d[1] is None:
+        return "{}return {};\n".format(indent, d[0])
+    else:
+        c_code = "{}int_op = {};\n".format(indent, d[0])
+        c_code += "{}uint_op = {};\n".format(indent, d[1])
+        return c_code
+
+
+def trie_as_C_code(trie, indent="   ", prefix_string="__intrinsic_"):
+    conditional = "if"
+
+    c_code = ""
+    for (s, t, d) in trie:
+        if d is not None:
+            c_code +=  "{}{} (name[0] == '\\0') {{\n".format(indent, conditional)
+            c_code += "{}   /* {} */\n".format(indent, prefix_string)
+            c_code += emit_trie_leaf(indent + "   ", d);
+
+        else:
+            # Before emitting the string comparison, check to see of the
+            # subtree has a single element with an empty string.  In that
+            # case, use strcmp() instead of strncmp() and don't advance the
+            # name pointer.
+
+            if len(t) == 1 and t[0][2] is not None:
+                if s == "":
+                    c_code += "{}{} (name[0] == '\\0') {{\n".format(indent, conditional, s)
+                else:
+                    c_code += "{}{} (strcmp(name, \"{}\") == 0) {{\n".format(indent, conditional, s)
+
+                c_code += "{}   /* {} */\n".format(indent, prefix_string + s)
+                c_code += emit_trie_leaf(indent + "   ", t[0][2]);
+            else:
+                c_code += "{}{} (strncmp(name, \"{}\", {}) == 0) {{\n".format(indent, conditional, s, len(s))
+                c_code += "{}   name += {};\n\n".format(indent, len(s))
+
+                c_code += trie_as_C_code(t, indent + "   ", prefix_string + s)
+
+        conditional = "} else if"
+
+    c_code += "{}}} else\n".format(indent)
+    c_code += "{}   unreachable(\"Invalid intrinsic name\");\n\n".format(indent)
+    return c_code
+
+
 template = """
 namespace _glsl_to_nir {
 
@@ -81,17 +190,7 @@ get_intrinsic_opcode(const char *name, const ir_dereference *return_deref)
    nir_intrinsic_op int_op;
    nir_intrinsic_op uint_op;
 
-    % for (name, ops) in intrinsics:
-   if (strcmp(name, "${name[12:]}") == 0) {
-        % if ops[1] is None:
-      return ${ops[0]};
-        % else:
-      int_op = ${ops[0]};
-      uint_op = ${ops[1]};
-        % endif
-   } else
-    % endfor
-      unreachable("Unknown intrinsic name");
+${trie_search}
 
    assert(return_deref);
    if (return_deref->type == glsl_type::int_type)
@@ -105,4 +204,7 @@ get_intrinsic_opcode(const char *name, const ir_dereference *return_deref)
 """
 
 if __name__ == "__main__":
-    print(Template(template).render(intrinsics=intrinsics))
+    trie_search = trie_as_C_code(generate_trie(
+        [(s[12:], d) for s, d in sorted(intrinsics, key=lambda t: t[0])]))
+
+    print(Template(template).render(trie_search=trie_search))
-- 
2.5.5



More information about the mesa-dev mailing list