- PVSM.RU - https://www.pvsm.ru -
Не так давно мне пришлось делать приложение для Windows Phone работающее с xml-файлами. Всё было неплохо, но когда в файле стало ~100.000 записей, чтение их занимало ну уж очень много времени. И я решил сравненить производительность различных способов чтения данных из xml возможных на платформе .Net.
Для лучшего понимания показателей проведенных тестов стоить рассказать на чём они были проведены. Тесты из разряда «Desktop» я выполял на домашнем компьютере:
Тесты на 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 [1].
Для уменьшение погрешностей каждый тест был выполен 100 раз и полученные данные усреднены. А для имитации некоторых действий над записью вызывался пустой метод ProcessId(id).
На мой взгляд реализация чтения данных этим способом наиболее простая и понятная. Но, как мы увидим в конце, достигается это уж очень большой ценой. Код метода следующий:
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 [1].
Использование 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 [1].
Ну и наконец последний способ чтения данных из 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 [1].
* Для упрощения, в методах были опущены проверки.
Ниже представлены результаты тестирования. Для запуска каждого теста время измерялось отедельно и затем усреднялось. Время в таблице в миллисекундах.
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 установлена более старая версия .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 [1].
Предсказуемо, что и на телефоне быстрее оказался 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. Стоит упомянуть, что на написание всего этого меня вдохновила эта статья [2].
Автор: azhidkov
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/windows-phone/2580
Ссылки в тексте:
[1] Source Code Highlighter: http://virtser.net/blog/post/source-code-highlighter.aspx
[2] эта статья: http://www.nearinfinity.com/blogs/joe_ferner/performance_linq_to_sql_vs.html
Нажмите здесь для печати.