Code Review: Precompilation5


#1

tfpt review “/shelveset:Precompilation5;REDMOND\tomat”

Mostly Ruby changes, one simple DLR change.

Implements precompilation of simple Ruby method invocations. Adds
pre-generated rules for call sites that are

  1.  context bound
    
  2.  without a block, splat or RHS argument
    
  3.  with self of runtime type RubyObject strongly typed to Object in 
    

the call site

  1.  the method is resolved to an existing Ruby method or an "empty" 
    

library method

  1.  no context class checks are needed due to protected visibility
    
  2.  the method doesn't have optional or unsplatted parameters and 
    

the number of mandatory parameters matches the call site

  1.  the method has at most 5 parameters
    

The rules are generated to MethodDispatcher.Generated.cs by
MethodDispatcher.Generator.rb. The generator finds blocks in C# code
that match this pattern:

#if GENERATOR

#else
<C# template>
#end
#region Generated by …

#endregion

It creates a new class G < Generator for each such block end
module-eval’s in that class. Then it calls G.new.generate.
The default implementation takes the <C# template> and replaces each
occurrence of /$MethodName/ or /$MethodName{/ … /}/ in the
template by a value returned by a call to MethodName on the generator
class. The generator replaces the content of “Generated by” region by
the resulting code. The <C# template> is optional - the #else block can
be omitted.

There could be multiple templates in a single file. The generator also
searches the file for /$$/ comments before evaluating them. Text in
between such comment and following semicolon is evaluated as a Ruby
global variable definition. For example,

internal const int /$$/PrecompiledParameterCount = 5;

sets a global variable $PrecompiledParameterCount to 5.


The shelveset also moves event sites (method added/removed/undefined,
…) to RubyModule and RubyClass so that they don’t become megamorphic.
Encapsulates class version into VersionHandle class.
Changes RubyObject.ToString to detect that it’s called by rule
expression writer in DEBUG builds. We shouldn’t call to_s dynamically in
that case since it would recursively call the writer leading to a stack
overflow.

Changes perf stats output writer to a file instead of a console: if
-X:TrackPerformance -X:PerfStats is passed on command line
“perfstats.log” file is created in the current directory.
Adds perf-counters for Ruby rules. For example, for “mspec ci library”
we get the following numbers below. “Bind” means that a rule is
generated in runtime, “BindDelegate” that a site is being bound. The
number of sites bound to pregenerated rules can be calculated by
subtracting Bind from BindDelegate. In this case we saved generating 30%
of RubyCallAction rules (1 - 153k/219k) by using precompiled rules.

Ruby:

RubyCallAction(S,2=):

BindDelegate

1

Ruby:

RubyCallAction(.S,16):

Bind

1

Ruby:

RubyCallAction(S,2=):

Bind

1

Ruby:

ConvertToIntAction(.C,0):

Bind

1

Ruby:

ConvertToIntAction:

BindDelegate

1

Ruby:

ConvertToHashAction(.C,0):

Bind

1

Ruby:

ConvertToHashAction:

BindDelegate

1

Ruby:

ConvertToArrayAction(.C,0):

Bind

1

Ruby:

RubyCallAction(.S,16):

BindDelegate

1

Ruby:

ConvertToArrayAction:

BindDelegate

1

Ruby:

RubyCallAction(S,10):

Bind

1

Ruby:

RubyCallAction(S,10):

BindDelegate

1

Ruby:

RubyCallAction(.C,3):

Bind

1

Ruby:

RubyCallAction(.C,3):

BindDelegate

1

Ruby:

RubyCallAction(S,7):

BindDelegate

2

Ruby:

SuperCallAction(.S,3&):

Bind

2

Ruby:

ConvertToSymbolAction:

BindDelegate

2

Ruby:

RubyCallAction(S,7):

Bind

2

Ruby:

ConvertToSymbolAction(.C,0):

Bind

2

Ruby:

RubyCallAction(.S,4&):

BindDelegate

3

Ruby:

ConvertToProcAction:

BindDelegate

3

Ruby:

ConvertToProcAction(.C,0):

Bind

3

Ruby:

RubyCallAction(.S,4&):

Bind

3

Ruby:

TryConvertToStrAction(.C,0):

Bind

3

Ruby:

TryConvertToStrAction:

BindDelegate

3

Ruby:

RubyCallAction(S,4&):

Bind

3

Ruby:

RubyCallAction(S,4&):

BindDelegate

3

Ruby:

RubyCallAction(.S,7):

Bind

4

Ruby:

RubyCallAction(.S,7):

BindDelegate

4

Ruby:

SuperCallAction(.S,6&):

Bind

5

Ruby:

RubyCallAction(.S,3&):

BindDelegate

7

Ruby:

RubyCallAction(.S,3&):

Bind

7

Ruby:

RubyCallAction(.S,1*):

BindDelegate

11

Ruby:

RubyCallAction(.S,1*):

Bind

11

Ruby:

RubyCallAction(.S,5):

Bind

11

Ruby:

RubyCallAction(S,2*):

Bind

11

Ruby:

RubyCallAction(S,2*):

BindDelegate

11

Ruby:

RubyCallAction(.S,8):

Bind

11

Ruby:

RubyCallAction(.S,8):

BindDelegate

11

Ruby:

RubyCallAction(.S,6):

Bind

12

Ruby:

RubyCallAction(.S,6):

BindDelegate

12

Ruby:

RubyCallAction(.S,5):

BindDelegate

12

Ruby:

ConvertToFixnumAction:

BindDelegate

13

Ruby:

ConvertToFixnumAction(.C,0):

Bind

13

Ruby:

RubyCallAction(S,3&):

BindDelegate

14

Ruby:

SuperCallAction(.S,5&):

Bind

14

Ruby:

RubyCallAction(S,3&):

Bind

14

Ruby:

RubyCallAction(S,1*&):

BindDelegate

15

Ruby:

RubyCallAction(S,1*&):

Bind

15

Ruby:

ConvertToStrAction:

BindDelegate

16

Ruby:

ConvertToStrAction(.C,0):

Bind

16

Ruby:

RubyCallAction(S,5):

BindDelegate

25

Ruby:

RubyCallAction(S,5):

Bind

25

Ruby:

RubyCallAction(S,6):

BindDelegate

26

Ruby:

RubyCallAction(S,6):

Bind

26

Ruby:

RubyCallAction(S,9):

BindDelegate

38

Ruby:

RubyCallAction(S,9):

Bind

38

Ruby:

ConvertToRegexAction(.C,0):

Bind

44

Ruby:

ConvertToRegexAction:

BindDelegate

44

Ruby:

RubyCallAction(S,2*&):

BindDelegate

45

Ruby:

RubyCallAction(S,2*&):

Bind

45

Ruby:

RubyCallAction(.C,2):

BindDelegate

46

Ruby:

RubyCallAction(.C,2):

Bind

46

Ruby:

ConvertToFAction:

BindDelegate

46

Ruby:

ConvertToFAction(.C,0):

Bind

46

Ruby:

RubyCallAction(.S,0*&):

Bind

55

Ruby:

RubyCallAction(.S,0*&):

BindDelegate

55

Ruby:

SuperCallAction(.S,2&):

Bind

57

Ruby:

RubyCallAction(.S,1=):

Bind

59

Ruby:

RubyCallAction(.S,1=):

BindDelegate

59

Ruby:

SuperCallAction(.S,0&):

Bind

121

Ruby:

RubyCallAction(S,4):

Bind

128

Ruby:

RubyCallAction(S,4):

BindDelegate

130

Ruby:

SuperCallAction(.S,0*&):

Bind

137

Ruby:

RubyCallAction(.S,4):

Bind

146

Ruby:

RubyCallAction(.S,4):

BindDelegate

154

Ruby:

CompositeConversionAction(.C,0):

Bind

315

Ruby:

CompositeConversionAction:

BindDelegate

315

Ruby:

RubyCallAction(.S,2&):

BindDelegate

439

Ruby:

SuperCallAction(.S,1&):

Bind

439

Ruby:

RubyCallAction(.S,2&):

Bind

439

Ruby:

RubyCallAction(.S,0=):

Bind

460

Ruby:

RubyCallAction(.S,0=):

BindDelegate

460

Ruby:

TryConvertToAAction(.C,0):

Bind

1241

Ruby:

TryConvertToAAction:

BindDelegate

1241

Ruby:

TryConvertToArrayAction:

BindDelegate

1262

Ruby:

TryConvertToArrayAction(.C,0):

Bind

1262

Ruby:

RubyCallAction(.S,3):

Bind

1432

Ruby:

RubyCallAction(.S,0*):

Bind

1456

Ruby:

RubyCallAction(.S,0*):

BindDelegate

1456

Ruby:

RubyCallAction(.S,3):

BindDelegate

1468

Ruby:

RubyCallAction(.C,0&):

Bind

1510

Ruby:

RubyCallAction(.C,0&):

BindDelegate

1510

Ruby:

RubyCallAction(.S,0&):

Bind

1686

Ruby:

RubyCallAction(.S,0&):

BindDelegate

1686

Ruby:

ConvertToSAction:

BindDelegate

1737

Ruby:

ConvertToSAction(.C,0):

Bind

1737

Ruby:

RubyCallAction(S,2&):

Bind

1896

Ruby:

RubyCallAction(.C,0):

Bind

2096

Ruby:

RubyCallAction(S,0=):

Bind

2136

Ruby:

RubyCallAction(S,3):

Bind

2717

Ruby:

RubyCallAction(S,1=):

Bind

2729

Ruby:

RubyCallAction(.S,2):

Bind

4756

Ruby:

RubyCallAction(S,2):

Bind

4865

Ruby:

RubyCallAction(.S,1&):

Bind

4992

Ruby:

RubyCallAction(S,0*):

Bind

5508

Ruby:

RubyCallAction(S,1&):

Bind

5724

Ruby:

RubyCallAction(.S,0):

Bind

7159

Ruby:

RubyCallAction(S,1*):

Bind

7351

Ruby:

RubyCallAction(.S,1):

Bind

10717

Ruby:

RubyCallAction(.C,1):

Bind

11999

Ruby:

RubyCallAction(S,0&):

Bind

12303

Ruby:

RubyCallAction(S,0):

Bind

26073

Ruby:

RubyCallAction(S,1):

Bind

38619

153377

Ruby:

RubyCallAction(S,2&):

BindDelegate

1896

Ruby:

RubyCallAction(.C,0):

BindDelegate

2105

Ruby:

RubyCallAction(S,0=):

BindDelegate

2136

Ruby:

RubyCallAction(S,1=):

BindDelegate

2729

Ruby:

RubyCallAction(S,3):

BindDelegate

2760

Ruby:

RubyCallAction(.S,1&):

BindDelegate

4992

Ruby:

RubyCallAction(S,0*):

BindDelegate

5508

Ruby:

RubyCallAction(S,1&):

BindDelegate

5724

Ruby:

RubyCallAction(S,1*):

BindDelegate

7351

Ruby:

RubyCallAction(S,2):

BindDelegate

7882

Ruby:

RubyCallAction(.S,2):

BindDelegate

8695

Ruby:

RubyCallAction(S,0&):

BindDelegate

12303

Ruby:

RubyCallAction(.C,1):

BindDelegate

14264

Ruby:

RubyCallAction(.S,1):

BindDelegate

19195

Ruby:

RubyCallAction(.S,0):

BindDelegate

26125

Ruby:

RubyCallAction(S,0):

BindDelegate

44212

Ruby:

RubyCallAction(S,1):

BindDelegate

51343

219220

0.3004

Tomas


#2

Looks good to me (as do the following two changes).

From: Tomas M.
Sent: Tuesday, May 05, 2009 11:41 AM
To: IronRuby External Code R.; Rowan Code R.
Cc: removed_email_address@domain.invalid
Subject: Code Review: Precompilation5

tfpt review “/shelveset:Precompilation5;REDMOND\tomat”

Mostly Ruby changes, one simple DLR change.

Implements precompilation of simple Ruby method invocations. Adds
pre-generated rules for call sites that are

  1.  context bound
    
  2.  without a block, splat or RHS argument
    
  3.  with self of runtime type RubyObject strongly typed to Object in 
    

the call site

  1.  the method is resolved to an existing Ruby method or an "empty" 
    

library method

  1.  no context class checks are needed due to protected visibility
    
  2.  the method doesn't have optional or unsplatted parameters and 
    

the number of mandatory parameters matches the call site

  1.  the method has at most 5 parameters
    

The rules are generated to MethodDispatcher.Generated.cs by
MethodDispatcher.Generator.rb. The generator finds blocks in C# code
that match this pattern:

#if GENERATOR

#else
<C# template>
#end
#region Generated by …

#endregion

It creates a new class G < Generator for each such block end
module-eval’s in that class. Then it calls G.new.generate.
The default implementation takes the <C# template> and replaces each
occurrence of /$MethodName/ or /$MethodName{/ … /}/ in the
template by a value returned by a call to MethodName on the generator
class. The generator replaces the content of “Generated by” region by
the resulting code. The <C# template> is optional - the #else block can
be omitted.

There could be multiple templates in a single file. The generator also
searches the file for /$$/ comments before evaluating them. Text in
between such comment and following semicolon is evaluated as a Ruby
global variable definition. For example,

internal const int /$$/PrecompiledParameterCount = 5;

sets a global variable $PrecompiledParameterCount to 5.


The shelveset also moves event sites (method added/removed/undefined,
…) to RubyModule and RubyClass so that they don’t become megamorphic.
Encapsulates class version into VersionHandle class.
Changes RubyObject.ToString to detect that it’s called by rule
expression writer in DEBUG builds. We shouldn’t call to_s dynamically in
that case since it would recursively call the writer leading to a stack
overflow.

Changes perf stats output writer to a file instead of a console: if
-X:TrackPerformance -X:PerfStats is passed on command line
“perfstats.log” file is created in the current directory.
Adds perf-counters for Ruby rules. For example, for “mspec ci library”
we get the following numbers below. “Bind” means that a rule is
generated in runtime, “BindDelegate” that a site is being bound. The
number of sites bound to pregenerated rules can be calculated by
subtracting Bind from BindDelegate. In this case we saved generating 30%
of RubyCallAction rules (1 - 153k/219k) by using precompiled rules.

Ruby:

RubyCallAction(S,2=):

BindDelegate

1

Ruby:

RubyCallAction(.S,16):

Bind

1

Ruby:

RubyCallAction(S,2=):

Bind

1

Ruby:

ConvertToIntAction(.C,0):

Bind

1

Ruby:

ConvertToIntAction:

BindDelegate

1

Ruby:

ConvertToHashAction(.C,0):

Bind

1

Ruby:

ConvertToHashAction:

BindDelegate

1

Ruby:

ConvertToArrayAction(.C,0):

Bind

1

Ruby:

RubyCallAction(.S,16):

BindDelegate

1

Ruby:

ConvertToArrayAction:

BindDelegate

1

Ruby:

RubyCallAction(S,10):

Bind

1

Ruby:

RubyCallAction(S,10):

BindDelegate

1

Ruby:

RubyCallAction(.C,3):

Bind

1

Ruby:

RubyCallAction(.C,3):

BindDelegate

1

Ruby:

RubyCallAction(S,7):

BindDelegate

2

Ruby:

SuperCallAction(.S,3&):

Bind

2

Ruby:

ConvertToSymbolAction:

BindDelegate

2

Ruby:

RubyCallAction(S,7):

Bind

2

Ruby:

ConvertToSymbolAction(.C,0):

Bind

2

Ruby:

RubyCallAction(.S,4&):

BindDelegate

3

Ruby:

ConvertToProcAction:

BindDelegate

3

Ruby:

ConvertToProcAction(.C,0):

Bind

3

Ruby:

RubyCallAction(.S,4&):

Bind

3

Ruby:

TryConvertToStrAction(.C,0):

Bind

3

Ruby:

TryConvertToStrAction:

BindDelegate

3

Ruby:

RubyCallAction(S,4&):

Bind

3

Ruby:

RubyCallAction(S,4&):

BindDelegate

3

Ruby:

RubyCallAction(.S,7):

Bind

4

Ruby:

RubyCallAction(.S,7):

BindDelegate

4

Ruby:

SuperCallAction(.S,6&):

Bind

5

Ruby:

RubyCallAction(.S,3&):

BindDelegate

7

Ruby:

RubyCallAction(.S,3&):

Bind

7

Ruby:

RubyCallAction(.S,1*):

BindDelegate

11

Ruby:

RubyCallAction(.S,1*):

Bind

11

Ruby:

RubyCallAction(.S,5):

Bind

11

Ruby:

RubyCallAction(S,2*):

Bind

11

Ruby:

RubyCallAction(S,2*):

BindDelegate

11

Ruby:

RubyCallAction(.S,8):

Bind

11

Ruby:

RubyCallAction(.S,8):

BindDelegate

11

Ruby:

RubyCallAction(.S,6):

Bind

12

Ruby:

RubyCallAction(.S,6):

BindDelegate

12

Ruby:

RubyCallAction(.S,5):

BindDelegate

12

Ruby:

ConvertToFixnumAction:

BindDelegate

13

Ruby:

ConvertToFixnumAction(.C,0):

Bind

13

Ruby:

RubyCallAction(S,3&):

BindDelegate

14

Ruby:

SuperCallAction(.S,5&):

Bind

14

Ruby:

RubyCallAction(S,3&):

Bind

14

Ruby:

RubyCallAction(S,1*&):

BindDelegate

15

Ruby:

RubyCallAction(S,1*&):

Bind

15

Ruby:

ConvertToStrAction:

BindDelegate

16

Ruby:

ConvertToStrAction(.C,0):

Bind

16

Ruby:

RubyCallAction(S,5):

BindDelegate

25

Ruby:

RubyCallAction(S,5):

Bind

25

Ruby:

RubyCallAction(S,6):

BindDelegate

26

Ruby:

RubyCallAction(S,6):

Bind

26

Ruby:

RubyCallAction(S,9):

BindDelegate

38

Ruby:

RubyCallAction(S,9):

Bind

38

Ruby:

ConvertToRegexAction(.C,0):

Bind

44

Ruby:

ConvertToRegexAction:

BindDelegate

44

Ruby:

RubyCallAction(S,2*&):

BindDelegate

45

Ruby:

RubyCallAction(S,2*&):

Bind

45

Ruby:

RubyCallAction(.C,2):

BindDelegate

46

Ruby:

RubyCallAction(.C,2):

Bind

46

Ruby:

ConvertToFAction:

BindDelegate

46

Ruby:

ConvertToFAction(.C,0):

Bind

46

Ruby:

RubyCallAction(.S,0*&):

Bind

55

Ruby:

RubyCallAction(.S,0*&):

BindDelegate

55

Ruby:

SuperCallAction(.S,2&):

Bind

57

Ruby:

RubyCallAction(.S,1=):

Bind

59

Ruby:

RubyCallAction(.S,1=):

BindDelegate

59

Ruby:

SuperCallAction(.S,0&):

Bind

121

Ruby:

RubyCallAction(S,4):

Bind

128

Ruby:

RubyCallAction(S,4):

BindDelegate

130

Ruby:

SuperCallAction(.S,0*&):

Bind

137

Ruby:

RubyCallAction(.S,4):

Bind

146

Ruby:

RubyCallAction(.S,4):

BindDelegate

154

Ruby:

CompositeConversionAction(.C,0):

Bind

315

Ruby:

CompositeConversionAction:

BindDelegate

315

Ruby:

RubyCallAction(.S,2&):

BindDelegate

439

Ruby:

SuperCallAction(.S,1&):

Bind

439

Ruby:

RubyCallAction(.S,2&):

Bind

439

Ruby:

RubyCallAction(.S,0=):

Bind

460

Ruby:

RubyCallAction(.S,0=):

BindDelegate

460

Ruby:

TryConvertToAAction(.C,0):

Bind

1241

Ruby:

TryConvertToAAction:

BindDelegate

1241

Ruby:

TryConvertToArrayAction:

BindDelegate

1262

Ruby:

TryConvertToArrayAction(.C,0):

Bind

1262

Ruby:

RubyCallAction(.S,3):

Bind

1432

Ruby:

RubyCallAction(.S,0*):

Bind

1456

Ruby:

RubyCallAction(.S,0*):

BindDelegate

1456

Ruby:

RubyCallAction(.S,3):

BindDelegate

1468

Ruby:

RubyCallAction(.C,0&):

Bind

1510

Ruby:

RubyCallAction(.C,0&):

BindDelegate

1510

Ruby:

RubyCallAction(.S,0&):

Bind

1686

Ruby:

RubyCallAction(.S,0&):

BindDelegate

1686

Ruby:

ConvertToSAction:

BindDelegate

1737

Ruby:

ConvertToSAction(.C,0):

Bind

1737

Ruby:

RubyCallAction(S,2&):

Bind

1896

Ruby:

RubyCallAction(.C,0):

Bind

2096

Ruby:

RubyCallAction(S,0=):

Bind

2136

Ruby:

RubyCallAction(S,3):

Bind

2717

Ruby:

RubyCallAction(S,1=):

Bind

2729

Ruby:

RubyCallAction(.S,2):

Bind

4756

Ruby:

RubyCallAction(S,2):

Bind

4865

Ruby:

RubyCallAction(.S,1&):

Bind

4992

Ruby:

RubyCallAction(S,0*):

Bind

5508

Ruby:

RubyCallAction(S,1&):

Bind

5724

Ruby:

RubyCallAction(.S,0):

Bind

7159

Ruby:

RubyCallAction(S,1*):

Bind

7351

Ruby:

RubyCallAction(.S,1):

Bind

10717

Ruby:

RubyCallAction(.C,1):

Bind

11999

Ruby:

RubyCallAction(S,0&):

Bind

12303

Ruby:

RubyCallAction(S,0):

Bind

26073

Ruby:

RubyCallAction(S,1):

Bind

38619

153377

Ruby:

RubyCallAction(S,2&):

BindDelegate

1896

Ruby:

RubyCallAction(.C,0):

BindDelegate

2105

Ruby:

RubyCallAction(S,0=):

BindDelegate

2136

Ruby:

RubyCallAction(S,1=):

BindDelegate

2729

Ruby:

RubyCallAction(S,3):

BindDelegate

2760

Ruby:

RubyCallAction(.S,1&):

BindDelegate

4992

Ruby:

RubyCallAction(S,0*):

BindDelegate

5508

Ruby:

RubyCallAction(S,1&):

BindDelegate

5724

Ruby:

RubyCallAction(S,1*):

BindDelegate

7351

Ruby:

RubyCallAction(S,2):

BindDelegate

7882

Ruby:

RubyCallAction(.S,2):

BindDelegate

8695

Ruby:

RubyCallAction(S,0&):

BindDelegate

12303

Ruby:

RubyCallAction(.C,1):

BindDelegate

14264

Ruby:

RubyCallAction(.S,1):

BindDelegate

19195

Ruby:

RubyCallAction(.S,0):

BindDelegate

26125

Ruby:

RubyCallAction(S,0):

BindDelegate

44212

Ruby:

RubyCallAction(S,1):

BindDelegate

51343

219220

0.3004

Tomas