未验证 提交 c841f90f 编写于 作者: A Andy Gocke 提交者: GitHub

Add a perf benchmark for the compiler Emit phase (#29323)

Instructions can be found in the README file under src/Tools/CompilerBenchmarks.
上级 d72b6c55
......@@ -146,6 +146,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IlAsmDeploy", "src\Tools\IL
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NuGetProjectPackUtil", "src\NuGet\NuGetProjectPackUtil.csproj", "{B2B261E8-56EC-45DC-8AB8-A491A065EFD4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CompilerBenchmarks", "src\Tools\CompilerBenchmarks\CompilerBenchmarks.csproj", "{B446E771-AB52-41C9-ACFC-FDF8EACAF291}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems*{1ee8cad3-55f9-4d91-96b2-084641da9a6c}*SharedItemsImports = 4
......@@ -397,6 +399,10 @@ Global
{B2B261E8-56EC-45DC-8AB8-A491A065EFD4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B2B261E8-56EC-45DC-8AB8-A491A065EFD4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B2B261E8-56EC-45DC-8AB8-A491A065EFD4}.Release|Any CPU.Build.0 = Release|Any CPU
{B446E771-AB52-41C9-ACFC-FDF8EACAF291}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B446E771-AB52-41C9-ACFC-FDF8EACAF291}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B446E771-AB52-41C9-ACFC-FDF8EACAF291}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B446E771-AB52-41C9-ACFC-FDF8EACAF291}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
......@@ -464,6 +470,7 @@ Global
{E8F0BAA5-7327-43D1-9A51-644E81AE55F1} = {C65C6143-BED3-46E6-869E-9F0BE6E84C37}
{46B3E63A-C462-4133-9F27-3B85DA5E7D37} = {FD0FAF5F-1DED-485C-99FA-84B97F3A8EEC}
{B2B261E8-56EC-45DC-8AB8-A491A065EFD4} = {FD0FAF5F-1DED-485C-99FA-84B97F3A8EEC}
{B446E771-AB52-41C9-ACFC-FDF8EACAF291} = {FD0FAF5F-1DED-485C-99FA-84B97F3A8EEC}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {6F599E08-A9EA-4FAA-897F-5D824B0210E6}
......
......@@ -371,6 +371,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "InteractiveHost64", "src\In
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.VisualStudio.IntegrationTest.IntegrationService", "src\VisualStudio\IntegrationTest\IntegrationService\Microsoft.VisualStudio.IntegrationTest.IntegrationService.csproj", "{764D2C19-0187-4837-A2A3-96DDC6EF4CE2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CompilerBenchmarks", "src\Tools\CompilerBenchmarks\CompilerBenchmarks.csproj", "{9860FCF7-3111-4C12-A16F-ACEBA42D930F}"
EndProject
Global
GlobalSection(SharedMSBuildProjectFiles) = preSolution
src\Compilers\VisualBasic\BasicAnalyzerDriver\BasicAnalyzerDriver.projitems*{2523d0e6-df32-4a3e-8ae0-a19bffae2ef6}*SharedItemsImports = 4
......@@ -992,6 +994,10 @@ Global
{764D2C19-0187-4837-A2A3-96DDC6EF4CE2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{764D2C19-0187-4837-A2A3-96DDC6EF4CE2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{764D2C19-0187-4837-A2A3-96DDC6EF4CE2}.Release|Any CPU.Build.0 = Release|Any CPU
{9860FCF7-3111-4C12-A16F-ACEBA42D930F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9860FCF7-3111-4C12-A16F-ACEBA42D930F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9860FCF7-3111-4C12-A16F-ACEBA42D930F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9860FCF7-3111-4C12-A16F-ACEBA42D930F}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
......@@ -1168,6 +1174,7 @@ Global
{B2B261E8-56EC-45DC-8AB8-A491A065EFD4} = {FD0FAF5F-1DED-485C-99FA-84B97F3A8EEC}
{2F11618A-9251-4609-B3D5-CE4D2B3D3E49} = {5CA5F70E-0FDB-467B-B22C-3CD5994F0087}
{764D2C19-0187-4837-A2A3-96DDC6EF4CE2} = {CC126D03-7EAC-493F-B187-DCDEE1EF6A70}
{9860FCF7-3111-4C12-A16F-ACEBA42D930F} = {FD0FAF5F-1DED-485C-99FA-84B97F3A8EEC}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {604E6B91-7BC0-4126-AE07-D4D2FEFC3D29}
......
......@@ -11,6 +11,7 @@
<RoslynDiagnosticsNugetPackageVersion>2.6.2-beta2-63202-01</RoslynDiagnosticsNugetPackageVersion>
<BasicUndoVersion>0.9.3</BasicUndoVersion>
<BenchmarkDotNetVersion>0.11.0</BenchmarkDotNetVersion>
<EnvDTEVersion>8.0.1</EnvDTEVersion>
<EnvDTE80Version>8.0.0</EnvDTE80Version>
<dotnetxunitVersion>2.3.1</dotnetxunitVersion>
......
......@@ -84,6 +84,7 @@
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.EditorFeatures.UnitTests" />
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.CSharp.EditorFeatures.UnitTests" />
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.EditorFeatures2.UnitTests" />
<InternalsVisibleTo Include="CompilerBenchmarks" />
</ItemGroup>
<Import Project="..\CSharpAnalyzerDriver\CSharpAnalyzerDriver.projitems" Label="Shared" />
</Project>
\ No newline at end of file
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Emit
......
......@@ -103,6 +103,7 @@
<InternalsVisibleTo Include="Roslyn.Test.PdbUtilities" />
<InternalsVisibleTo Include="VBCSCompiler.UnitTests" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" Key="$(MoqPublicKey)" />
<InternalsVisibleTo Include="CompilerBenchmarks" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="CodeAnalysisResources.resx">
......
......@@ -35,6 +35,7 @@
<InternalsVisibleTo Include="InteractiveHost.UnitTests" />
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.CSharp.Scripting.UnitTests" />
<InternalsVisibleTo Include="Microsoft.CodeAnalysis.CSharp.EditorFeatures.UnitTests" />
<InternalsVisibleTo Include="CompilerBenchmarks" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="System.Runtime.Extensions" Version="$(SystemRuntimeExtensionsVersion)" />
......
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -->
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.0</TargetFramework>
<IsShipping>false</IsShipping>
</PropertyGroup>
<ItemGroup>
<None Include="README.md" />
<None Include="run-perf.ps1" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="$(BenchmarkDotNetVersion)" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj" />
<ProjectReference Include="..\..\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj" />
<ProjectReference Include="..\..\Compilers\Test\Resources\Core\Microsoft.CodeAnalysis.Compiler.Test.Resources.csproj" />
<ProjectReference Include="..\..\Compilers\Test\Utilities\CSharp\Microsoft.CodeAnalysis.CSharp.Test.Utilities.csproj" />
<ProjectReference Include="..\..\Compilers\VisualBasic\Portable\Microsoft.CodeAnalysis.VisualBasic.vbproj" />
<ProjectReference Include="..\..\Test\PdbUtilities\Roslyn.Test.PdbUtilities.csproj" />
<ProjectReference Include="..\..\Test\Utilities\Portable\Roslyn.Test.Utilities.csproj" />
</ItemGroup>
</Project>
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.IO;
using BenchmarkDotNet.Attributes;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Emit;
using Roslyn.Utilities;
using static Microsoft.CodeAnalysis.Compilation;
namespace CompilerBenchmarks
{
public class EmitBenchmark
{
public enum EmitStageSelection
{
FullEmit, // Measures Compilation.Emit
SerializeOnly // Measures just metadata serialization (internal API)
}
[Params(EmitStageSelection.FullEmit, EmitStageSelection.SerializeOnly)]
public EmitStageSelection Selection { get; set; }
private Compilation _comp;
private CommonPEModuleBuilder _moduleBeingBuilt;
private EmitOptions _options;
private MemoryStream _peStream;
[GlobalSetup]
public void GlobalSetup()
{
_peStream = new MemoryStream();
_comp = Helpers.CreateReproCompilation();
// Call GetDiagnostics to force binding to finish and most semantic analysis to be completed
_ = _comp.GetDiagnostics();
if (Selection == EmitStageSelection.SerializeOnly)
{
_options = EmitOptions.Default.WithIncludePrivateMembers(true);
bool embedPdb = _options.DebugInformationFormat == DebugInformationFormat.Embedded;
var diagnostics = DiagnosticBag.GetInstance();
_moduleBeingBuilt = _comp.CheckOptionsAndCreateModuleBuilder(
diagnostics,
manifestResources: null,
_options,
debugEntryPoint: null,
sourceLinkStream: null,
embeddedTexts: null,
testData: null,
cancellationToken: default);
bool success = false;
success = _comp.CompileMethods(
_moduleBeingBuilt,
emittingPdb: embedPdb,
emitMetadataOnly: _options.EmitMetadataOnly,
emitTestCoverageData: _options.EmitTestCoverageData,
diagnostics: diagnostics,
filterOpt: null,
cancellationToken: default);
_comp.GenerateResourcesAndDocumentationComments(
_moduleBeingBuilt,
xmlDocumentationStream: null,
win32ResourcesStream: null,
_options.OutputNameOverride,
diagnostics,
cancellationToken: default);
_comp.ReportUnusedImports(null, diagnostics, default);
_moduleBeingBuilt.CompilationFinished();
diagnostics.Free();
}
}
[Benchmark]
public object RunEmit()
{
_peStream.Position = 0;
switch (Selection)
{
case EmitStageSelection.FullEmit:
{
return _comp.Emit(_peStream);
}
case EmitStageSelection.SerializeOnly:
{
var diagnostics = DiagnosticBag.GetInstance();
_comp.SerializeToPeStream(
_moduleBeingBuilt,
new SimpleEmitStreamProvider(_peStream),
metadataPEStreamProvider: null,
pdbStreamProvider: null,
testSymWriterFactory: null,
diagnostics,
metadataOnly: _options.EmitMetadataOnly,
includePrivateMembers: _options.IncludePrivateMembers,
emitTestCoverageData: _options.EmitTestCoverageData,
pePdbFilePath: _options.PdbFilePath,
privateKeyOpt: null,
cancellationToken: default);
diagnostics.Free();
return _peStream;
}
default:
throw ExceptionUtilities.UnexpectedValue(Selection);
}
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using System.Text;
using BenchmarkDotNet.Environments;
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Toolchains;
using BenchmarkDotNet.Toolchains.CsProj;
using BenchmarkDotNet.Toolchains.DotNetCli;
namespace CompilerBenchmarks
{
public class FixedCsProjGenerator : CsProjGenerator
{
private const string Template = @"
<Project ToolsVersion=""15.0"">
<PropertyGroup>
<ImportDirectoryBuildProps>false</ImportDirectoryBuildProps>
<ImportDirectoryBuildTargets>false</ImportDirectoryBuildTargets>
</PropertyGroup>
<Import Project=""Sdk.props"" Sdk=""Microsoft.Net.Sdk"" />
<PropertyGroup>
<AssemblyTitle>$PROGRAMNAME$</AssemblyTitle>
<TargetFramework>$TFM$</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<PlatformTarget>$PLATFORM$</PlatformTarget>
<AssemblyName>$PROGRAMNAME$</AssemblyName>
<OutputType>Exe</OutputType>
<OutputPath>bin\$CONFIGURATIONNAME$</OutputPath>
<TreatWarningsAsErrors>False</TreatWarningsAsErrors>
<DebugType>pdbonly</DebugType>
<DebugSymbols>true</DebugSymbols>
$COPIEDSETTINGS$
</PropertyGroup>
<ItemGroup>
<Compile Include=""$CODEFILENAME$"" Exclude=""bin\**;obj\**;**\*.xproj;packages\**"" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include=""$CSPROJPATH$"" />
</ItemGroup>
$RUNTIMESETTINGS$
<Import Project=""Sdk.targets"" Sdk=""Microsoft.Net.Sdk"" />
</Project>";
public FixedCsProjGenerator(string targetFrameworkMoniker, Func<Platform, string> platformProvider, string runtimeFrameworkVersion = null)
: base(targetFrameworkMoniker, platformProvider, runtimeFrameworkVersion)
{ }
protected override string GetBuildArtifactsDirectoryPath(BuildPartition buildPartition, string programName)
{
string directoryName = Path.GetDirectoryName(buildPartition.AssemblyLocation)
?? throw new DirectoryNotFoundException(buildPartition.AssemblyLocation);
return Path.Combine(directoryName, programName);
}
public new static string PlatformProvider(Platform p) => p.ToString();
public static IToolchain Default => new Toolchain(
"FixedCsProjToolchain",
new FixedCsProjGenerator("netcoreapp2.0", PlatformProvider),
new DotNetCliBuilder("netcoreapp2.0"),
new DotNetCliExecutor(null));
protected override void GenerateProject(BuildPartition buildPartition, ArtifactsPaths artifactsPaths, ILogger logger)
{
string template = Template;
var benchmark = buildPartition.RepresentativeBenchmarkCase;
var projectFile = GetProjectFilePath(benchmark.Descriptor.Type, logger);
string platform = PlatformProvider(buildPartition.Platform);
string content = SetPlatform(template, platform);
content = SetCodeFileName(content, Path.GetFileName(artifactsPaths.ProgramCodePath));
content = content.Replace("$CSPROJPATH$", projectFile.FullName);
content = SetTargetFrameworkMoniker(content, TargetFrameworkMoniker);
content = content.Replace("$PROGRAMNAME$", artifactsPaths.ProgramName);
content = content.Replace("$RUNTIMESETTINGS$", GetRuntimeSettings(benchmark.Job.Environment.Gc, buildPartition.Resolver));
content = content.Replace("$COPIEDSETTINGS$", GetSettingsThatNeedsToBeCopied(projectFile));
content = content.Replace("$CONFIGURATIONNAME$", buildPartition.BuildConfiguration);
File.WriteAllText(artifactsPaths.ProjectFilePath, content);
}
// the host project or one of the .props file that it imports might contain some custom settings that needs to be copied, sth like
// <NetCoreAppImplicitPackageVersion>2.0.0-beta-001607-00</NetCoreAppImplicitPackageVersion>
// <RuntimeFrameworkVersion>2.0.0-beta-001607-00</RuntimeFrameworkVersion>
private string GetSettingsThatNeedsToBeCopied(FileInfo projectFile)
{
if (!string.IsNullOrEmpty(RuntimeFrameworkVersion)) // some power users knows what to configure, just do it and copy nothing more
return $"<RuntimeFrameworkVersion>{RuntimeFrameworkVersion}</RuntimeFrameworkVersion>";
var customSettings = new StringBuilder();
using (var file = new StreamReader(File.OpenRead(projectFile.FullName)))
{
string line;
while ((line = file.ReadLine()) != null)
{
if (line.Contains("NetCoreAppImplicitPackageVersion") || line.Contains("RuntimeFrameworkVersion") || line.Contains("PackageTargetFallback") || line.Contains("LangVersion"))
{
customSettings.Append(line);
}
else if (line.Contains("<Import Project"))
{
string propsFilePath = line.Trim().Split('"')[1]; // its sth like <Import Project="..\..\build\common.props" />
var directoryName = projectFile.DirectoryName ?? throw new DirectoryNotFoundException(projectFile.DirectoryName);
string absolutePath = File.Exists(propsFilePath)
? propsFilePath // absolute path or relative to current dir
: Path.Combine(directoryName, propsFilePath); // relative to csproj
if (File.Exists(absolutePath))
{
customSettings.Append(GetSettingsThatNeedsToBeCopied(new FileInfo(absolutePath)));
}
}
}
}
return customSettings.ToString();
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
namespace CompilerBenchmarks
{
internal static class Helpers
{
public const string TestProjectEnvVarName = "ROSLYN_TEST_PROJECT_DIR";
public static Compilation CreateReproCompilation()
{
var projectDir = Environment.GetEnvironmentVariable(TestProjectEnvVarName);
var cmdLineParser = new CSharpCommandLineParser();
var responseFile = Path.Combine(projectDir, "repro.rsp");
var compiler = new MockCSharpCompiler(responseFile, projectDir, Array.Empty<string>());
var output = new StringWriter();
return compiler.CreateCompilation(output, null, null);
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using System.Linq;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Validators;
namespace CompilerBenchmarks
{
public class Program
{
private class IgnoreReleaseOnly : ManualConfig
{
public IgnoreReleaseOnly()
{
Add(JitOptimizationsValidator.DontFailOnError);
Add(DefaultConfig.Instance.GetLoggers().ToArray());
Add(DefaultConfig.Instance.GetExporters().ToArray());
Add(DefaultConfig.Instance.GetColumnProviders().ToArray());
Add(new Job { Infrastructure = { Toolchain = FixedCsProjGenerator.Default } });
}
}
public static void Main(string[] args)
{
var projectPath = args[0];
var artifactsPath = Path.Combine(projectPath, "../BenchmarkDotNet.Artifacts");
var config = new IgnoreReleaseOnly();
var artifactsDir = Directory.CreateDirectory(artifactsPath);
config.ArtifactsPath = artifactsDir.FullName;
// Benchmark.NET creates a new process to run the benchmark, so the easiest way
// to communicate information is pass by environment variable
Environment.SetEnvironmentVariable(Helpers.TestProjectEnvVarName, projectPath);
_ = BenchmarkRunner.Run<EmitBenchmark>(config);
}
}
}

This project contains a set of "micro" benchmarks for the compiler, focused on measuring
the time spent in specific phases of the compiler, e.g. Emit, metadata serialization, binding,
parsing, etc.
To run all benchmarks, simply run the `run-perf.ps1` file on your machine, which should produce
a simple output table containing the results. To compare the results of your change, you can
run the script before and after and attempt to compare the results. Calculating statistical
significance is beyond the scope of this document, but you can get a general idea of whether
or not your changes are significant if the different is substantially larger than the "error"
value reported in the results summary.
\ No newline at end of file
# Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
[CmdletBinding(PositionalBinding=$false)]
param (
# By default, the Roslyn dir is expected to be next to this dir
[string]$roslynDir = "$PSScriptRoot/../../.."
)
Set-Variable -Name LastExitCode 0
Set-StrictMode -Version 2.0
$ErrorActionPreference = "Stop"
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
try {
. (Join-Path $roslynDir "build/scripts/build-utils.ps1")
# Download dotnet if it isn't already available
Ensure-DotnetSdk
$reproPath = Join-Path $binariesDir "CodeAnalysisRepro"
if (-not (Test-Path $reproPath)) {
$tmpFile = [System.IO.Path]::GetTempFileName()
Invoke-WebRequest -Uri "https://roslyninfra.blob.core.windows.net/perf-artifacts/CodeAnalysisRepro.zip" -UseBasicParsing -OutFile $tmpFile
[Reflection.Assembly]::LoadWithPartialName('System.IO.Compression.FileSystem') | Out-Null
[IO.Compression.ZipFile]::ExtractToDirectory($tmpFile, $binariesDir)
}
Exec-Command "dotnet" "run -c Release $reproPath"
}
catch {
Write-Host $_
Write-Host $_.Exception
exit 1
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册