鉴于:

  • .NET程序集名为expression_host
  • .NET程序集名为CreateInstanceTest
  • CreateInstanceTest在其配置文件
  • 中启用NetFx40_LegacySecurityPolicy
  • expression_host的属性为SecurityPermission(SecurityAction.RequestOptional)
  • CreateInstanceTest加载expression_host程序集-爆炸!!! -Activator.CreateInstance是 toast

  • 请观察输出:
    new() = 13, Activator.CreateInstance() = 111
    Just loaded expression_host, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
    new() = 6, Activator.CreateInstance() = 1944
    

    解释:
  • 该程序执行500,000次new TestClass()和500,000次Activator.CreateInstance(typeof(TestClass))
  • 然后加载expression_host程序集
  • 然后重复步骤1。
  • 数字以毫秒为单位。

  • 两个组件都非常小。这是代码:

    CreateInstanceTest

    CreateInstanceTest.csproj :
    <?xml version="1.0" encoding="utf-8"?>
    <Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
      <PropertyGroup>
        <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
        <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
        <ProjectGuid>{83690315-C8AC-4C52-9CDD-334115F521C0}</ProjectGuid>
        <OutputType>Exe</OutputType>
        <RootNamespace>CreateInstanceTest</RootNamespace>
        <AssemblyName>CreateInstanceTest</AssemblyName>
        <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
        <PlatformTarget>AnyCPU</PlatformTarget>
        <DebugSymbols>true</DebugSymbols>
        <DebugType>full</DebugType>
        <OutputPath>bin\$(Configuration)\</OutputPath>
        <ErrorReport>prompt</ErrorReport>
        <WarningLevel>4</WarningLevel>
        <Prefer32Bit>false</Prefer32Bit>
        <UseVSHostingProcess>false</UseVSHostingProcess>
      </PropertyGroup>
      <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
        <Optimize>false</Optimize>
        <DefineConstants>DEBUG;TRACE</DefineConstants>
      </PropertyGroup>
      <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
        <Optimize>true</Optimize>
        <DefineConstants>TRACE</DefineConstants>
      </PropertyGroup>
      <ItemGroup>
        <Reference Include="System" />
      </ItemGroup>
      <ItemGroup>
        <Compile Include="Program.cs" />
      </ItemGroup>
      <ItemGroup>
        <None Include="App.config">
          <SubType>Designer</SubType>
        </None>
      </ItemGroup>
      <ItemGroup>
        <ProjectReference Include="..\..\Users\mkharitonov\Documents\Visual Studio 2012\Projects\CreateInstanceTest\expression_host\expression_host.csproj">
          <Project>{01f4b604-d5a3-454f-aff7-e1f5c43d293e}</Project>
          <Name>expression_host</Name>
        </ProjectReference>
      </ItemGroup>
      <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
    </Project>
    

    Program.cs :
    using System;
    using System.Diagnostics;
    
    namespace CreateInstanceTest
    {
        internal class TestClass
        {
        }
    
        internal class Program
        {
            public static void Main()
            {
                const int COUNT = 500000;
                long newTime;
                long createInstanceTime;
    
                DoOneRound(COUNT, out newTime, out createInstanceTime);
                Console.WriteLine("new() = {0}, Activator.CreateInstance() = {1}", newTime, createInstanceTime);
    
                ScrewThingsUp();
    
                DoOneRound(COUNT, out newTime, out createInstanceTime);
                Console.WriteLine("new() = {0}, Activator.CreateInstance() = {1}", newTime, createInstanceTime);
    
                Console.WriteLine("Press any key to terminate ...");
                Console.ReadKey();
            }
    
            public static void DoOneRound(int count, out long newTime, out long createInstanceTime)
            {
                var sw = new Stopwatch();
    
                sw.Start();
                for (int index = 0; index < count; ++index)
                {
    // ReSharper disable ObjectCreationAsStatement
                    new TestClass();
    // ReSharper restore ObjectCreationAsStatement
                }
                sw.Stop();
                newTime = sw.ElapsedMilliseconds;
    
                var type = typeof(TestClass);
                sw.Restart();
                for (int index = 0; index < count; ++index)
                {
                    Activator.CreateInstance(type);
                }
                sw.Stop();
                createInstanceTime = sw.ElapsedMilliseconds;
            }
    
            private static void ScrewThingsUp()
            {
                Console.WriteLine("Just loaded {0}", typeof(ReportExprHostImpl).Assembly.FullName);
           }
        }
    }
    

    app.config :
    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <runtime>
        <NetFx40_LegacySecurityPolicy enabled="true"/>
      </runtime>
    </configuration>
    

    expression_host

    expression_host.csproj :
    <?xml version="1.0" encoding="utf-8"?>
    <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
      <PropertyGroup>
        <SchemaVersion>2.0</SchemaVersion>
        <ProjectGuid>{01F4B604-D5A3-454F-AFF7-E1F5C43D293E}</ProjectGuid>
        <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
        <AssemblyName>expression_host</AssemblyName>
        <OutputType>Library</OutputType>
        <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
        <DebugSymbols>true</DebugSymbols>
        <OutputPath>bin\$(Configuration)\</OutputPath>
        <DebugType>full</DebugType>
        <PlatformTarget>AnyCPU</PlatformTarget>
        <ErrorReport>prompt</ErrorReport>
        <Prefer32Bit>false</Prefer32Bit>
      </PropertyGroup>
      <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
        <DefineConstants>DEBUG;TRACE</DefineConstants>
      </PropertyGroup>
      <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
        <DefineConstants>TRACE</DefineConstants>
        <Optimize>true</Optimize>
      </PropertyGroup>
      <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
      <ItemGroup>
        <Compile Include="ReportExprHostImpl.cs" />
      </ItemGroup>
    </Project>
    

    ReportExprHostImpl.cs :
    using System.Security.Permissions;
    
    [assembly: SecurityPermission(SecurityAction.RequestOptional)]
    public class ReportExprHostImpl
    {
    }
    

    就是这样。

    现在是什么问题?问题是如何处理它。这实际上是真实案例的最小复制,与A case of abysmal degradation of Activator.CreateInstance performance有关

    在实际的应用程序中,我们必须运行报告,并且被NetFx40_LegacySecurityPolicy所困扰,这会使我们的应用程序的某些部分执行得很糟糕。

    最后,在实际应用程序中,我们无法更改表达式主机程序集的代码,因为它们是由Microsoft报告框架动态构建的。

    所以,我们能做些什么?

    编辑

    [assembly: SecurityTransparent]添加到CreateInstanceTest程序集似乎可以改善:
    new() = 6, Activator.CreateInstance() = 106
    Just loaded expression_host, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
    new() = 14, Activator.CreateInstance() = 904
    

    现在Activator.CreateInstance仅慢9倍,而不是20倍。但仍然慢9倍!

    编辑2

    这似乎是.NET内部的事情。托管探查器(ANTS)并不是很有用。

    其他受影响的方法:
  • MethodInfo.Invoke(与Activator.CreateInstance相同的行为)
  • 调用已编译的lambda表达式
  • 在泛型参数类型上调用new运算符-前提是new T()编译为Activator.CreateInstance<T>()-bummer。
  • 用Reflection.Emit与null所有者一起发出的动态方法-请参阅DynamicMethod重载接受owner参数。

  • 我们未能解决此问题,但是在我们的案例中,用动态发出的构造函数(具有所有者类型)替换Activator.CreateInstance解决了我们的问题。这是一种解决方法,因为其他方法仍然存在问题。

    编辑1

    顺便说一句,我们就此问题与Microsoft支持部门联系,事实证明这完全是浪费时间。我们可以从他们那里得到的最好的是这就是事实。

    最佳答案

    也许这会有所帮助:http://ayende.com/blog/3167/creating-objects-perf-implications

    我也将尝试单独使用ConstructorInfo而不是在包装和发出构造代码之前调用Activator.CreateInstance(我不记得Activator.CreateInstance是否这样做,因为这是建议的解决方案的一部分,因此我在移动之前就已经尝试过了上)。

    关于.net - 如何使用完全空的类型使Activator.CreateInstance的运行速度慢约20倍,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/21191291/

    10-10 13:45