一个极度降低Activator.CreateInstance性能的案例

时间:2014-01-15 19:25:41

标签: .net performance .net-4.5

我们在.NET服务器端应用程序中观察到一种有趣的行为。

CPU /内存限制工作随着时间的推移而变慢。我们使用PerfView来尝试找出罪魁祸首,似乎Activator.CreateInstance就是那个。

我们是如何找到它的?我们在新会议开始时和执行大约3,000份报告后执行相同的工作。在第一种情况下,PerfView甚至没有显示Activator.CreateInstance - 它的百分比太接近0%(但可以肯定它被称为)。对于第二种情况,它显示了28%。

那么,与Activator.CreateInstance的交易是什么?

我们正在使用.NET 4.5

修改

我们使用的是Activator.CreateInstance的默认构建版本。

编辑2

一个重要的发展。我们已经设法将案例缩小到使用Microsoft报告框架并启用NetFx40_LegacySecurityPolicy

事实上,在生成一个小报告之前和之后,我们有一个小的测试用例调用Activator.CreateInstance。请在下面找到输出:

new() = 4, Activator.CreateInstance() = 38
Building one report ... done.
new() = 13, Activator.CreateInstance() = 2261

这意味着在生成报告之前调用Activator.CreateInstance() 500,000次只需要38毫秒。 在微小的报告之后它花费了59倍

以下是整个代码:

Program.cs的

using System;
using System.Diagnostics;
using System.IO;
using Microsoft.Reporting.WebForms;

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);

            Console.Write("Building one report ... ");
            Console.Out.Flush();
            RunReport();
            Console.WriteLine("done.");

            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 RunReport()
        {
            var localReport = new LocalReport();
            localReport.LoadReportDefinition(new StringReader(
                "<?xml version=\"1.0\" encoding=\"utf-8\"?>"+
                    "<Report xmlns=\"http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition\">"+
                    "  <Body>"+
                    "    <Height>1in</Height>"+
                    "  </Body>"+
                    "  <Width>1in</Width>"+
                    "  <Page>"+
                    "    <PageFooter>"+
                    "      <Height>1in</Height>"+
                    "      <ReportItems>"+
                    "        <Textbox Name=\"OverallTotalPages\">"+
                    "          <Paragraphs>"+
                    "            <Paragraph>"+
                    "              <TextRuns>"+
                    "                <TextRun>"+
                    "                  <Value>=Globals!OverallTotalPages</Value>"+
                    "                </TextRun>"+
                    "              </TextRuns>"+
                    "            </Paragraph>"+
                    "          </Paragraphs>"+
                    "        </Textbox>"+
                    "      </ReportItems>"+
                    "    </PageFooter>"+
                    "  </Page>"+
                    "</Report>"
                ));
            localReport.Render("pdf");
       }
    }
}

的App.config

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

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="Microsoft.ReportViewer.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
    <Reference Include="Microsoft.ReportViewer.WebForms, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
    <Reference Include="System" />
    <Reference Include="System.Core" />
  </ItemGroup>
  <ItemGroup>
    <Compile Include="Program.cs" />
  </ItemGroup>
  <ItemGroup>
    <None Include="App.config">
      <SubType>Designer</SubType>
    </None>
  </ItemGroup>
  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

注意,在配置文件中禁用NetFx40_LegacySecurityPolicy会产生奇迹:

new() = 7, Activator.CreateInstance() = 106
Building one report ... done.
new() = 7, Activator.CreateInstance() = 78

不幸的是,由于其他原因,我们仍然遇到NetFx40_LegacySecurityPolicy,所以禁用它不是一种选择。

欢迎任何意见。

编辑3

How to make Activator.CreateInstance run about 20 times slower with a completely empty type

0 个答案:

没有答案