Bug 40953 - Bad performance using TypeBuilder
Summary: Bad performance using TypeBuilder
Status: CONFIRMED
Alias: None
Product: Runtime
Classification: Mono
Component: Reflection ()
Version: unspecified
Hardware: PC Linux
: Normal normal
Target Milestone: Future Cycle (TBD)
Assignee: Bugzilla
URL:
Depends on:
Blocks:
 
Reported: 2016-05-07 16:45 UTC by Igor Chevdar
Modified: 2016-09-14 22:09 UTC (History)
3 users (show)

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


Attachments
The test program (8.00 KB, text/plain)
2016-05-07 16:45 UTC, Igor Chevdar
Details


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 for Bug 40953 on GitHub or Developer Community if you have new information to add and do not yet see a matching new report.

If the latest results still closely match this report, you can use the original description:

  • Export the original title and description: GitHub Markdown or Developer Community HTML
  • Copy the title and description into the new report. Adjust them to be up-to-date if needed.
  • Add your new information.

In special cases on GitHub you might also want the comments: GitHub Markdown with public comments

Related Links:
Status:
CONFIRMED

Description Igor Chevdar 2016-05-07 16:45:30 UTC
Created attachment 15938 [details]
The test program

Hello.

I extensively use dynamic code generation with AssemblyBuilder/ModuleBuilder/TypeBuilder. I've always been using Microsoft.NET, but recently tried to run my projects with Mono on Ubuntu (my version of Ubuntu is 16.04 and version of Mono is 4.2.1). Surprisingly all of my performance tests shew that code is working much slower. I investigated the problem and found that any use of TypeBuilder results in bad performance. I tried to find some clues on the Internet but haven't found anything useful. I wrote rather simple example to demonstrate that. In that example there is class Closure used as argument of a DynamicMethod, but if we try to build that class dynamically with TypeBuilder suddenly the resulting code is working 6-8 times slower on my computer under x64 arch (Intel Core i7-4710MQ @ 2.5GHz, 16GB RAM). Under Microsoft.NET (I have 4.6 framework installed) there is no significant difference between these two versions.

To reproduce one needs to compile the attached file (I tried to compile it on both Visual Studio 2013 and MonoDevelop - results are very similar) and run it with Mono on Linux.
Comment 1 Igor Chevdar 2016-05-10 07:25:10 UTC
I managed to simplify the test program. Now it only calls the default constructor and still TypeBuilder works much slower than explicitly written.



public class Program
    {
        static void Main(string[] args)
        {
            var assembly = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("test"), AssemblyBuilderAccess.Run);
            var module = assembly.DefineDynamicModule("zzz");

            var closureTypeBuilder = module.DefineType("Closure", TypeAttributes.Public | TypeAttributes.Class);
            
            var dynamicClosureType = closureTypeBuilder.CreateType();
            var staticClosureType = typeof(Closure);

            foreach(var closureType in new[] {staticClosureType, dynamicClosureType})
            {
                var compiledExp = CreateFunc(closureType);
                for(int i = 0; i < 10; ++i)
                    compiledExp();

                const int iterations = 1000001;
                var stopwatch = Stopwatch.StartNew();
                for(int i = 0; i < iterations; ++i)
                    compiledExp();
                stopwatch.Stop();
                Console.WriteLine("{0:0.000} millions runs per second", iterations * 1.0 / stopwatch.Elapsed.TotalSeconds / 1000000.0);
            }
        }

        private static Func<object> CreateFunc(Type closureType)
        {
            var lambdaMethod = new DynamicMethod("lambda", typeof(object), Type.EmptyTypes, typeof(string), true);
            var il = lambdaMethod.GetILGenerator();
            il.Emit(OpCodes.Newobj, closureType.GetConstructor(Type.EmptyTypes));
            il.Emit(OpCodes.Ret);
            var lambda = (Func<object>)lambdaMethod.CreateDelegate(typeof(Func<object>));
            return lambda;
        }

        public class Closure
        {
        }
    }
Comment 2 Zoltan Varga 2016-05-12 03:14:25 UTC
This is because of these in reflection.c:

	klass->has_finalize = 1;
	klass->has_finalize_inited = 1;

which makes the runtime think dynamic classes have a finalizer, making us use slower allocation functions.
Comment 3 Igor Chevdar 2016-05-12 08:50:34 UTC
Wow, that's interesting. Is there any reason behind that assumption? And will there be a way to configure that in the future?
Comment 4 Zoltan Varga 2016-05-12 10:04:19 UTC
Probly not, its just a bug.