由於運行約20倍慢:如何使Activator.CreateInstance一個完全空型
- .NET程序集命名爲
expression_host
- 命名爲
CreateInstanceTest
- CreateInstanceTest .NET組件使得NetFx40_LegacySecurityPolicy在其配置文件
- expression_host歸功於
SecurityPermission(SecurityAction.RequestOptional)
- CreateInstanceTest加載expression_host程序集 - 砰! -
Activator.CreateInstance
祝酒
請觀察輸出:
new() = 13, Activator.CreateInstance() = 111
Just loaded expression_host, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
new() = 6, Activator.CreateInstance() = 1944
說明:
- 程序沒有50萬次
new TestClass()
和50萬次Activator.CreateInstance(typeof(TestClass))
- 然後加載expression_host組裝
- Th en重複步驟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倍!
EDIT 2
這似乎是一個內部.NET的事情。託管分析器(ANTS)不是很有用。
其他受影響的方法:
MethodInfo.Invoke
(相同的行爲Activator.CreateInstance
)- 調用編譯lambda表達式
- 調用上通用的參數類型
new
運營商 - becase的new T()
被編譯成Activator.CreateInstance<T>()
- 無賴。 - 用Reflection.Emit與
null
所有者一起發射的動態方法 - 請參閱DynamicMethod
過載接受owner
參數。
我們未能解決這個問題,但在我們的案例中用動態發出的構造函數(擁有者類型)替換Activator.CreateInstance
解決了我們的問題。這是一種解決方法,因爲問題仍然存在於其他方法中。
編輯1
順便說一句,我們在這個問題上,這竟然是完全是浪費時間聯繫了微軟的支持。我們從他們身上得到的最好的結果就是這樣。
有趣。嘗試分析代碼。如果通常的.NET分析器太高級嘗試perfview。這應該隱藏什麼。 – usr
結果是最令人困惑,沒有多大意義。 PerfView不夠好,我們嘗試了ANTS分析器。我們仍在挖掘它。 – mark
這似乎是一個內部.NET的事情 - 管理的分析器不是很有用。 – mark