Не так давно мне пришлось делать приложение для Windows Phone работающее с xml-файлами. Всё было неплохо, но когда в файле стало ~100.000 записей, чтение их занимало ну уж очень много времени. И я решил сравненить производительность различных способов чтения данных из xml возможных на платформе .Net.
Оборудование
Для лучшего понимания показателей проведенных тестов стоить рассказать на чём они были проведены. Тесты из разряда «Desktop» я выполял на домашнем компьютере:
- Процессор: Pentium Dual-Core T4300 2100 Mhz
- RAM: DDR2 2048Mb
Тесты на Windows Phone были выполнены на HTC 7 Mozart.
Подготовка к тестированию
Для тестирования использовался простой xml-файл. ID для каждого элемента генерировались рандомно, а количество записей различалось в зависимости от теста и составляло: 1, 10, 100, 1 000, 100 000 штук соответственно. Итоговый файл выглядел примерно следующим образом:
<?xml version="1.0"?>
<items>
<item id="433382426" />
<item id="1215581841" />
<item id="2085749980" />
........
<item id="363608924" />
</items>* This source code was highlighted with Source Code Highlighter.
Для уменьшение погрешностей каждый тест был выполен 100 раз и полученные данные усреднены. А для имитации некоторых действий над записью вызывался пустой метод ProcessId(id).
XmlDocument.Load
На мой взгляд реализация чтения данных этим способом наиболее простая и понятная. Но, как мы увидим в конце, достигается это уж очень большой ценой. Код метода следующий:
private static void XmlDocumentReader(string filename)
{
var doc = new XmlDocument();
doc.Load(filename);
XmlNodeList nodes = doc.SelectNodes("//item");
if (nodes == null)
throw new ApplicationException("invalid data");foreach (XmlNode node in nodes)
{
string id = node.Attributes["id"].Value;
ProcessId(id);
}
}* This source code was highlighted with Source Code Highlighter.
LINQ to XML
Использование Linq-to-XML также оставляет реализацию метода довольно простой и понятной.
private static void XDocumentReader(string filename)
{
XDocument doc = XDocument.Load(filename);
if (doc == null || doc.Root == null)
throw new ApplicationException("invalid data");foreach (XElement child in doc.Root.Elements("item"))
{
XAttribute attr = child.Attribute("id");
if (attr == null)
throw new ApplicationException("invalid data");string id = attr.Value;
ProcessId(id);
}
}* This source code was highlighted with Source Code Highlighter.
XmlReader
Ну и наконец последний способ чтения данных из XML — использование XmlTextReader. Стоит сказать, что этот метод самый сложный для понимания. В процессе чтения xml-файла вы двигаетесь по нему сверху вниз (без возможности движения в обратном направлении), и вам каждый раз необходимо проверять, те ли данные вам нужно извлечь? Соответственно, код метода выглядит так:
private static void XmlReaderReader(string filename)
{
using (var reader = new XmlTextReader(filename))
{
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
if (reader.Name == "item")
{
reader.MoveToAttribute("id");
string id = reader.Value;
ProcessId(id);
}
}
}
}
}* This source code was highlighted with Source Code Highlighter.
* Для упрощения, в методах были опущены проверки.
Результаты для Desktop
Ниже представлены результаты тестирования. Для запуска каждого теста время измерялось отедельно и затем усреднялось. Время в таблице в миллисекундах.
1 | 10 | 100 | 1 000 | 10 000 | 100 000 | |
XmlDocument | 0,59 | 0,5 | 0,67 | 2,49 | 21,73 | 398,91 |
XmlReader | 0,51 | 0,47 | 0,55 | 1,31 | 8,62 | 79,65 |
Linq to XML | 0,57 | 0,59 | 0,64 | 2,09 | 15,6 | 192,66 |
Как видно из таблицы, XmlReader при чтении больших xml файлов, выигрывает в производительности Linq To XML в 2,42 раза, а XmlDocument в более чем 5 раз!
Тестирование на Windows Phone
Теперь настало время провести тесты на телефоне. Стоит заметить, что на Windows Phone установлена более старая версия .Net Framework'а, поэтому метод с использованием XmlDocument.Load не работает, а код для XmlReader пришлось немного переписать:
private static void XmlReaderReader(string filename)
{
using (var reader = XmlReader.Create(filename)) {
while (reader.Read()) {
if (reader.NodeType == XmlNodeType.Element) {
if (reader.Name == "item") {
reader.MoveToAttribute("id");
string id = reader.Value;
ProcessId(id);
}
}
}
}
}* This source code was highlighted with Source Code Highlighter.
Результаты для Windows Phone
Предсказуемо, что и на телефоне быстрее оказался XmlReader. Но в отличии от настольного компьютера, разница в производительности на больших файлах у них различна. На телефоне XmlReader быстрее LINQ to XML в 1,91 раз, а на десктопе в 2,42 раза.
1 | 10 | 100 | 1 000 | 10 000 | 100 000 | |
XmlReader | 1,67 | 1,74 | 3,19 | 19,5 | 173,84 | 1736,18 |
Linq to XML | 1,73 | 2,21 | 4,75 | 31,39 | 314,39 | 3315,13 |
Разница в скорости чтения 100 элементов из файла на Desktop и Windows Phone.
Разница в скорости чтения 100 000 элементов из файла на Desktop и Windows Phone.
Как можно видеть, скорость чтения данных на телефоне и настольном компьютере, в зависимости от объема данных, изменяется нелинейно. Интересно узнать почему это так?
Заключение
Как мы вяснили, самым произоводительным способом чтения данных из xml является использование XmlReader'a вне зависимости от платформы. Но неудобство его использования заключается в довольно сложном способое выборки данных — нам каждый раз приходиться проверять на каком элементе стоит указатель.
Если же для вас производительность не является краеугольным камнем, а главное — ясность и простота сопровождаемости кода, то наиболее подходящим является использование LINQ to XML. Также необходимо стараться избегать использования XmlDocument.Load в рабочих проектах из-за его низкой производительности.
P.S. Стоит упомянуть, что на написание всего этого меня вдохновила эта статья.
Автор: azhidkov