Merge Perf => Dev

--HG--
branch : dev
This commit is contained in:
Renaud Paquay 2010-12-02 22:56:46 -08:00
commit 40924a0f73
12 changed files with 382 additions and 16 deletions

View File

@ -1 +1 @@
setup /SiteName:Profiling /AdminUsername:admin /AdminPassword:profiling-secret /DatabaseProvider:SqlCe /EnabledFeatures:Profiling,Orchard.Framework,Routable,Common,Dashboard,Feeds,Orchard.PublishLater,HomePage,Contents,Navigation,Reports,Scheduling,Indexing,Settings,Localization,XmlRpc,Orchard.Users,Orchard.Roles,TinyMce,Orchard.Themes,Orchard.MultiTenancy,Orchard.Blogs,Orchard.Comments,Orchard.Modules,Orchard.Scripting,Orchard.Scripting.Lightweight,Orchard.Widgets,Orchard.Media,Orchard.Tags,Orchard.Experimental
setup /SiteName:Profiling /AdminUsername:admin /AdminPassword:profiling-secret /DatabaseProvider:SQLServer /DatabaseConnectionString:"Data Source=.;Initial Catalog=Orchard;Integrated Security=True" /EnabledFeatures:Profiling,Orchard.Framework,Common,Containers,Contents,Dashboard,Feeds,HomePage,Navigation,Reports,Routable,Scheduling,Settings,Shapes,Orchard.PublishLater,Orchard.Blogs,Orchard.Comments,Orchard.ContentTypes,Orchard.jQuery,Orchard.Lists,Orchard.Media,Orchard.Modules,Orchard.Pages,Orchard.Roles,Orchard.Tags,Orchard.Themes,Orchard.Users,Orchard.Scripting,Orchard.Scripting.Lightweight,Orchard.Widgets,TinyMce,TheThemeMachine

View File

@ -0,0 +1,311 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Xml.Linq;
using Autofac;
using Autofac.Core;
namespace Orchard.Experimental {
// note - not thread aware
public interface IContainerSpyOutput {
void Write(XElement target);
}
public class ContainerSpy : Module, IDependency {
private readonly ConcurrentDictionary<int, ThreadContext> _contexts = new ConcurrentDictionary<int, ThreadContext>();
protected override void Load(ContainerBuilder builder) {
builder.RegisterInstance(new Output(_contexts)).As<IContainerSpyOutput>();
//builder.RegisterCallback(cr => cr.Registered += (_, registered) => {
// registered.ComponentRegistration.Preparing += (__, preparing) => Preparing(GetContext(_contexts), preparing);
// registered.ComponentRegistration.Activating += (__, activating) => Activating(GetContext(_contexts), activating);
// registered.ComponentRegistration.Activated += (__, activated) => Activated(GetContext(_contexts), activated);
//});
}
protected override void AttachToComponentRegistration(IComponentRegistry componentRegistry, IComponentRegistration registration) {
registration.Preparing += (__, preparing) => Preparing(GetContext(_contexts), preparing);
registration.Activating += (__, activating) => Activating(GetContext(_contexts), activating);
registration.Activated += (__, activated) => Activated(GetContext(_contexts), activated);
}
static ThreadContext GetContext(ConcurrentDictionary<int, ThreadContext> nodes) {
return nodes.GetOrAdd(Thread.CurrentThread.ManagedThreadId, _ => {
var tc = new ThreadContext();
tc.Root = tc.Clock = tc.Chain = tc.Focus = new Node(tc);
return tc;
});
}
private static void Preparing(ThreadContext context, PreparingEventArgs preparing) {
context.Focus = context.Focus.Preparing(preparing);
context.Chain = context.Chain.Link(preparing, context.Focus);
context.Clock = MoveClock(context.Clock, context.Focus);
}
private static void Activating(ThreadContext context, ActivatingEventArgs<object> activating) {
context.Focus = context.Focus.Activating(activating);
}
private static void Activated(ThreadContext context, ActivatedEventArgs<object> activated) {
context.Clock = MoveClock(context.Clock, context.Root);
context.Chain = context.Chain.Activated(activated);
}
private static Node MoveClock(Node currentClock, Node newClock) {
var scanEnable = newClock;
while (scanEnable.Hot == false) {
scanEnable.Hot = true;
scanEnable = scanEnable._parent;
}
var scanDisable = currentClock;
while (scanDisable != scanEnable) {
scanDisable.Hot = false;
scanDisable = scanDisable._parent;
}
return newClock;
}
class ThreadContext {
public Node Root { get; set; }
public Node Focus { get; set; }
public Node Chain { get; set; }
public Node Clock { get; set; }
}
class Node {
private readonly ThreadContext _threadContext;
public Node _parent;
private Node _chain;
public readonly IComponentRegistration _component;
public readonly IDictionary<Guid, Node> _children = new Dictionary<Guid, Node>();
public int _preparingCount;
public int _activatingCount;
public int _activatedCount;
private bool _hot;
readonly Stopwatch _stopwatch = new Stopwatch();
private long _time;
public Node(ThreadContext threadContext) {
_threadContext = threadContext;
_hot = true; // meta-nodes are hot to avoid any timing
}
public Node(Node parent, IComponentRegistration component) {
_threadContext = parent._threadContext;
_parent = parent;
_component = component;
}
public bool Hot {
get {
return _hot;
}
set {
if (_hot == value)
return;
_hot = value;
if (_hot) {
_stopwatch.Start();
}
else {
_stopwatch.Stop();
}
}
}
public long Time {
get { return _time+ _stopwatch.ElapsedTicks * 1000000 / Stopwatch.Frequency; }
}
public void AddTime(long time) { _time += time; }
public override string ToString() {
if (_component == null)
return "root";
return _component.ToString();
}
private static void TraceMessage(string format, IComponentRegistration component) {
//Trace.WriteLine(Message(format, component));
}
private static string Message(string format, IComponentRegistration component) {
return string.Format(format, component.Id, string.Join(",", component.Services), Thread.CurrentThread.ManagedThreadId);
}
public Node Preparing(PreparingEventArgs e) {
// move focus down a level on the tree
// add a link in chain
Node child;
lock (_children) {
if (!_children.TryGetValue(e.Component.Id, out child)) {
child = new Node(this, e.Component);
_children[e.Component.Id] = child;
}
}
TraceMessage("Preparing[{2}] {0} {1}", e.Component);
Interlocked.Increment(ref child._preparingCount);
return child;
}
public Node Link(PreparingEventArgs e, Node focus) {
if (focus._chain != null) {
TraceMessage("REACTIVATED: Preparing[{2}] {0} {1}", e.Component);
}
focus._chain = this;
return focus;
}
public Node Activating(ActivatingEventArgs<object> e) {
// move focus up a level on the tree
if (_component == null) {
TraceMessage("UNMATCHED: Activating[{2}] {0} {1}", e.Component);
return this;
}
if (_component.Id != e.Component.Id) {
TraceMessage("MISSING: Activating[{2}] {0} {1}", _component);
return _parent.Activating(e);
}
TraceMessage("Activating[{2}] {0} {1}", e.Component);
Interlocked.Increment(ref _activatingCount);
return _parent;
}
public Node Activated(ActivatedEventArgs<object> e) {
// remove a link in chain
if (_component == null) {
TraceMessage("UNMATCHED: Activated[{2}] {0} {1}", e.Component);
return this;
}
if (_component.Id != e.Component.Id) {
_chain = _chain.Activated(e);
return this;
}
TraceMessage("Activated[{2}] {0} {1}", e.Component);
Interlocked.Increment(ref _activatedCount);
var chain = _chain;
_chain = null;
return chain;
}
}
class Output : IContainerSpyOutput {
private readonly ConcurrentDictionary<int, ThreadContext> _root;
public Output(ConcurrentDictionary<int, ThreadContext> root) {
_root = root;
}
public void Write(XElement target) {
var elts = _root.Values
.Select(entry => Write(entry.Root))
.OrderByDescending(GetWeight)
.ToArray();
var merged = _root.Values.Aggregate(new Node(null), (a, n) => Merge(a, n.Root));
var totals = new TotalVisitor();
totals.Visit(merged);
target.Add(Write(merged));
target.Add(Write(totals._totals));
target.Add(elts);
}
private class TotalVisitor {
public Node _totals = new Node(null);
public void Visit(Node source) {
foreach (var child in source._children) {
Visit(child.Key, child.Value);
}
}
public void Visit(Guid key, Node source) {
Node target;
if (!_totals._children.TryGetValue(key, out target)) {
target = new Node(_totals, source._component);
_totals._children[key] = target;
}
target._preparingCount += source._preparingCount;
target._activatingCount += source._activatingCount;
target._activatedCount += source._activatedCount;
foreach (var child in source._children) {
Visit(child.Key, child.Value);
}
}
}
private static Node Merge(Node target, Node source) {
target._preparingCount += source._preparingCount;
target._activatingCount += source._activatingCount;
target._activatedCount += source._activatedCount;
target.AddTime(source.Time);
foreach (var sourceChild in source._children) {
Node targetChild;
if (!target._children.TryGetValue(sourceChild.Key, out targetChild)) {
targetChild = new Node(target, sourceChild.Value._component);
target._children[sourceChild.Key] = targetChild;
}
Merge(targetChild, sourceChild.Value);
}
return target;
}
private XElement Write(Node node) {
var elt = new XElement(
"Component",
new XAttribute("services", node._component != null ? string.Join(",", node._component.Services) : "root"),
new XAttribute("preparing", node._preparingCount),
new XAttribute("activating", node._activatingCount),
new XAttribute("activated", node._activatedCount));
lock (node._children) {
var elts = node._children.Values
.Select(Write)
.OrderByDescending(GetMicrosecondInclusive)
.ToArray();
elt.Add(elts);
var weight = elts.Aggregate(node._preparingCount, (a, e) => a + GetWeight(e));
elt.SetAttributeValue("weight", weight);
elt.SetAttributeValue("usinc", node.Time);
if (weight != 0)
elt.SetAttributeValue("usincper", node.Time / weight);
}
return elt;
}
private static long GetMicrosecondInclusive(XElement elt) {
var attr = elt.Attribute("usinc");
return attr == null ? 0 : long.Parse(attr.Value);
}
private static int GetWeight(XElement elt) {
var attr = elt.Attribute("weight");
return attr == null ? 0 : int.Parse(attr.Value);
}
}
}
}

View File

@ -1,6 +1,7 @@
using System;
using System.Web;
using System.Web.Mvc;
using System.Xml.Linq;
using Orchard.Experimental.Models;
using Orchard.DisplayManagement;
using Orchard.Localization;
@ -13,9 +14,11 @@ namespace Orchard.Experimental.Controllers {
[Themed, Admin]
public class HomeController : Controller {
private readonly INotifier _notifier;
private readonly IContainerSpyOutput _containerSpyOutput;
public HomeController(INotifier notifier, IShapeFactory shapeFactory) {
public HomeController(INotifier notifier, IShapeFactory shapeFactory, IContainerSpyOutput containerSpyOutput) {
_notifier = notifier;
_containerSpyOutput = containerSpyOutput;
T = NullLocalizer.Instance;
Shape = shapeFactory;
}
@ -107,6 +110,12 @@ namespace Orchard.Experimental.Controllers {
public static string Break(dynamic view) {
return view.Model.Box.Title;
}
public ActionResult ContainerData() {
var root = new XElement("root");
_containerSpyOutput.Write(root);
return Content(root.ToString(), "text/xml");
}
}
}

View File

@ -34,6 +34,7 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Autofac, Version=2.2.4.900, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL" />
<Reference Include="HibernatingRhinos.Profiler.Appender">
<HintPath>..\..\..\..\lib\nhprof\HibernatingRhinos.Profiler.Appender.dll</HintPath>
</Reference>
@ -53,6 +54,7 @@
<ItemGroup>
<Compile Include="AdminMenu.cs" />
<Compile Include="Commands\ProfilingCommands.cs" />
<Compile Include="ContainerSpy.cs" />
<Compile Include="Controllers\CommandsController.cs" />
<Compile Include="Controllers\ContentController.cs" />
<Compile Include="Controllers\HomeController.cs" />

View File

@ -1,8 +1,8 @@
using System;
using System.Collections.Generic;
using System.Web.Routing;
using Autofac;
using JetBrains.Annotations;
using Orchard.Caching;
using Orchard.Commands;
using Orchard.Commands.Builtin;
using Orchard.ContentManagement;
@ -46,6 +46,7 @@ namespace Orchard.Setup {
builder.RegisterModule(new MvcModule());
builder.RegisterModule(new CommandModule());
builder.RegisterModule(new WorkContextModule());
builder.RegisterModule(new CacheModule());
builder.RegisterType<RoutePublisher>().As<IRoutePublisher>().InstancePerLifetimeScope();
builder.RegisterType<ModelBinderPublisher>().As<IModelBinderPublisher>().InstancePerLifetimeScope();

View File

@ -86,6 +86,23 @@ namespace Orchard.Data {
_dataServicesProviderFactory
.CreateProvider(parameters)
.BuildConfiguration(parameters));
#region NH-2.1.2 specific optimization
// cannot be done in fluent config
// the IsSelectable = false prevents unused ContentPartRecord proxies from being created
// for each ContentItemRecord or ContentItemVersionRecord.
// done for perf reasons - has no other side-effect
foreach (var persistentClass in config.ClassMappings) {
if (persistentClass.EntityName.StartsWith("Orchard.ContentManagement.Records.")) {
foreach (var property in persistentClass.PropertyIterator) {
if (property.Name.EndsWith("Record") && !property.IsBasicPropertyAccessor) {
property.IsSelectable = false;
}
}
}
}
#endregion
return config;
}

View File

@ -1,29 +1,30 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using Autofac.Features.Metadata;
using Orchard.Caching;
using Orchard.Environment.Extensions;
using Orchard.Environment.Extensions.Models;
using Orchard.Utility;
namespace Orchard.DisplayManagement.Descriptors {
public class DefaultShapeTableManager : IShapeTableManager, ISingletonDependency {
public class DefaultShapeTableManager : IShapeTableManager {
private readonly IEnumerable<Meta<IShapeTableProvider>> _bindingStrategies;
private readonly IExtensionManager _extensionManager;
private readonly ICacheManager _cacheManager;
public DefaultShapeTableManager(
IEnumerable<Meta<IShapeTableProvider>> bindingStrategies,
IExtensionManager extensionManager) {
IExtensionManager extensionManager,
ICacheManager cacheManager) {
_extensionManager = extensionManager;
_cacheManager = cacheManager;
_bindingStrategies = bindingStrategies;
}
readonly ConcurrentDictionary<string, ShapeTable> _tables = new ConcurrentDictionary<string, ShapeTable>();
public ShapeTable GetShapeTable(string themeName) {
return _tables.GetOrAdd(themeName ?? "", x => {
return _cacheManager.Get(themeName ?? "", x => {
var builderFactory = new ShapeTableBuilderFactory();
foreach (var bindingStrategy in _bindingStrategies) {
Feature strategyDefaultFeature = bindingStrategy.Metadata.ContainsKey("Feature") ?

View File

@ -1,6 +1,6 @@
namespace Orchard.DisplayManagement.Descriptors {
public interface IShapeTableManager : IDependency {
public interface IShapeTableManager : ISingletonDependency {
ShapeTable GetShapeTable(string themeName);
}

View File

@ -1,5 +1,5 @@
namespace Orchard.Localization {
public interface IText {
public interface IText : ISingletonDependency {
LocalizedString Get(string textHint, params object[] args);
}
}

View File

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using Autofac;
using Autofac.Core;
@ -6,6 +7,11 @@ using Module = Autofac.Module;
namespace Orchard.Localization {
public class LocalizationModule : Module {
private readonly IDictionary<string, Localizer> _localizerCache;
public LocalizationModule() {
_localizerCache = new Dictionary<string, Localizer>();
}
protected override void Load(ContainerBuilder builder) {
builder.RegisterType<Text>().As<IText>().InstancePerDependency();
@ -19,8 +25,14 @@ namespace Orchard.Localization {
var scope = registration.Activator.LimitType.FullName;
registration.Activated += (sender, e) => {
var localizer = LocalizationUtilities.Resolve(e.Context, scope);
userProperty.SetValue(e.Instance, localizer, null);
if (_localizerCache.ContainsKey(scope)) {
userProperty.SetValue(e.Instance, _localizerCache[scope], null);
}
else {
var localizer = LocalizationUtilities.Resolve(e.Context, scope);
_localizerCache.Add(scope, localizer);
userProperty.SetValue(e.Instance, localizer, null);
}
};
}
}

View File

@ -9,7 +9,7 @@ namespace Orchard.Logging {
Fatal
}
public interface ILogger {
public interface ILogger : ISingletonDependency {
bool IsEnabled(LogLevel level);
void Log(LogLevel level, Exception exception, string format, params object[] args);
}

View File

@ -11,6 +11,12 @@ using Module = Autofac.Module;
namespace Orchard.Logging {
public class LoggingModule : Module {
private readonly IDictionary<string, ILogger> _loggerCache;
public LoggingModule() {
_loggerCache = new Dictionary<string, ILogger>();
}
protected override void Load(ContainerBuilder moduleBuilder) {
// by default, use Orchard's logger that delegates to Castle's logger factory
moduleBuilder.RegisterType<CastleLoggerFactory>().As<ILoggerFactory>().InstancePerLifetimeScope();
@ -65,8 +71,15 @@ namespace Orchard.Logging {
var propertyInfo = entry.PropertyInfo;
yield return (ctx, instance) => {
var propertyValue = ctx.Resolve<ILogger>(new TypedParameter(typeof(Type), componentType));
propertyInfo.SetValue(instance, propertyValue, null);
string component = componentType.ToString();
if (_loggerCache.ContainsKey(component)) {
propertyInfo.SetValue(instance, _loggerCache[component], null);
}
else {
var propertyValue = ctx.Resolve<ILogger>(new TypedParameter(typeof(Type), componentType));
_loggerCache.Add(component, propertyValue);
propertyInfo.SetValue(instance, propertyValue, null);
}
};
}
}