【问题标题】:Performance and use of Resolve in UnityResolve在Unity中的表现和使用
【发布时间】:2012-02-10 08:38:01
【问题描述】:
我们使用 Unity 作为 IOC 容器。
到目前为止,我们的程序的编写方式是在需要使用接口之前解析接口。
然而,这会导致接口在循环内被解析。这可能会导致接口在一次运行中被解析 100,000 次。
问题是:将解决方案移到循环之外会有显着差异还是这只是微优化?
我们的程序需要一天的时间来运行。所以对于那些会回答“自己测试”的人来说,在这里问问题实际上更快:)
【问题讨论】:
标签:
c#
performance
unity-container
ioc-container
【解决方案1】:
这取决于你如何注册我要说的接口。您是向哪个 LifetimeManager 注册的?
您的程序是否总是需要一个新实例?还是可以一遍又一遍地重复同一个实例?
如果需要同一个实例(Singleton),使用ContainerControlledLifetimeManager:
Container.RegisterType<Interface, Implementation>(new ContainerControlledLifetimeManager());
如果你不关心你得到哪个实例(新的或旧的,由 GC 维护),你可以使用 ExternallyControlledLifeTimeManager:
Container.RegisterType<Interface, Implementation>(new ExternallyControlledLifeTimeManager());
您还可以创建自己的 LifetimeManager 实现以更好地满足您的需求。
看看这篇关于LifetimeManagers的文章
【解决方案2】:
当然会影响性能。多少取决于构造对象所具有的依赖图的复杂性以及是否也构造了任何资源(例如数据库连接等)。
随着 Unity 的发展,它既不是最快也不是最慢的容器。只是平均水平。
服务定位(使用Resolve而不是构造函数注入)被许多人认为是一种反模式。原因是它很容易被误用,而且它将依赖项隐藏为实现细节(而不是在构造函数中记录依赖项)。
【解决方案3】:
我不太明白你用循环解析 100,000 次是什么意思...
如果您确实想解析 100,000 次,那么无论您是否在循环中执行它都没有关系,速度将是相同的。
我建议您检查一下是否可以重用已解决的实例...
再一次,没有人知道你的速度问题是否是由容器引起的。
我们不知道您在 100,000 次迭代的循环中在做什么,如果您在循环本身中花费的时间少于 1%,那么解析接口的开销很可能是很可能的。不要等待一天,只需运行 1000 次迭代,您就会看到......
另外,您不需要运行您的业务逻辑,只需实现一个类似的循环并只在那里解析您的接口,这样您就可以看到 Unity 对您的应用程序的影响有多大。
但无论如何,1 天以上的 100,000 次解决意味着每秒约 2 次......它会产生效果,但不会那么戏剧化......我宁愿看看你可以在你的业务逻辑中优化什么,或者并行化你的循环。
Unity 也没有有史以来最快容器的声誉(实际上恰恰相反);)您可以尝试 Autofac,它的速度明显更快......
这里是some performance benchmarks for IoC containers。
【解决方案4】:
我确定(或者我希望!)解析已经优化,特别是在容器控制实例 (ContainerControlledLifetimeManager) 的情况下,我从您在这里的问题中假设,但调用它是有道理的less 比调用 more 工作量少。
我进行了一个非常幼稚的测试,其中解析结果慢了一个数量级,但并不慢。当然,这并没有考虑到容器中可能注册的其他内容,或者正在管理的实例数量,或者正在实例化的内容,或者我错过的任何其他因素。恕我直言,真正回答您的问题的唯一方法是测试您的应用,抱歉:(
RESULTS:
00:00:00.2462702 : Resolve ContainerControlledLifetimeManager
00:00:00.0014184 : Plain assignment
00:00:00.3514334 : Resolve PerResolveLifetimeManager
00:00:00.0019258 : Direct instantiation
class Program
{
static readonly IUnityContainer _container = new UnityContainer();
static void Main(string[] args)
{
_container.RegisterType(typeof(IInterfaceOne), typeof (ClassOne), new ContainerControlledLifetimeManager());
_container.RegisterType(typeof(IInterfaceTwo), typeof(ClassTwo), new PerResolveLifetimeManager());
var classOne = new ClassOne();
DoLots("Resolve ContainerControlledLifetimeManager", ()=>_container.Resolve<IInterfaceOne>());
DoLots("Plain assignment", () =>classOne);
DoLots("Resolve PerResolveLifetimeManager ", () => _container.Resolve<IInterfaceTwo>());
DoLots("Direct instantiation", () => new ClassTwo());
Console.ReadLine();
}
static void DoLots(string msg, Func<object> resolveFunc)
{
var stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < 100000; i++)
{
var instance = resolveFunc();
}
stopwatch.Stop();
Console.WriteLine(string.Format("{0} : {1}",stopwatch.Elapsed , msg ));
}
}
public interface IInterfaceOne{}
public interface IInterfaceTwo{}
public class ClassOne : IInterfaceOne{}
public class ClassTwo : IInterfaceTwo{}
【解决方案5】:
这个问题有点老了,但我在使用 Unity 时发现,为了提高性能,我强制使用除控制器之外的任何对象的单例、ContainerControlledLifetimeManager、实例(在上面的 Roels 回答中提到)。每次调用控制器时,控制器都需要是新实例。我在 global.asax 文件中调用的 Bootstrap.cs/RegisterTypes 方法中使用了以下代码。
var controllerClasses = AllClasses.FromLoadedAssemblies()
.Where(t => t.FullName.Contains("Controller")).ToList();
var nonControllerClasses = AllClasses.FromLoadedAssemblies()
.Where(t => !t.FullName.Contains("Controller")).ToList();
container.RegisterTypes(controllerClasses, WithMappings.FromMatchingInterface, WithName.Default);
container.RegisterTypes(nonControllerClasses, WithMappings.FromMatchingInterface, WithName.Default,
type => new ContainerControlledLifetimeManager());
注意 - 您将希望在填充 controllerClasses 和 nonControllerClasses 的代码上使 where 表达式更具体,以便您只注册您需要的类。 IE 按命名空间过滤。