Merge: Fast super strings
authorJean Privat <jean@pryen.org>
Sat, 28 Mar 2015 01:34:45 +0000 (08:34 +0700)
committerJean Privat <jean@pryen.org>
Sat, 28 Mar 2015 01:34:45 +0000 (08:34 +0700)
Superstrings, like "a{b}c", are managed in the AST as a special group of sub-expression nodes that are either literal string parts or standard expressions.
The previous example is basically `["a", b, "c"]`

Previously, the compilation of super-strings was direct: the values are grouped in an array and `to_s` is called on it.

So in fact `"a{b}c"` was compiled as `["a", b, "c"].to_s`.

This basic implementation is simple and correct. But it has some drawbacks:

* a new Array[Object] (and a NativeArray[Object]) is allocated each time the super-string is evaluated.
* all elements are to_s-ized in `Array::to_s`, even the literal parts.
* an additional NativeArray[String] is allocated in `Array:to_s` to do the fast concatenation.

Because of the numerous allocations, superstrings caused a lot of work to the GC.

This PR provides a better, but more complex implementation:

* instead of an Array[Object], a NativeArray[String] is directly build and a fast concatenation `native_to_s` is invoked.
* the allocated NativeArray is cached in a static variable so it can be reused in next evaluation.
* the literal string parts are stored in the native array as is, and only once just after the allocation of the native array.

Results for nitc/nitc/nitc:
before: 0m6.076s
after: 0m5.512s (-9% not bad!)

Pull-Request: #1219
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>

1  2 
src/compiler/separate_compiler.nit

@@@ -1580,7 -1580,7 +1580,7 @@@ class SeparateCompilerVisito
                        self.add("{res} = {recv}->attrs[{a.const_color}] != NULL; /* {a} on {recv.inspect}*/")
                else
  
 -                      if not mtype.is_c_primitive then
 +                      if not mtype.is_c_primitive and not mtype.is_tagged then
                                self.add("{res} = {recv}->attrs[{a.const_color}].val != NULL; /* {a} on {recv.inspect} */")
                        else
                                self.add("{res} = 1; /* NOT YET IMPLEMENTED: isset of primitives: {a} on {recv.inspect} */")
                self.require_declaration(a.const_color)
                if self.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
                        var attr = "{recv}->attrs[{a.const_color}]"
 -                      if mtype.is_c_primitive then
 +                      if mtype.is_tagged then
 +                              # The attribute is not primitive, thus store it as tagged
 +                              var tv = autobox(value, compiler.mainmodule.object_type)
 +                              self.add("{attr} = {tv}; /* {a} on {recv.inspect} */")
 +                      else if mtype.is_c_primitive then
                                assert mtype isa MClassType
                                # The attribute is primitive, thus we store it in a box
                                # The trick is to create the box the first time then resuse the box
                end
        end
  
+       redef fun native_array_get(nat, i)
+       do
+               var nclass = mmodule.native_array_class
+               var recv = "((struct instance_{nclass.c_name}*){nat})->values"
+               # Because the objects are boxed, return the box to avoid unnecessary (or broken) unboxing/reboxing
+               var res = self.new_expr("{recv}[{i}]", compiler.mainmodule.object_type)
+               return res
+       end
+       redef fun native_array_set(nat, i, val)
+       do
+               var nclass = mmodule.native_array_class
+               var recv = "((struct instance_{nclass.c_name}*){nat})->values"
+               self.add("{recv}[{i}]={val};")
+       end
        fun link_unresolved_type(mclassdef: MClassDef, mtype: MType) do
                assert mtype.need_anchor
                var compiler = self.compiler
@@@ -2308,14 -2320,3 +2324,14 @@@ redef class AMethPropde
                return super
        end
  end
 +
 +redef class AAttrPropdef
 +      redef fun init_expr(v, recv)
 +      do
 +              super
 +              if is_lazy and v.compiler.modelbuilder.toolcontext.opt_no_union_attribute.value then
 +                      var guard = self.mlazypropdef.mproperty
 +                      v.write_attribute(guard, recv, v.bool_instance(false))
 +              end
 +      end
 +end