[Mono-list] inlining of methods using structs

Rüdiger Klaehn rudi@lambda-computing.de
Wed, 18 Aug 2004 11:26:24 +0200


Hi,

I am new on this list. I have been interested in using .NET for 
numerical problems for some time.

When investigating the lower than expected performance of some code of 
mine, I found out that both the Microsoft JIT and the mono JIT do not 
inline methods with an explicit struct parameter or a struct return value.

This came as quite a shock to me, since I always assumed that structs 
are the way to go for performance critical stuff. I attached an example 
for a case that would benefit tremendously from inlining of method calls 
involving structs in some way.

Is there some deep philosophical reason for not inlining method calls 
involving structs? It does not make any sense for me, since especially 
in the case of small structs the opportunities for inlining and 
subsequent optimizations are huge.

THis would be a nice way to improve the performance of the JIT compiler 
by a factor of 4 or so in many cases.

This is the relevant code in mono/mini/mini.c:
---
	    /* fixme: why cant we inline valuetype returns? */
	    MONO_TYPE_ISSTRUCT (signature->ret))
		return FALSE;

	/* its not worth to inline methods with valuetype arguments?? */
	for (i = 0; i < signature->param_count; i++) {
		if (MONO_TYPE_ISSTRUCT (signature->params [i])) {
			return FALSE;
		}
	}
---

Another thing I have noticed is that mono only inlines methods that are 
shorter than 20 bytes of IL code. I am sure that this makes sense for 
most applications, but in some cases it would be very useful to increase 
this value to 64 or maybe even 128. I think there should be a 
per-assembly attribute to specify the inlining threshold.

best regards

Rüdiger



This is a small benchmark for struct inlining:
---
using System;

namespace OperatorInliningTest
{
     public struct Complex
     {
         internal double re, im;
         public Complex(double re, double im)
         {
             this.re = re;
             this.im = im;
         }
         public static implicit operator Complex(double re) {
             return new Complex(re, 0);
         }
         public static Complex I
         {
             get { return new Complex(0,1); }
         }
         public static Complex Zero
         {
             get { return new Complex(0,0); }
         }
         public static Complex One
         {
             get { return new Complex(1,0); }
         }
         public static Complex operator +(Complex a, Complex b)
         {
             return new Complex(a.re+b.re,a.im+b.im);
         }
         public static Complex operator *(Complex a, Complex b)
         {
             return new Complex(a.re*b.re-a.im*b.im,a.re*b.im+a.im*b.re);
         }
         public double AbsoluteSquared
         {
             get { return re*re+im*im; }
         }
     }
     class Program
     {
         static int MandelbrotIteration1(Complex c)
         {
             Complex x = Complex.Zero;
             for (int i = 0; i < 1000; i++)
             {
                 x = x*x+c;
                 if (x.AbsoluteSquared > 4)
                     return i;
             }
             return -1;
         }
         static int MandelbrotIteration4(Complex c)
         {
             double xre, xim, cre, cim, t;
             xre = 0; xim = 0;
             cre = c.re; cim = c.im;
             for (int i = 0; i < 1000; i++)
             {
                 //x=x*x...
                 t = xre * xre - xim * xim;
                 xim = xre * xim + xre * xim;
                 xre = t;
                 //...=c;
                 xre += cre;
                 xim += cim;
                 //if(x.AbsoluteSquared>4)
                 if (xre * xre + xim * xim > 4)
                     return i;
             }
             return -1;
         }
         static void Main(string[] args)
         {
             DateTime time0, time1;
             TimeSpan delta0, delta1;
             Complex x=Complex.Zero;
             time0 = DateTime.Now;
             for (int i = 0; i < 10000; i++)
                 MandelbrotIteration1(x);
             delta0 = DateTime.Now - time0;
             time1 = DateTime.Now;
             for (int i = 0; i < 10000; i++)
                 MandelbrotIteration4(x);
             delta1 = DateTime.Now - time1;
             Console.WriteLine("Relying on the JIT to inline: {0}", delta0);
             Console.WriteLine("Manually inlined like in the bad old 
days {0}:", delta1);
             Console.ReadLine();
         }
     }
}