问题描述
由于:
- 在.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
说明:
- 程序没有50万次
新的TestClass()
50万次Activator.CreateInstance(typeof运算(TestClass中))
- 然后它加载前pression_host组装
- 然后重复步骤1。
- 的数字是毫秒。
- The program does 500,000 times
new TestClass()
and 500,000 timesActivator.CreateInstance(typeof(TestClass))
- Then it loads the expression_host assembly
- Then it repeats the step 1.
- 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 asActivator.CreateInstance
)- Invoking a compiled lambda expression
- Calling
new
operator on a generic argument type - becasenew T()
is compiled toActivator.CreateInstance<T>()
- bummer. - Dynamic method emitted with Reflection.Emit with
null
owner - see theDynamicMethod
overload accepting theowner
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倍,完全空的类型慢的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!