2010-03-15 88 views
2

我有一个Dictionary<int, MyClass>增加90000至的XElement的XDocument

它包含100,000个项目

万件值填充,而90000为空。

我有这样的代码:

var nullitems = MyInfoCollection.Where(x => x.Value == null).ToList(); 
nullitems.ForEach(x => LogMissedSequenceError(x.Key + 1)); 

private void LogMissedSequenceError(long SequenceNumber) 
     { 
      DateTime recordTime = DateTime.Now; 

      var errors = MyXDocument.Descendants("ERRORS").FirstOrDefault(); 
      if (errors != null) 
      { 

       errors.Add(
        new XElement("ERROR", 
         new XElement("DATETIME", DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss:fff")), 
         new XElement("DETAIL", "No information was read for expected sequence number " + SequenceNumber), 
         new XAttribute("TYPE", "MISSED"), 
         new XElement("PAGEID", SequenceNumber) 
         ) 
       ); 
      } 
     } 

这似乎需要约2分钟即可完成。我似乎无法找到瓶颈可能出现的位置,或者这种时机听起来正确吗?

任何人都可以看到为什么采取这么久?

+0

使用调试跟踪查看查询需要多长时间,以及记录需要多长时间。另外,在处理100,000个项目文档时,XML可能会有很多处理开销。 – 2010-03-15 16:32:10

+0

如果您检查您可能会发现查找每个通话中的“ERRORS”元素是最昂贵的部分之一。或者将ERRORS元素传递给该方法,或者使用LINQ来获得您的优势,如下所述。 – 2010-03-15 18:09:04

+0

你运行了一个分析器吗?我会感兴趣的是为什么你运行DateTime.now两次(因为它调用相对昂贵),以及在调用90k次时真的有多慢... – 2010-03-15 16:38:43

回答

1

如何关于用LINQ查询替换方法调用?

static void Main(string[] args) 
{ 

    var MyInfoCollection = (from key in Enumerable.Range(0, 100000) 
          let value = (MoreRandom() % 10 != 0) 
                ? (string)null 
                : "H" 
          select new { Value = value, Key = key } 
          ).ToDictionary(k => k.Key, v => v.Value); 

    var MyXDocument = new XElement("ROOT", 
            new XElement("ERRORS") 
           ); 
    var sw = Stopwatch.StartNew(); 
    //=== 
    var errorTime = DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss:fff"); 
    var addedIndex = MyInfoCollection.Select((item, index) => 
                new 
                { 
                 Value = item.Value, 
                 Key = item.Key, 
                 Index = index 
                }); 
    var errorQuery = from item in addedIndex 
        where string.IsNullOrEmpty(item.Value) 
        let sequenceNumber = item.Key + 1 
        let detail = "No information was read for expected " + 
            "sequence number " + sequenceNumber 
        select new XElement("ERROR", 
         new XElement("DATETIME", errorTime), 
         new XElement("DETAIL", detail), 
         new XAttribute("TYPE", "MISSED"), 
         new XElement("PAGEID", sequenceNumber) 
         ); 

    var errors = MyXDocument.Descendants("ERRORS").FirstOrDefault(); 
    if (errors != null) 
     errors.Add(errorQuery); 
    //=== 
    sw.Stop(); 
    Console.WriteLine(sw.ElapsedMilliseconds); //623 
} 
static RandomNumberGenerator rand = RandomNumberGenerator.Create(); 
static int MoreRandom() 
{ 
    var buff = new byte[1]; 
    rand.GetBytes(buff); 
    return buff[0]; 
} 
+0

你达人!关键字'let'用于什么? – Jon 2010-03-15 20:21:25

+0

@Jon:“让”让你分配一个上下文变量。这就像在foreach循环中创建一个变量赋值。你可以在“select”语句中放入相同的信息,但我发现这个模型更容易理解和重用。 – 2010-03-15 20:32:24

+0

我也在LINQ查询中使用了DateTime.Now进行了测试。它的确会稍微减缓这个过程,但是没有像遍历查询“ERRORS”元素一样。 – 2010-03-15 20:34:06

2

这是我最有可能做的。

private void BuildErrorNodes() 
{ 
    const string nodeFormat = @"<ERROR TYPE=""MISSED""><DATETIME>{0}</DATETIME><DETAIL>No information was read for expected sequence number {1}</DETAIL><PAGEID>{1}</PAGEID></ERROR>"; 

    var sb = new StringBuilder("<ERRORS>"); 
    foreach (var item in MyInfoCollection) 
    { 
     if (item.Value == null) 
     { 
      sb.AppendFormat(
       nodeFormat, 
       DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss:fff"), 
       item.Key + 1 
      ); 
     } 
    } 

    sb.Append("</ERRORS>"); 

    var errorsNode = MyXDocument.Descendants("ERRORS").FirstOrDefault(); 
    errorsNode.ReplaceWith(XElement.Parse(sb.ToString())); 
} 
3

如果您MyInfoCollection是巨大的,我不会把ToList()它只是让你可以使用ForEach扩展方法。调用ToList()将创建并填充一个巨大的列表。我会删除ToList()调用,并将.ForEach变成for each语句,或者写一个。 ForEach扩展方法为IEnumerable<T>

然后对其进行配置并查看需要多长时间。还有一件事是删除ERRORS元素的find和null检查。如果它不在那里,那么请不要拨打上面的for each声明。这样你空一次检查一次而不是90,000次。

加上迈克尔葡萄汁指出的那样,我会定义一个字符串来保存的价值DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss:fff"),然后引用它或者通过它在此外,你甚至不使用此电话:

DateTime recordTime = DateTime.Now; 
+0

到目前为止,我刚刚实现了foreach循环而不是ForEach扩展方法,它们看起来速度相同。 ToList()是快速的,只是日志花费时间。 – Jon 2010-03-15 17:02:34

相关问题