本文介绍了如何使Activator.CreateInstance运行约20倍,完全空的类型慢的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

由于:

  • 在.NET程序集名为 EX pression_host
  • 名为.NET程序集 CreateInstanceTest
  • CreateInstanceTest使NetFx40_LegacySecurityPolicy在其配置文件
  • EX pression_host是由于与的SecurityPermission(SecurityAction.RequestOptional)
  • CreateInstanceTest加载EX pression_host总成 - 砰! - Activator.CreateInstance 祝酒
  • .NET assembly named expression_host
  • .NET assembly named CreateInstanceTest
  • CreateInstanceTest enables NetFx40_LegacySecurityPolicy in its config file
  • expression_host is attributed with SecurityPermission(SecurityAction.RequestOptional)
  • CreateInstanceTest loads the expression_host assembly - bang!!! - Activator.CreateInstance is toast

请,观察输出:

new() = 13, Activator.CreateInstance() = 111
Just loaded expression_host, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
new() = 6, Activator.CreateInstance() = 1944

说明:

  1. 程序没有50万次新的TestClass() 50万次 Activator.CreateInstance(typeof运算(TestClass中))
  2. 然后它加载前pression_host组装
  3. 然后重复步骤1。
  4. 的数字是毫秒。
  1. The program does 500,000 times new TestClass() and 500,000 times Activator.CreateInstance(typeof(TestClass))
  2. Then it loads the expression_host assembly
  3. Then it repeats the step 1.
  4. The numbers are milliseconds.

这两个组件是非常小的。这里是code:

Both assemblies are really small. Here is the code:

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>

EX pression_host

EX pression_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案件Activator.CreateInstance效果糟糕降解

Now what is the question? The question is how to deal with it. This is actually the most minimal reproduction of a real life case and is related to A case of abysmal degradation of Activator.CreateInstance performance

在实际应用中,我们要运行报告,我们坚持与 NetFx40_LegacySecurityPolicy ,这使得我们的应用程序的某些部分进行可怕的。

In the real application we have to run reports and we are stuck with NetFx40_LegacySecurityPolicy, which makes certain parts of our application perform terrible.

最后,在实际应用中,我们不能改变前pression主机组件的code,因为这是由微软报告框架动态生成的。

Finally, in the real application we cannot change the code of the expression host assemblies, because these are built dynamically by the Microsoft reporting framework.

所以,我们能做些什么?

So what can we do?

修改

添加 [总成:SecurityTransparent] 的CreateInstanceTest装配似乎改善的事情:

Adding [assembly: SecurityTransparent] to the CreateInstanceTest assembly seems to improve things:

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倍慢!

Now Activator.CreateInstance is only about 9 times slower, rather than 20. But still, 9 times slower!

编辑2

这似乎是一个内部.NET的事情。托管分析器(蚂蚁)是不是非常有用。

This seems to be an internal .NET thing. Managed profiler (ANTS) was not very useful.

其他受影响的方法:

  • MethodInfo.Invoke (相同的行为 Activator.CreateInstance
  • 在调用编译拉姆达EX pression
  • 电话在一般的参数类型新运营商 - becase的新T()被编译为 Activator.CreateInstance&LT; T&GT;() - 无赖
  • 在发出与Reflection.Emit的与动态方法老板 - 看 DynamicMethod的超负荷接受所有者参数。
  • MethodInfo.Invoke (same behavior as Activator.CreateInstance)
  • Invoking a compiled lambda expression
  • Calling new operator on a generic argument type - becase new T() is compiled to Activator.CreateInstance<T>() - bummer.
  • Dynamic method emitted with Reflection.Emit with null owner - see the DynamicMethod overload accepting the owner parameter.

我们没能解决这个问题,但在我们的情况下,具有动态发射的构造(与店主型)替换 Activator.CreateInstance 解决我们的问题。这是一个解决办法,因为这个问题仍然是其他方法。

We failed to solve this issue, but in our case replacing Activator.CreateInstance with a dynamically emitted constructor (with the owner type) solved our problem. It is a workaround, since the problem remains for other methods.

修改1

顺便说一句,我们联系了在这个问题上,这竟然是完全是浪费时间Microsoft支持。我们可以从他们那里得到最好的是,这​​是怎么回事。

BTW, we contacted Microsoft support on this issue, which turned out to be a complete waste of time. The best we could get from them is that this is how it is.

推荐答案

或许,这将有助于:的

Perhaps this would help: http://ayende.com/blog/3167/creating-objects-perf-implications

我也尝试使用 ConstructorInfo 自身而不是调用 Activator.CreateInstance 包装,并发射前建设code(我不记得了,如果这就是 Activator.CreateInstance 做还是不做,还是因为它是推荐的解决方案的一部分,我会试图将其移动之前)。

I would also try using the ConstructorInfo on its own instead of calling Activator.CreateInstance before wrapping it and emitting construction code (I don't remember if that's what Activator.CreateInstance do or not, still since it's part of the suggested solution I would have tried it before moving on).

这篇关于如何使Activator.CreateInstance运行约20倍,完全空的类型慢的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-14 13:26