aspnetcore/benchmarks/Microsoft.AspNetCore.Mvc.Pe.../RuntimePerformanceBenchmark...

213 lines
8.3 KiB
C#

// Copyright (c) .NET Foundation. 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.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Mvc.ViewEngines;
using Microsoft.AspNetCore.Mvc.ViewFeatures;
using Microsoft.AspNetCore.Razor.Hosting;
using Microsoft.AspNetCore.Routing;
using Microsoft.CodeAnalysis;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.FileProviders;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.ObjectPool;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Mvc.Performance
{
public class RuntimePerformanceBenchmarkBase
{
private class NullLoggerFactory : ILoggerFactory, ILogger
{
void ILoggerFactory.AddProvider(ILoggerProvider provider) {}
ILogger ILoggerFactory.CreateLogger(string categoryName) => this;
void IDisposable.Dispose() {}
IDisposable ILogger.BeginScope<TState>(TState state) => null;
bool ILogger.IsEnabled(LogLevel logLevel) => false;
void ILogger.Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) {}
}
private class BenchmarkViewExecutor : ViewExecutor
{
public BenchmarkViewExecutor(IOptions<MvcViewOptions> viewOptions, IHttpResponseStreamWriterFactory writerFactory, ICompositeViewEngine viewEngine, ITempDataDictionaryFactory tempDataFactory, DiagnosticListener diagnosticListener, IModelMetadataProvider modelMetadataProvider)
: base(viewOptions, writerFactory, viewEngine, tempDataFactory, diagnosticListener, modelMetadataProvider)
{
}
public StringBuilder StringBuilder { get; } = new StringBuilder();
public override async Task ExecuteAsync(
ActionContext actionContext,
IView view,
ViewDataDictionary viewData,
ITempDataDictionary tempData,
string contentType,
int? statusCode)
{
using (var stringWriter = new StringWriter(StringBuilder))
{
var viewContext = new ViewContext(
actionContext,
view,
viewData,
tempData,
stringWriter,
ViewOptions.HtmlHelperOptions);
await ExecuteAsync(viewContext, contentType, statusCode);
await stringWriter.FlushAsync();
}
}
}
private class BenchmarkHostingEnvironment : IHostingEnvironment
{
public BenchmarkHostingEnvironment()
{
ApplicationName = typeof(ViewAssemblyMarker).Assembly.FullName;
WebRootFileProvider = new NullFileProvider();
ContentRootFileProvider = new NullFileProvider();
ContentRootPath = AppContext.BaseDirectory;
WebRootPath = AppContext.BaseDirectory;
}
public string EnvironmentName { get; set; }
public string ApplicationName { get; set; }
public string WebRootPath { get; set; }
public IFileProvider WebRootFileProvider { get; set; }
public string ContentRootPath { get; set; }
public IFileProvider ContentRootFileProvider { get; set; }
}
protected RuntimePerformanceBenchmarkBase(params string[] viewPaths)
{
ViewPaths = viewPaths;
}
public virtual string[] ViewPaths { get; private set; }
[ParamsSource(nameof(ViewPaths))]
public string ViewPath;
protected IView View;
private ServiceProvider _serviceProvider;
private RouteData _routeData;
private ActionDescriptor _actionDescriptor;
private IServiceScope _requestScope;
private ICompositeViewEngine _viewEngine;
private BenchmarkViewExecutor _executor;
private ViewEngineResult _viewEngineResult;
private ActionContext _actionContext;
private ViewDataDictionary _viewDataDictionary;
private ITempDataDictionaryFactory _tempDataDictionaryFactory;
private ITempDataDictionary _tempData;
// runs once for every Document value
[GlobalSetup]
public void GlobalSetup()
{
var loader = new RazorCompiledItemLoader();
var viewsDll = Path.ChangeExtension(typeof(ViewAssemblyMarker).Assembly.Location, "Views.dll");
var viewsAssembly = Assembly.Load(File.ReadAllBytes(viewsDll));
var services = new ServiceCollection();
var listener = new DiagnosticListener(GetType().Assembly.FullName);
var partManager = new ApplicationPartManager();
partManager.ApplicationParts.Add(CompiledRazorAssemblyApplicationPartFactory.GetDefaultApplicationParts(viewsAssembly).Single());
var builder = services
.AddSingleton<ILoggerFactory, NullLoggerFactory>()
.AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>()
.AddSingleton<DiagnosticSource>(listener)
.AddSingleton(listener)
.AddSingleton<IHostingEnvironment, BenchmarkHostingEnvironment>()
.AddSingleton<ApplicationPartManager>(partManager)
.AddScoped<BenchmarkViewExecutor>()
.AddMvc();
_serviceProvider = services.BuildServiceProvider();
_routeData = new RouteData();
_actionDescriptor = new ActionDescriptor();
_tempDataDictionaryFactory = _serviceProvider.GetRequiredService<ITempDataDictionaryFactory>();
_viewEngine = _serviceProvider.GetRequiredService<ICompositeViewEngine>();
}
[GlobalCleanup]
public void GlobalCleanup()
{
_serviceProvider.Dispose();
}
[IterationSetup]
public virtual void IterationSetup()
{
_requestScope = _serviceProvider.CreateScope();
_viewEngineResult = _viewEngine.GetView(null, ViewPath, true);
_viewEngineResult.EnsureSuccessful(null);
_actionContext = new ActionContext(
new DefaultHttpContext()
{
RequestServices = _requestScope.ServiceProvider
},
_routeData,
_actionDescriptor);
_tempData = _tempDataDictionaryFactory.GetTempData(_actionContext.HttpContext);
_viewDataDictionary = new ViewDataDictionary(
_requestScope.ServiceProvider.GetRequiredService<IModelMetadataProvider>(),
_actionContext.ModelState);
_viewDataDictionary.Model = Model;
_executor = _requestScope.ServiceProvider.GetRequiredService<BenchmarkViewExecutor>();
}
[IterationCleanup]
public virtual void IterationCleanup()
{
if (_viewEngineResult.View is IDisposable d)
{
d.Dispose();
}
_requestScope.Dispose();
}
protected virtual object Model { get; } = null;
[Benchmark]
public async Task<string> RenderView()
{
await _executor.ExecuteAsync(
_actionContext,
_viewEngineResult.View,
_viewDataDictionary,
_tempData,
"text/html",
200);
return _executor.StringBuilder.ToString();
}
}
}