LINQ To XML 效能問題?

LINQ To XML 效能問題?

最近在 PTT 上面,有篇文章提到讀取 XML 的問題,隨手寫了 LINQ To XML 的語法來解決了。不過,似乎有網友質疑 LINQ To XML 效能不是很好?

這個 XML 檔案的內容如下,主要的撰寫的功能是比對 test 標籤,過濾出符合 mid 與 enble 特定條件的元素,並秀出子元素 modle 元素值。

例如,mid = "1" 且 enble = "TRUE" 的 modle 元素值是 "tttt"。

   1:  <?xml version="1.0" encoding="utf-8" ?>
   2:  <root>
   3:    <test mid="1" enble="TRUE">
   4:      <modle>tttt</modle>
   5:    </test>
   6:    <test mid="2" enble="TRUE">
   7:      <modle>yyyy</modle>
   8:    </test>
   9:  </root>

首先,我是利用 LINQ To XML 的方式來解決,程式碼如下:

   1:  XDocument xd = XDocument.Load(@"XMLFile.xml");
   2:  var q = from x in xd.Descendants("test")
   3:          where (int) x.Attribute("mid") == 1 && (bool) x.Attribute("enble")
   4:          select x.Element("modle").Value;
   5:   
   6:  foreach (string o in q)
   7:  {
   8:      Console.WriteLine(o);
   9:  }

後來,網友提出 XmlDocument + XPath 的方式來解決,程式碼如下:

   1:  XmlDocument xmlDoc = new XmlDocument();
   2:  xmlDoc.Load(@"XMLFile.xml");
   3:   
   4:  XmlNodeList nodes = xmlDoc.SelectNodes("//root/test[@mid=1 and @enble='TRUE']/modle");
   5:  foreach (XmlElement element in nodes)
   6:  {
   7:      Console.WriteLine(element.InnerText);
   8:  }

有網友說:比起直接下 XPath,LINQ 那段 code 似乎造成了額外的成本。

上面兩段程式碼的比較的立足點,似乎是不太相同的。如果,LINQ To XML + XPath 的方式來比較,可能比較適當的吧?如下:

   1:  XDocument xd = XDocument.Load(@"XMLFile.xml");
   2:  var q = from x in xd.XPathSelectElements("//root/test[@mid=1 and @enble='TRUE']/modle")
   3:          select x.Value;
   4:   
   5:  foreach (string o in q)
   6:  {
   7:      Console.WriteLine(o);
   8:  }

而原先第一段 LINQ To XML 程式碼,我認為就如同使用 XmlDocument + foreach ChildNodes 的方式,如下:

   1:  XmlDocument xd = new XmlDocument();
   2:  xd.Load(@"XMLFile.xml");
   3:   
   4:  foreach (XmlNode node in xd.DocumentElement.ChildNodes)
   5:  {
   6:      if (node.Name == "test" && node.Attributes["mid"].Value == "1" &&
   7:          node.Attributes["enble"].Value == "TRUE")
   8:      {
   9:          Console.WriteLine(node.ChildNodes[0].InnerText);
  10:      }
  11:  }

有興趣的人可以利用以下程式碼,使用 Stopwatch 來玩玩吧,我也不知道到底哪一種是比較好的方法呢!

   1:  private void button1_Click(object sender, EventArgs e)
   2:  {
   3:      const int MAX = 1000000;
   4:      int num = 0;
   5:      Stopwatch sw = new Stopwatch();
   6:      long elapsedMilliseconds = 0;
   7:   
   8:      #region (1) LINQ To XML
   9:   
  10:      sw = Stopwatch.StartNew();
  11:      for (int i = 1; i <= MAX; i++)
  12:      {
  13:          XDocument xd = XDocument.Load(@"XMLFile.xml");
  14:          var q = from x in xd.Descendants("test")
  15:                  where (int) x.Attribute("mid") == 1 && (bool) x.Attribute("enble")
  16:                  select x.Element("modle").Value;
  17:   
  18:          foreach (string o in q)
  19:          {
  20:              Console.WriteLine(o);
  21:          }
  22:      }
  23:      sw.Stop();
  24:      elapsedMilliseconds = sw.ElapsedMilliseconds;
  25:      MessageBox.Show(string.Format("{0} ElapsedMilliseconds", elapsedMilliseconds));
  26:   
  27:      #endregion
  28:   
  29:      #region (2) XmlDocument + XPath
  30:   
  31:      sw = Stopwatch.StartNew();
  32:      for (int i = 1; i <= MAX; i++)
  33:      {
  34:          XmlDocument xmlDoc = new XmlDocument();
  35:          xmlDoc.Load(@"XMLFile.xml");
  36:   
  37:          XmlNodeList nodes = xmlDoc.SelectNodes("//root/test[@mid=1 and @enble='TRUE']/modle");
  38:          foreach (XmlElement element in nodes)
  39:          {
  40:              Console.WriteLine(element.InnerText);
  41:          }
  42:      }
  43:      sw.Stop();
  44:      elapsedMilliseconds = sw.ElapsedMilliseconds;
  45:      MessageBox.Show(string.Format("{0} ElapsedMilliseconds", elapsedMilliseconds));
  46:   
  47:      #endregion
  48:   
  49:      #region (3) LINQ To XML + XPath
  50:   
  51:      sw = Stopwatch.StartNew();
  52:      for (int i = 1; i <= MAX; i++)
  53:      {
  54:          XDocument xd = XDocument.Load(@"XMLFile.xml");
  55:          var q = from x in xd.XPathSelectElements("//root/test[@mid=1 and @enble='TRUE']/modle")
  56:                  select x.Value;
  57:   
  58:          foreach (string o in q)
  59:          {
  60:              Console.WriteLine(o);
  61:          }
  62:      }
  63:      sw.Stop();
  64:      elapsedMilliseconds = sw.ElapsedMilliseconds;
  65:      MessageBox.Show(string.Format("{0} ElapsedMilliseconds", elapsedMilliseconds));
  66:   
  67:      #endregion
  68:   
  69:      #region (4) XmlDocument + foreach ChildNodes
  70:   
  71:      sw = Stopwatch.StartNew();
  72:      for (int i = 1; i <= MAX; i++)
  73:      {
  74:          XmlDocument xd = new XmlDocument();
  75:          xd.Load(@"XMLFile.xml");
  76:   
  77:          foreach (XmlNode node in xd.DocumentElement.ChildNodes)
  78:          {
  79:              if (node.Name == "test" && node.Attributes["mid"].Value == "1" &&
  80:                  node.Attributes["enble"].Value == "TRUE")
  81:              {
  82:                  Console.WriteLine(node.ChildNodes[0].InnerText);
  83:              }
  84:          }
  85:      }
  86:      sw.Stop();
  87:      elapsedMilliseconds = sw.ElapsedMilliseconds;
  88:      MessageBox.Show(string.Format("{0} ElapsedMilliseconds", elapsedMilliseconds));
  89:   
  90:      #endregion
  91:  }

另外,還有網友提出使用 XmlSerialize、DataSet.ReadXML 等方式,有興趣的人也可以玩玩看,順便分享一下吧。