Очень низкая производительность десериализации с использованием сериализатора контрактов данных в приложении Silverlight

Вот такая ситуация:

Приложение Silverlight 3 обращается к службе WCF, размещенной на asp.net, чтобы получить список элементов для отображения в сетке. Когда список передается клиенту, он кэшируется в изолированном хранилище. Это делается с помощью DataContractSerializer для сериализации всех этих объектов в поток, который затем архивируется и затем шифруется. Когда приложение перезапускается, оно сначала загружается из кеша (в обратном порядке) и десериализует объекты с помощью метода DataContractSerializer.ReadObject (). Все это прекрасно работало во всех сценариях до недавнего времени, при этом весь путь «загрузка из кеша» (расшифровка / распаковка / десериализация) занимал не более сотен миллисекунд.

На некоторых машинах разработки, но не на всех (все машины с Windows 7) процесс десериализации - то есть вызов ReadObject (stream) занимает несколько минут и, кажется, блокирует всю машину, НО ТОЛЬКО ПРИ ЗАПУСКЕ В ОТЛАДЧИКЕ в VS2008. Выполнение кода конфигурации отладки вне отладчика не вызывает проблем.

Одна вещь, которая кажется подозрительной, заключается в том, что когда вы включаете остановку для исключений, вы можете видеть, что ReadObject () выдает много-много исключений System.FormatException, указывающих, что число было в неправильном формате. Когда я выключаю «Just My Code», тысячи таких сообщений выводятся на экран. Ни один не остается без присмотра. Это происходит как при обратном чтении из кеша, так и при десериализации по завершении вызова веб-службы для получения данных из службы WCF. ОДНАКО эти же исключения возникают на моем компьютере для разработки портативных компьютеров, который вообще не испытывает медлительности. И FWIW, мой ноутбук действительно старый, а мой настольный компьютер - зверь с 4 ядрами и 6 ГБ оперативной памяти.

Опять же, никаких проблем, если не запускать отладчик в VS2008. Кому-нибудь еще кажется это? Есть предположения?

Вот ссылка на отчет об ошибке: https://connect.microsoft.com/VisualStudio/feedback/details/539609/very-slow-performance-deserializing-using-datacontractserializer-in-a-silverlight-application-only-in-debugger

РЕДАКТИРОВАТЬ: теперь я знаю, откуда берутся исключения FormatExceptions. Кажется, что они созданы «по замыслу» - они возникают, когда у меня сериализуются двойники, которые являются double.NaN, так что этот xml выглядит как NaN ... Кажется, что DCS пытается проанализировать значение как число, но это не удается. за исключением, а затем ищет "NaN" и т. д. al. и обрабатывает их. Моя проблема не в том, что это не работает ... это работает ... просто это полностью калечит отладчик. Кто-нибудь знает, как настроить отладчик / vs2008sp1, чтобы справиться с этим более эффективно.


person caryden    schedule 05.03.2010    source источник
comment
Я думаю, у вас достаточно подробностей, чтобы написать отчет об ошибке на странице connect.microsoft.com/visualstudio . Когда вы сообщили, опубликуйте URL отчета об ошибке здесь, чтобы мы могли проголосовать за него.   -  person John Saunders    schedule 05.03.2010
comment
Мне любопытно, что вы используете для создания zip-архива для хранения, я не знал, были ли какие-то библиотеки для этого перенесены на Silverlight, или вы создали свои собственные?   -  person AnthonyWJones    schedule 05.03.2010
comment
@AnthonyWJones - Я использовал Silverlight SharpZipLib на Codeplex. codeplex.com/slsharpziplib - он не обладает всеми функциями полной версии, но работает нормально. для меня. У меня есть CacheManager, который перемещает данные в кеш и из него. Если вам нужно больше, просто дайте мне знать ...   -  person caryden    schedule 05.03.2010
comment
Вы спрашиваете, почему DCS выдает так много исключений? Или вам интересно, что ваш код работает медленнее, когда отладчик перехватывает исключения?   -  person Gabe    schedule 16.03.2010
comment
@gabe - Все, что мне нужно, это возможность запускать отладчик, не дожидаясь 2 минут, пока приложение запустится (загрузится). FWIW исключения не обрабатываются, а приложение функционирует. Я смог понять, почему - см. Правку выше. Но что мне нужно, так это способ, чтобы такое поведение не мешало моим усилиям по разработке останавливаться.   -  person caryden    schedule 16.03.2010
comment
нет Silverlight, но я получаю очень похожее поведение (и я в недоумении) - ›stackoverflow.com/questions/2876955/   -  person JohnIdol    schedule 26.05.2010


Ответы (5)


картден

Вместо этого вы можете подумать о переходе на XMLSerializer. Вот что я со временем определил:

Классы XMLSerializer и DataContractSerializer предоставляют простые средства сериализации и десериализации графов объектов в XML и обратно.

Ключевые различия:
1.
Полезная нагрузка XMLSerializer намного меньше, чем у DCS. если вы используете [XmlAttribute] вместо [XmlElement]
DCS всегда хранит значения как элементы
2.
DCS - это «согласие», а не «отказ»
С В DCS вы явно отмечаете, что вы хотите сериализовать, с помощью [DataMember]
С DCS вы можете сериализовать любое поле или свойство, даже если они помечены как защищенные или закрытые
В DCS вы можете использовать [IgnoreDataMember], чтобы иметь сериализатор игнорирует определенные свойства
С помощью XMLSerializer общедоступные свойства сериализуются, и сеттеры требуют десериализации
С XmlSerializer вы можете использовать [XmlIgnore], чтобы сериализатор игнорировал общедоступные свойства
3.
БУДЬТЕ ВНИМАТЕЛЬНЫ! DCS.ReadObject НЕ вызывает конструкторы во время десериализации
Если вам нужно выполнить инициализацию, DCS поддерживает следующие обработчики обратного вызова:
[OnDeserializing], [OnDeserialized], [OnSerializing], [OnSerialized]
(также полезно для решения проблем с управлением версиями)

Если вам нужна возможность переключаться между двумя сериализаторами, вы можете использовать оба набора атрибутов одновременно, например:

[DataContract]
[XmlRoot]
    public class ProfilePerson : NotifyPropertyChanges
    {
[XmlAttribute]
[DataMember]
        public string FirstName { get { return m_FirstName; } set { SetProperty(ref m_FirstName, value); } }
        private string m_FirstName;
[XmlElement]
[DataMember]
        public PersonLocation Location { get { return m_Location; } set { SetProperty(ref m_Location, value); } }
        private PersonLocation m_Location = new PersonLocation(); // Should change over time
[XmlIgnore]
[IgnoreDataMember]
        public Profile ParentProfile { get { return m_ParentProfile; } set { SetProperty(ref m_ParentProfile, value); } }
        private Profile m_ParentProfile = null;

        public ProfilePerson()
        {
        }
    }

Также обратите внимание на мой класс Serializer, который может переключаться между ними:

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace ClassLibrary
{
    // Instantiate this class to serialize objects using either XmlSerializer or DataContractSerializer
    internal class Serializer
    {
        private readonly bool m_bDCS;

        internal Serializer(bool bDCS)
        {
            m_bDCS = bDCS;
        }

        internal TT Deserialize<TT>(string input)
        {
            MemoryStream stream = new MemoryStream(input.ToByteArray());
            if (m_bDCS)
            {
                DataContractSerializer dc = new DataContractSerializer(typeof(TT));
                return (TT)dc.ReadObject(stream);
            }
            else
            {
                XmlSerializer xs = new XmlSerializer(typeof(TT));
                return (TT)xs.Deserialize(stream);
            }
        }

        internal string Serialize<TT>(object obj)
        {
            MemoryStream stream = new MemoryStream();
            if (m_bDCS)
            {
                DataContractSerializer dc = new DataContractSerializer(typeof(TT));
                dc.WriteObject(stream, obj);
            }
            else
            {
                XmlSerializer xs = new XmlSerializer(typeof(TT));
                xs.Serialize(stream, obj);
            }

            // be aware that the Unicode Byte-Order Mark will be at the front of the string
            return stream.ToArray().ToUtfString();
        }

        internal string SerializeToString<TT>(object obj)
        {
            StringBuilder builder = new StringBuilder();
            XmlWriter xmlWriter = XmlWriter.Create(builder);
            if (m_bDCS)
            {
                DataContractSerializer dc = new DataContractSerializer(typeof(TT));
                dc.WriteObject(xmlWriter, obj);
            }
            else
            {
                XmlSerializer xs = new XmlSerializer(typeof(TT));
                xs.Serialize(xmlWriter, obj);
            }

            string xml = builder.ToString();
            xml = RegexHelper.ReplacePattern(xml, RegexHelper.WildcardToPattern("<?xml*>", WildcardSearch.Anywhere), string.Empty);
            xml = RegexHelper.ReplacePattern(xml, RegexHelper.WildcardToPattern(" xmlns:*\"*\"", WildcardSearch.Anywhere), string.Empty);
            xml = xml.Replace(Environment.NewLine + "  ", string.Empty);
            xml = xml.Replace(Environment.NewLine, string.Empty);
            return xml;
        }
    }
}
person Jim McCurdy    schedule 08.03.2010
comment
Спасибо, но DataContractSerializer ОТЛИЧНО работает в производственной среде и до недавнего времени находился в отладке. Я не могу оправдать переделку всего моего уровня обслуживания, чтобы использовать для этого другой сериализатор, когда то, что у меня есть, отлично работает в производстве. НО это огромный PITB, который убивает производительность в режиме отладки - отсюда и награда. - person caryden; 16.03.2010

Это предположение, но я думаю, что он работает медленно в режиме отладки, потому что для каждого исключения он выполняет некоторые действия, чтобы показать исключение в окне отладки и т. Д. Если вы работаете в режиме выпуска, эти дополнительные шаги не выполняются. .

Я никогда не делал этого, поэтому я действительно не знаю id, что это сработает, но пробовали ли вы просто настроить эту одну сборку для запуска в режиме выпуска, в то время как все остальные настроены на отладку? Если я прав, это может решить вашу проблему. Если я ошибаюсь, то вы тратите только 1 или 2 минуты.

person Gabriel McAdams    schedule 16.03.2010
comment
Ты прав. Я смог подтвердить, что причина именно в этом, аналогичным образом. Более серьезная проблема заключалась в том, что вызывает FormatExceptions. - person caryden; 17.03.2010

Что касается вашей проблемы с отладкой, вы пытались отключить помощника по исключениям? (Инструменты> Параметры> Отладка> Включить помощника по исключениям).

Еще одним моментом должна быть обработка исключений в «Отладка»> «Исключения»: вы можете отключить необработанные пользователем данные для CLR или только снять отметку с исключения System.FormatException.

person JoeBilly    schedule 16.03.2010

Хорошо - я понял корень проблемы. Это было то, о чем я упоминал в РЕДАКТИРОВАНИИ основного вопроса. Проблема заключалась в том, что в xml правильно сериализовались двойники со значением double.NaN. Я использовал эти значения, чтобы указать «на», когда знаменатель был 0D. Пример: ROE (рентабельность капитала = чистая прибыль / средний капитал), когда средний капитал равен 0D, будет сериализован как:

<ROE>NaN</ROE> 

Когда DCS пытается десериализовать его, очевидно, он сначала пытается прочитать номер, а затем перехватывает исключение, когда это не удается, а затем обрабатывает NaN. Проблема в том, что в режиме DEBUG это, кажется, создает много накладных расходов.

Решение: поменял свойство на удвоение? и установите для него значение null вместо NaN. Теперь все происходит мгновенно в режиме DEBUG. Спасибо всем за помощь.

person caryden    schedule 17.03.2010

Попробуйте отключить некоторые надстройки IE. В моем случае панель инструментов LastPass убила мою отладку Silverlight. Мой компьютер зависал на несколько минут каждый раз, когда я взаимодействовал с Visual Studio после точки останова.

person Josh Mouch    schedule 03.11.2011