Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple types in one dynamic assembly is way slower than multiple dynamic assemblies with one type each

So I'm emitting some dynamic proxies via DefineDynamicAssembly, and while testing I found that:

  • One type per dynamic assembly: fast, but uses a lot of memory
  • All types in one dynamic assembly: very very slow, but uses much less memory

In my test I generate 10,000 types, and the one-type-per-assembly code runs about 8-10 times faster. The memory usage is completely in line with what I expected, but how come the time to generate the types is that much longer?

Edit: Added some sample code.

One assembly:

var an = new AssemblyName( "Foo" );
var ab = AppDomain.CurrentDomain.DefineDynamicAssembly( an, AssemblyBuilderAccess.Run );
var mb = ab.DefineDynamicModule( "Bar" );

for( int i = 0; i < 10000; i++ )
{                
    var tb = mb.DefineType( "Baz" + i.ToString( "000" ) );
    var met = tb.DefineMethod( "Qux", MethodAttributes.Public );
    met.SetReturnType( typeof( int ) );

    var ilg = met.GetILGenerator();
    ilg.Emit( OpCodes.Ldc_I4, 4711 );
    ilg.Emit( OpCodes.Ret );

    tb.CreateType();
}

One assembly per type:

 for( int i = 0; i < 10000; i++ )
 {
    var an = new AssemblyName( "Foo" );
    var ab = AppDomain.CurrentDomain.DefineDynamicAssembly( an,
                                                            AssemblyBuilderAccess.Run );
    var mb = ab.DefineDynamicModule( "Bar" );

    var tb = mb.DefineType( "Baz" + i.ToString( "000" ) );
    var met = tb.DefineMethod( "Qux", MethodAttributes.Public );
    met.SetReturnType( typeof( int ) );

    var ilg = met.GetILGenerator();
    ilg.Emit( OpCodes.Ldc_I4, 4711 );
    ilg.Emit( OpCodes.Ret );

    tb.CreateType();
}
like image 318
Chris Avatar asked Nov 14 '17 21:11

Chris


People also ask

Which CLR API can be used to create dynamic assemblies System assembly generate system runtime create system reflection emit system dynamic?

Remarks. A dynamic assembly is an assembly that is created using the Reflection Emit APIs. You can use AssemblyBuilder to generate dynamic assemblies in memory and execute their code during the same application run.

What is the difference between static and dynamic assemblies?

Static assemblies are stored on disk in portable executable (PE) files. You can also use the . NET Framework to create dynamic assemblies, which are run directly from memory and are not saved to disk before execution. You can save dynamic assemblies to disk after they have executed.

What is collectible assembly?

Collectible assemblies are dynamic assemblies that can be unloaded without unloading the application domain in which they were created. All managed and unmanaged memory used by a collectible assembly and the types it contains can be reclaimed. Information such as the assembly name is removed from internal tables.


1 Answers

On my PC in LINQPad using C# 7.0 I get one assembly about 8.8 seconds, one assembly per type about 2.6 seconds. Most of the time in the one assembly is in DefineType and CreateType whereas in the time is mainly in DefineDynamicAssembly+DefineDynamicModule.

DefineType checks there is no name conflicts, which is a Dictionary lookup. If the Dictionary is empty, this is about a check for null.

The majority of the time is spent in CreateType, but I don't see where, however it appears that it requires extra time adding types to a single Module.

Creating multiple modules slows the whole process down, but most of the time is spent creating modules and in DefineType, which has to scan every module for a duplicate, so now has increasing up to 10,000 null checks. With a unique module per type, CreateType is very fast.

like image 122
NetMage Avatar answered Oct 12 '22 23:10

NetMage