www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 16330] New: Expansion of code (static foreach, templates) is

https://issues.dlang.org/show_bug.cgi?id=16330

          Issue ID: 16330
           Summary: Expansion of code (static foreach, templates) is too
                    slow
           Product: D
           Version: D2
          Hardware: All
                OS: All
            Status: NEW
          Severity: normal
          Priority: P1
         Component: dmd
          Assignee: nobody puremagic.com
          Reporter: bugzilla digitalmars.com

Ethan Watson writes:

Expansion of code (static foreach, templates) is slow to the point where string
mixins are a legitimate compile-time optimisation

Take an example of whittling down a tuple/variable argument list. Doing it
recursively would look something like this:

template SomeEliminator( Symbols... )
{
  static if( Symbols.length >= 1 )
  {
    static if( SomeCondition!( Symbol[ 0 ] ) )
    {
      alias SomeEliminator = TypeTuple!( Symbol[ 0 ], Symbols[ 1 .. $ ] );
    }
    else
    {
      alias SomeEliminator = TypeTuple!( Symbols[ 1 .. $ ] );
    }
  }
  else
  {
    alias SomeEliminator = TypeTuple!( );
  }
}

Okay, that works, but the template expansion is a killer on compile-time
performance. It's legitimately far quicker on the compiler to do this:

template SomeEliminator( Symbols... )
{
  string SymbolSelector()
  {
    string[] strOutputs;
    foreach( iIndex, Symbol; Symbols )
    {
      static if( SomeCondition!( Symbol ) )
      {
        strOutputs ~= "Symbols[ " ~ iIndex.stringof ~ " ]";
      }
    }
    return strOutputs.joinWith( ", " );
  }
  mixin( "alias SomeEliminator = TypeTuple!( " ~ SymbolSelector() ~ " );" );
}

With just a small codebase that I'm working on here, it chops seconds off the
compile time. Of course, maybe there's something I'm missing here about
variable parameter parsing and doing it without a mixin is quite possible and
just as quick as the mixin, but that would make it the third method I know of
to achieve the same effect. The idiomatic way of doing this without mixins
should at least be defined, and optimised at the compiler level so that people
don't get punished for writing natural D code.

Then there was this one that I came across:

outofswitch: switch( symbolName )
{
  foreach( Variable; VariablesOf!( SearchType ) )
  {
    case Variable.Name:
      doSomething!( Variable.Type )();
      break outofswitch;
  }
  default:
    writeln( symbolName, " was not found!" );
    break;
}

This caused compile time to blow way out. How far out? By rewriting it like
this, I cut compile times in half (at that point, from 10 seconds to 5):

switch( symbolName )
{
  mixin( generateSwitchFor!( SearchType )() );
  default:
    writeln( symbolName, " was not found!" );
    break;
}

Now, I love mixins, both template form and string form. The binding system uses
them extensively. But mixins like this are effectively a hack. Anytime I have
to break out a mixin because my compile time doubled from a seemingly simple
piece of code is not good.

--
Jul 28 2016