性能优化使用生成的XmlSerializer类

时间:2014-08-11 08:04:26

标签: c# .net performance xml-serialization

我们的应用程序正在读取一些XML文件。 XML格式是固定的,因此我们可以使用XmlSerializer非常轻松地阅读它们。

我使用此代码读取XML文件并将其转换为类:

public static T FromXml<T>(this string xml) where T : class
{
    if (string.IsNullOrEmpty(xml))
    {
        return default(T);
    }

    XmlSerializer xmlserializer = new XmlSerializer(typeof(T));

    XmlTextReader textReader = new XmlTextReader(new StringReader(xml));
    textReader.Normalization = false;

    XmlReaderSettings settings = new XmlReaderSettings();

    T value;

    using (XmlReader reader = XmlReader.Create(textReader, settings))
    {
        value = (T)xmlserializer.Deserialize(reader);
    }

    return value;
}

但是,存在一些性能问题。在第一次调用此代码时,会使用T的特定类型,XmlSerializer会生成Project.XmlSerializer.dll文件。

这很好,但花费一些宝贵的毫秒(在我的情况下大约900毫秒)。这可以通过使用XML Serializer Generator (sgen)在正手上生成该程序集来规避。这将时间减少到大约一半。主要是由于装配的阅读和反射。

我希望进一步优化这一点,方法是将XmlSerializer类放在程序集中的实际类中,但我找不到让XmlSerializer的方法知道不要读取外部组件,而是使用当前组件中的序列化器。

有任何想法如何做到这一点或另一种方法来使这项工作? (我无法预加载它们,因为大多数序列化类在启动时使用)


使用ANTS Profiler进行分析(来自其他机器的指标,但模式相同):

enter image description here

平原。在生成和加载XmlSerializer程序集时,大多数时间(300ms + 400ms = 700ms)都会丢失。

enter image description here

用sgen生成程序集。大多数情况下(336ms)在加载XmlSerializer程序集时会丢失。

enter image description here

当在项目中包含程序集的实际源代码并直接调用序列化程序时,操作将降至456ms(首先是1s,第二次是556ms)。

2 个答案:

答案 0 :(得分:3)

注意:OP发布了一个示例配置:http://pastebin.com/d67nch3R

根据示例配置和您遇到的问题类型,有几种暴力方法,几乎​​可以保证完成这一操作,两者都可以完全放弃XML序列化程序

路线#1

放弃XML序列化并使用XDocument从XML中获取数据。

路线#2

使用json和Newtonsoft Json存储和加载配置。它应该比XML Serializer

执行得更好

示例json对应物看起来像这样:

{
  "Connections": {
    "-default": "Local\\SqlServer",
    "-forcedefault": "false",
    "group": {
      "-name": "Local",
      "connection": {
        "-name": "SqlServer",
        "database": {
          "-provider": "SqlServer",
          "-connectionString": "blah"
        }
      }
    }
  },
  "LastLanguage": "en",
  "UserName": "un",
  "SavePassword": "true",
  "AutoConnect": "false",
  "Password": "someObfuscatedHashedPassword==",
  "ConnectionName": "Somewhere\\Database",
  "LastAvailableBandwidth": "0",
  "LastAvailableLatency": "0",
  "DateLastConnectionSuccesful": "2014-08-13T15:21:35.9663654+02:00"
}

加载它:

UserSettings settings = JsonConvert.DeserializeObject<UserSettings>(File.ReadAllText("settings.json"))

答案 1 :(得分:2)

除非你在app启动时进行序列化,否则一种方法是强制CLR加载甚至编译你提前使用的任何类,可能在一个可以在后台运行的线程中当你启动你的应用程序时。

例如:

foreach (Assembly a in assembliesThatShouldBeCompileed)
    foreach (Type type in a.GetTypes())
        if (!type.IsAbstract && type.IsClass)
        {
            foreach (MethodInfo method in type.GetMethods(
                                BindingFlags.DeclaredOnly |
                                BindingFlags.NonPublic |
                                BindingFlags.Public |
                                BindingFlags.Instance |
                                BindingFlags.Static))
            {
                if (method.ContainsGenericParameters || 
                    method.IsGenericMethod || 
                    method.IsGenericMethodDefinition)
                    continue;

                if ((method.Attributes & MethodAttributes.PinvokeImpl) > 0)
                    continue;

                System.Runtime.CompilerServices
                   .RuntimeHelpers.PrepareMethod(method.MethodHandle);
            }
        }
然而,奇怪的是,如果SGEN代码在单独的程序集中,那么您的分析似乎表明没有太大区别,而加载似乎是瓶颈。我想知道它们在同一个组件中的情况如何?