Linq到对象花费更多时间的性能问题
本文关键字:时间 性能 问题 对象 Linq | 更新日期: 2023-09-27 18:08:53
我有一个基于对象值更新的xml代码。这里的"foreach"循环大约需要12-15分钟来获取一个200kb的xml文件。请建议我如何提高性能。(XML文件由四个级别的标记组成,其中子(第4级)标记的编号各为10)
代码:IEnumerable<XElement> elements = xmlDoc.Descendants();
foreach (DataSource Data in DataLst)
{
XElement xmlElem = (from xmlData in elements
where Data.Name == xmlData.Name.LocalName //Name
&& Data.Store == xmlData.Element(XName.Get("Store", "")).Value
&& Data.Section == xmlData.Element(XName.Get("Section", "")).Value
select xmlData.Element(XName.Get("Val", ""))).Single();
xmlElem.ReplaceWith(new XElement(XName.Get("Val", ""), Data.Value));
}
看起来这里有一个O(n)&乘以O(m)的问题,因为n = DataList的大小,m = xml的大小。为了使它变成O(n)+O(m),你应该对数据进行索引;例如:
var lookup = elements.ToLookup(
x => new {
Name = x.Name.LocalName,
Store = x.Element(XName.Get("Store", "")).Value,
Section = x.Element(XName.Get("Section", "")).Value},
x => x.Element(XName.Get("Val", ""))
);
foreach (DataSource Data in DataLst)
{
XElement xmlElem = lookup[
new {Data.Name, Data.Store, Data.Section}].Single();
xmlElem.ReplaceWith(new XElement(XName.Get("Val", ""), Data.Value));
}
(未测试-仅显示一般方法)
我认为更好的方法是将XML反序列化为c#类,然后使用LINQ,应该很快。
"感谢大家的宝贵时间和努力"
问题答案:实际上对象'DataLst'的类型是IEnumerable<>这需要时间来获取值,但在我将其更改为列表<>类型后,性能大大提高(现在在20秒内运行)
如果运行这个需要这么长时间,那么也许可以这样做:
- 不要同时迭代—只迭代xml文件和从DataLst中加载数据(使用sql命令或简单的linq语句加载基于Name/Store/Section的数据),使用此数据(Name/Store/Section)为键创建一个简单的结构/类—不要忘记实现equals,和GetHashCode
- 遍历XML-Elements,并使用字典查找要替换 的值。
这样,您只需迭代xml文件一次,而不是对数据源中的每个数据迭代一次。
不清楚为什么要花那么长时间-这是一个非常很长的时间。DataLst
中有多少元素?为了简单起见,我将重写查询,以though开头:
IEnumerable<XElement> elements = xmlDoc.Descendants();
foreach (DataSource data in DataLst)
{
XElement valElement = (from element in xmlDoc.Descendants(data.Name)
where data.Store == element.Element("Store").Value
&& data.Section == element.Element("Section").Value
select element.Element("Val")).Single();
valElement.ReplaceWith(new XElement("Val"), data.Value));
}
(顺便说一下,我假设您的元素实际上都没有名称空间。)
下一步:考虑替换valElement
的内容而不是替换元素本身。改为:
valElement.ReplaceAll(data.Value);
现在,这一切都是为了保持简单性,避免预计算等…因为它听起来不应该花这么长时间。但是,您可能需要按照Marc和Carsten的建议构建查找。
尝试将LINQ中的Single()
调用替换为First()
。
冒着着火的风险,您考虑过用XQuery来编写这个吗?一个不错的XQuery处理器很有可能有一个连接优化器来有效地处理这个查询。