Bug 6160 - mono does not inline a situation expected
Summary: mono does not inline a situation expected
Alias: None
Product: Runtime
Classification: Mono
Component: JIT ()
Version: unspecified
Hardware: PC Mac OS
: --- normal
Target Milestone: ---
Assignee: Bugzilla
Depends on:
Reported: 2012-07-17 13:45 UTC by Jonathan Shore
Modified: 2012-09-02 13:07 UTC (History)
4 users (show)

Is this bug a regression?: ---
Last known good build:

A reduction of the problem into a test case. (654.77 KB, application/x-bzip2)
2012-07-17 13:45 UTC, Jonathan Shore

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.

Please create a new report on GitHub or Developer Community with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:

Description Jonathan Shore 2012-07-17 13:45:57 UTC
Created attachment 2212 [details]
A reduction of the problem into a test case.

I am using a 3rd party library for vector / matrix functionality that has a generic class  Vector<T>.   I have created a number of specializations of this class, such as:

   class SpecialVector : Vector<double>
      public override double this[int i]
         get { return _data[i]; }
         set { _data[i] = value; }

      public override double[] Data
         { get { return _data; } }


      private double[] _data;

I have a rather tight-looped numerical algorithm on a number of these vectors.  Such as:

   for (int i = 0 ; i < len ; i++)
	var x = x[i];
	for (int k = ileft; k <= iright; k++) 
	    var xk = x[k];
	    var yk = y[k];

Where, x and y are instances of these vectors.   x[i] and y[i] call the this[int] accessor on their respective vectors.   Vector<double> defines double this[int i] as a virtual function.   

SpecialVector is a sealed class, however.   Nevertheless, I realize a 125% increase in performance if I do the following:

   var vx = x.Data;
   var vy = y.Data;
   for (int i = 0 ; i < len ; i++)
      var x = vx[i];
      for (int k = ileft; k <= iright; k++) 
         var xk = vx[k];
         var yk = vy[k];

I suspect that mono / LLVM is not attempting inline this[int i] since is a virtual method, in spite of being trivially determined to be leaf class.  OR is there another reason why this is not inlined?

Getting performance out of this is very important for what I do.  Would like to request this as a feature request.  I have included a small test project.
Comment 1 Jonathan Shore 2012-07-17 14:12:34 UTC
Note that the LowessOriginal and LowessInlined classes differ by 3 lines in the ComputeLowess function.  In the original sets up vx, vy, vyhat as pointing to the vector class instances.  In the "inlined" version, vx, vy, and vyhat get the double[] reference from the vectors.

I forgot to make the class sealed in the test.  Tried that as well as referring to SpecialVector instead of Vector<double> in the code, with the same effect of not being inlined.
Comment 2 Jonathan Shore 2012-07-17 14:14:33 UTC
finally note that I ran with:   

MONO_INLINELIMIT=128 mono -O=unsafe --llvm bin/Release/TestInlining.exe
Comment 3 Zoltan Varga 2012-08-15 00:07:24 UTC
The JIT can't determine the method that is called at JIT time, because:
- the code uses variables of type Vector<double> instead of SpecialVector.
- even if it uses SpecialVector, the C# compiler generates calls to the original virtual method instead of
  the final method, i.e. from the IL disassembly of LowessOriginal:ComputeLowess:

	IL_0048:  callvirt instance !0 class [MathNet.Numerics]MathNet.Numerics.LinearAlgebra.Generic.Vector`1<float64>::get_Item(int32)
Comment 4 Miguel de Icaza [MSFT] 2012-09-02 13:07:43 UTC
Mono in general does not implement optimizations that remove virtual method calls based on the current set of loaded classes.

The reason is that new classes can be loaded dynamically and this would require mono to re-jit existing code where the optimization was done (easy, but would increase memory usage to keep a list of those methods) and would require Mono to support hijacking of existing threads and hot-swapping the code for a new version.

The second is not easy to do.

One fairly trick option would be to generate the plain call, followed by a bunch of NOP opcodes that would leave enough room for the machine-specific Callvirt to be injected when dynamic code is loaded.

The only problem with this approach is to do this in an atomic and thread-safe way (what happens if the code is executing on another thread at the time the re-virtualization happens).

For now, I am going to close this as a wont-fix optimization.