diff --git a/src/BenchmarkDotNet.Annotations/Jobs/RuntimeMoniker.cs b/src/BenchmarkDotNet.Annotations/Jobs/RuntimeMoniker.cs index 81a2431629..ccca5840e9 100644 --- a/src/BenchmarkDotNet.Annotations/Jobs/RuntimeMoniker.cs +++ b/src/BenchmarkDotNet.Annotations/Jobs/RuntimeMoniker.cs @@ -248,5 +248,15 @@ public enum RuntimeMoniker /// .NET 11 using MonoVM (not CLR which is the default) /// Mono11_0, + + /// + /// .NET 10 CLR with composite ReadyToRun compilation + /// + CompositeR2R10_0, + + /// + /// .NET 11 CLR with composite ReadyToRun compilation + /// + CompositeR2R11_0, } } diff --git a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs index 8430b907b6..1025c4e431 100644 --- a/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs +++ b/src/BenchmarkDotNet.Diagnostics.dotMemory/DotMemoryDiagnoser.cs @@ -83,6 +83,8 @@ internal override bool IsSupported(RuntimeMoniker runtimeMoniker) case RuntimeMoniker.Net90: case RuntimeMoniker.Net10_0: case RuntimeMoniker.Net11_0: + case RuntimeMoniker.CompositeR2R10_0: + case RuntimeMoniker.CompositeR2R11_0: return true; case RuntimeMoniker.NotRecognized: case RuntimeMoniker.Mono: diff --git a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs b/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs index bea8894868..6b30646998 100644 --- a/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs +++ b/src/BenchmarkDotNet.Diagnostics.dotTrace/DotTraceDiagnoser.cs @@ -86,6 +86,8 @@ internal override bool IsSupported(RuntimeMoniker runtimeMoniker) case RuntimeMoniker.Net90: case RuntimeMoniker.Net10_0: case RuntimeMoniker.Net11_0: + case RuntimeMoniker.CompositeR2R10_0: + case RuntimeMoniker.CompositeR2R11_0: return true; case RuntimeMoniker.NotRecognized: case RuntimeMoniker.Mono: diff --git a/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs b/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs index b8a3866ffe..86b1d7046d 100644 --- a/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs +++ b/src/BenchmarkDotNet/ConsoleArguments/ConfigParser.cs @@ -18,6 +18,7 @@ using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Portability; using BenchmarkDotNet.Reports; +using BenchmarkDotNet.Toolchains.CompositeR2R; using BenchmarkDotNet.Toolchains.CoreRun; using BenchmarkDotNet.Toolchains.CsProj; using BenchmarkDotNet.Toolchains.DotNetCli; @@ -641,6 +642,10 @@ private static Job CreateJobForGivenRuntime(Job baseJob, string runtimeId, Comma case RuntimeMoniker.Mono11_0: return MakeMonoJob(baseJob, options, MonoRuntime.Mono11_0); + case RuntimeMoniker.CompositeR2R10_0: + case RuntimeMoniker.CompositeR2R11_0: + return CreateCompositeR2RJob(baseJob, options, runtimeMoniker.GetRuntime()); + default: throw new NotSupportedException($"Runtime {runtimeId} is not supported"); } @@ -703,6 +708,21 @@ private static Job MakeMonoAOTLLVMJob(Job baseJob, CommandLineOptions options, s return baseJob.WithRuntime(monoAotLLVMRuntime).WithToolchain(toolChain).WithId(monoAotLLVMRuntime.Name); } + private static Job CreateCompositeR2RJob(Job baseJob, CommandLineOptions options, Runtime runtime) + { + var toolChain = CompositeR2RToolchain.From( + new NetCoreAppSettings( + targetFrameworkMoniker: runtime.MsBuildMoniker, + runtimeFrameworkVersion: null, + name: $"CompositeR2R {runtime.Name}", + customDotNetCliPath: options.CliPath?.FullName, + packagesPath: options.RestorePath?.FullName, + customRuntimePack: options.CustomRuntimePack, + aotCompilerPath: options.AOTCompilerPath != null ? options.AOTCompilerPath.ToString() : null)); + + return baseJob.WithRuntime(runtime).WithToolchain(toolChain).WithId("CompositeR2R"); + } + private static Job MakeWasmJob(Job baseJob, CommandLineOptions options, string msBuildMoniker, RuntimeMoniker moniker) { bool wasmAot = options.AOTCompilerMode == MonoAotCompilerMode.wasm; @@ -827,4 +847,4 @@ internal static bool TryParse(string runtime, out RuntimeMoniker runtimeMoniker) return Enum.TryParse(runtime.Replace('.', '_'), ignoreCase: true, out runtimeMoniker); } } -} \ No newline at end of file +} diff --git a/src/BenchmarkDotNet/Extensions/RuntimeMonikerExtensions.cs b/src/BenchmarkDotNet/Extensions/RuntimeMonikerExtensions.cs index 1dad83d690..e3a3847695 100644 --- a/src/BenchmarkDotNet/Extensions/RuntimeMonikerExtensions.cs +++ b/src/BenchmarkDotNet/Extensions/RuntimeMonikerExtensions.cs @@ -74,6 +74,10 @@ internal static Runtime GetRuntime(this RuntimeMoniker runtimeMoniker) return MonoRuntime.Mono10_0; case RuntimeMoniker.Mono11_0: return MonoRuntime.Mono11_0; + case RuntimeMoniker.CompositeR2R10_0: + return CoreRuntime.Core10_0; + case RuntimeMoniker.CompositeR2R11_0: + return CoreRuntime.Core11_0; default: throw new ArgumentOutOfRangeException(nameof(runtimeMoniker), runtimeMoniker, "Runtime Moniker not supported"); } diff --git a/src/BenchmarkDotNet/Templates/CompositeR2RCsProj.txt b/src/BenchmarkDotNet/Templates/CompositeR2RCsProj.txt new file mode 100644 index 0000000000..7a2126c940 --- /dev/null +++ b/src/BenchmarkDotNet/Templates/CompositeR2RCsProj.txt @@ -0,0 +1,69 @@ + + + + + false + false + $PROGRAMNAME$ + $TFM$ + true + $PLATFORM$ + $PROGRAMNAME$ + Exe + False + + false + + false + false + + false + true + + true + + latest + + false + + true + BenchmarkDotNet.Autogenerated.UniqueProgramName + + + + true + $RUNTIMEIDENTIFIER$ + true + true + + + + + + + + + + + + + + + + + $COPIEDSETTINGS$ + + + + + + + + $RUNTIMEPACK$ + + + $CROSSGEN2PACK$ + + + + diff --git a/src/BenchmarkDotNet/Toolchains/CompositeR2R/CompositeR2RGenerator.cs b/src/BenchmarkDotNet/Toolchains/CompositeR2R/CompositeR2RGenerator.cs new file mode 100644 index 0000000000..d12fa79253 --- /dev/null +++ b/src/BenchmarkDotNet/Toolchains/CompositeR2R/CompositeR2RGenerator.cs @@ -0,0 +1,62 @@ +using System.IO; +using System.Text; +using System.Xml; +using BenchmarkDotNet.Detectors; +using BenchmarkDotNet.Extensions; +using BenchmarkDotNet.Helpers; +using BenchmarkDotNet.Loggers; +using BenchmarkDotNet.Running; +using BenchmarkDotNet.Toolchains.CsProj; +using BenchmarkDotNet.Toolchains.DotNetCli; + +namespace BenchmarkDotNet.Toolchains.CompositeR2R +{ + public class CompositeR2RGenerator : CsProjGenerator + { + private readonly string CustomRuntimePack; + private readonly string Crossgen2Pack; + + public CompositeR2RGenerator(string targetFrameworkMoniker, string cliPath, string packagesPath, string customRuntimePack, string crossgen2Pack) + : base(targetFrameworkMoniker, cliPath, packagesPath, runtimeFrameworkVersion: null) + { + CustomRuntimePack = customRuntimePack; + Crossgen2Pack = crossgen2Pack; + BenchmarkRunCallType = Code.CodeGenBenchmarkRunCallType.Direct; + } + + protected override void GenerateProject(BuildPartition buildPartition, ArtifactsPaths artifactsPaths, ILogger logger) + { + BenchmarkCase benchmark = buildPartition.RepresentativeBenchmarkCase; + var projectFile = GetProjectFilePath(benchmark.Descriptor.Type, logger); + + var xmlDoc = new XmlDocument(); + xmlDoc.Load(projectFile.FullName); + var (customProperties, sdkName) = GetSettingsThatNeedToBeCopied(xmlDoc, projectFile); + + string content = new StringBuilder(ResourceHelper.LoadTemplate("CompositeR2RCsProj.txt")) + .Replace("$PLATFORM$", buildPartition.Platform.ToConfig()) + .Replace("$CODEFILENAME$", Path.GetFileName(artifactsPaths.ProgramCodePath)) + .Replace("$CSPROJPATH$", projectFile.FullName) + .Replace("$TFM$", TargetFrameworkMoniker) + .Replace("$PROGRAMNAME$", artifactsPaths.ProgramName) + .Replace("$COPIEDSETTINGS$", customProperties) + .Replace("$SDKNAME$", sdkName) + .Replace("$RUNTIMEPACK$", CustomRuntimePack) + .Replace("$CROSSGEN2PACK$", Crossgen2Pack) + .Replace("$RUNTIMEIDENTIFIER$", CustomDotNetCliToolchainBuilder.GetPortableRuntimeIdentifier()) + .ToString(); + + File.WriteAllText(artifactsPaths.ProjectFilePath, content); + + GatherReferences(buildPartition, artifactsPaths, logger); + } + + protected override string GetExecutablePath(string binariesDirectoryPath, string programName) + => OsDetector.IsWindows() + ? Path.Combine(binariesDirectoryPath, $"{programName}.exe") + : Path.Combine(binariesDirectoryPath, programName); + + protected override string GetBinariesDirectoryPath(string buildArtifactsDirectoryPath, string configuration) + => Path.Combine(buildArtifactsDirectoryPath, "bin", configuration, TargetFrameworkMoniker, CustomDotNetCliToolchainBuilder.GetPortableRuntimeIdentifier(), "publish"); + } +} diff --git a/src/BenchmarkDotNet/Toolchains/CompositeR2R/CompositeR2RToolchain.cs b/src/BenchmarkDotNet/Toolchains/CompositeR2R/CompositeR2RToolchain.cs new file mode 100644 index 0000000000..33f02c882c --- /dev/null +++ b/src/BenchmarkDotNet/Toolchains/CompositeR2R/CompositeR2RToolchain.cs @@ -0,0 +1,44 @@ +using BenchmarkDotNet.Characteristics; +using BenchmarkDotNet.Running; +using BenchmarkDotNet.Toolchains.CsProj; +using BenchmarkDotNet.Toolchains.DotNetCli; +using BenchmarkDotNet.Validators; +using JetBrains.Annotations; +using System; +using System.Collections.Generic; + +namespace BenchmarkDotNet.Toolchains.CompositeR2R +{ + [PublicAPI] + public class CompositeR2RToolchain : CsProjCoreToolchain, IEquatable + { + private readonly string _customDotNetCliPath; + private CompositeR2RToolchain(string name, IGenerator generator, IBuilder builder, IExecutor executor, string customDotNetCliPath) + : base(name, generator, builder, executor, customDotNetCliPath) + { + _customDotNetCliPath = customDotNetCliPath; + } + + [PublicAPI] + public static new IToolchain From(NetCoreAppSettings settings) + => new CompositeR2RToolchain(settings.Name, + new CompositeR2RGenerator(settings.TargetFrameworkMoniker, settings.CustomDotNetCliPath, settings.PackagesPath, settings.CustomRuntimePack, settings.AOTCompilerPath), + new DotNetCliPublisher(settings.TargetFrameworkMoniker, settings.CustomDotNetCliPath), + new Executor(), + settings.CustomDotNetCliPath); + + public override IEnumerable Validate(BenchmarkCase benchmarkCase, IResolver resolver) + { + foreach (var validationError in DotNetSdkValidator.ValidateCoreSdks(_customDotNetCliPath, benchmarkCase)) + { + yield return validationError; + } + } + + public override bool Equals(object obj) => obj is CompositeR2RToolchain typed && Equals(typed); + + public bool Equals(CompositeR2RToolchain other) => Generator.Equals(other.Generator); + + public override int GetHashCode() => Generator.GetHashCode(); + } +}