WPF热启动AppDomain性能(Application.RunInternal,XamlReader.LoadBaml)

3
我有一个相对简单的应用程序,但热启动(第二次等)需要3-5秒,这太慢了。分析器(VS2010,CPU抽样)显示超过80%的时间花费在Application.RunInternal(约40%)和XamlRader.LoadBaml(约40%)函数中。
问题的根源是Window在非默认AppDomain中创建。如果我将Window创建移至默认AppDomain或为AppDomain提供无限制的权限集,则一切都像预期的那样快。
我的测试环境:
  • Windows Seven x64
  • .Net 4.0
  • 4Gb RAM
  • GeForce 9800GT 1Gb.

我是这样创建AppDomain的:
var permissionSet = new PermissionSet(null);

permissionSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution | SecurityPermissionFlag.SerializationFormatter | SecurityPermissionFlag.UnmanagedCode));
permissionSet.AddPermission(new ReflectionPermission(PermissionState.Unrestricted));
permissionSet.AddPermission(new UIPermission(PermissionState.Unrestricted));
permissionSet.AddPermission(new MediaPermission(PermissionState.Unrestricted));
permissionSet.AddPermission(new FileDialogPermission(PermissionState.Unrestricted));

var appDomainSetup =
    new AppDomainSetup
    {
        ApplicationBase = AppDomain.CurrentDomain.SetupInformation.ApplicationBase,
        ApplicationName = AppDomain.CurrentDomain.SetupInformation.ApplicationName,
        DisallowApplicationBaseProbing = false,
        DisallowBindingRedirects = true,
        DisallowCodeDownload = true,
        DisallowPublisherPolicy = true,
        LoaderOptimization = LoaderOptimization.MultiDomainHost
    };

_appDomain =
    AppDomain.CreateDomain(
        name,
        null,
        appDomainSetup,
        permissionSet,
        new[]
    {
        // a few types I need
        typeof(...).Assembly.Evidence.GetHostEvidence<StrongName>(),
    });

即使我将XAML裁剪成空窗口,行为仍然保持不变。
<Window
    x:Class="Rosmurta.Extensibility.WpfUI.RosmurtaWindow"
    x:ClassModifier="internal"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Test"
    Height="480"
    Width="640"
    WindowStyle="SingleBorderWindow">
    <Grid>
    </Grid>
</Window>

XamlRader.LoadBaml的解析内容不多,但即使是空窗口,在启动时它也会花费超过30%的时间。


我已经尝试了以下方法(但都没有帮助):

  • 在App.config中添加<generatePublisherEvidence enabled="false"/>
  • 在Main方法中添加[LoaderOptimization(LoaderOptimization.MultiDomainHost)]属性
  • 给所有程序集添加签名

还有什么其他的方法可以尝试吗?


嗨,怎么样进入实际调用呢? - user572559
@Dmitry,这些是PresentationFramework.dll中的方法。即使我以某种方式进入它们,我也无法对它们进行分析。 - adontz
虽然很困难,但你不能雇佣Rob Relyea,对吧?:) 即使这样,他会问的第一个问题是为什么需要在单独的AppDomain中加载应用程序?:) “3-5秒”的延迟应该足够明显,在调试时可以捕捉到原因,这对我来说似乎是唯一可行的选项。 - user572559
答案很简单,就是安全性。正如您可以从我提供的源代码中看到的那样,有一些CAS限制被应用。我无法理解的是,没有第一次机会异常或其他问题的迹象,代码在受限域中只是变慢了。在非受限非默认域中,一切都很快,因此CAS直接或间接地成为原因。我将继续尝试调试这个问题,只是想知道是否有人知道答案。 - adontz
那么,您的意思是说您无法通过在单独的应用程序域中加载应用程序来重现问题吗?是否有特定的CAS设置会严重影响性能? - 如果知道这一点,您可以进入 .net 代码并查看外部发生了什么。抱歉回答比较笼统,但这是我唯一能想到的方法。 - user572559
显示剩余2条评论
1个回答

1
这个问题很老了,但是我的回答可能会对某些人有所帮助。有时候,在启动过程中XAMLReader确实需要很长时间,但这并不总是意味着问题是由解析本身引起的。当解析XAML时,除了解析之外,XAMLParser还执行了几个相对繁重的操作:
  • 使用反射获取未知类型并创建实例
  • 初始化对象并创建实例。如果您的XAML(或您正在使用的组件库的XAML)包含许多自定义类,则它们将由XAMLParser创建,并进行JIT编译。
  • 在资源字典层次结构中搜索资源。如果资源字典层次结构真的很复杂,则此操作可能需要一些时间。

有几种通用技术可以帮助您减少WPF应用程序的启动时间:

  • 使用Ngen工具生成本地映像。
  • 当Ngen不适用时,启用MultiCore JIT
  • 对于.NET Core项目,由于没有Ngen,可以与MultiCore JIT一起使用ReadyToRun
  • 按需加载数据。我认为这不是你的情况,但可能对整个情况有用。例如,DevExpress具有Virtual Source和{{link1:Server Mode}}功能,可以异步为您执行此操作。
  • 您还可以将“重型”视图包装在控件中,延迟其加载而不会冻结UI。例如,您可以使用DevExpress LoadingDecorator
  • 确保您的视图控件未扩展到可见区域之外。例如,如果您有一个ListBox内部的StackPanel,ListBox将立即创建所有元素,因为StackPanel不限制其子项的高度,它们“认为”自己具有无限高度。结果,ListBox将立即创建所有可视元素并显着降低性能。
  • 确定瓶颈的简单方法是注释UI,注释我们的XAML部分,看看这如何影响启动

这里有一篇可能会有帮助的博客文章:9个减少WPF应用程序启动时间的技巧


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接