Re[10]: Сериализация в дотнете 2 - самопальный датасет
От: VladD2 Российская Империя www.nemerle.org
Дата: 20.04.03 19:12
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Зачем обертывать чужой парсер, если есть родной?


Я же сказал. Если дело в XMLReader... Не факт что он действительно работает быстро. Это нужно проверять.

AVK>Результат очень хороший. Не забывай что это xml.


Блин, твой необоснованный оптимизм просто поражает. Посмотри на результат Трентора. Он между прочем просто текст писал. Если в его код вставить теги и именами олей, то будет тот же эффект, что и с XMLWriter. С загрузкой то же фигня.
... << RSDN@Home 1.0 beta 6a >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[8]: Сериализация в дотнете 2 - самопальный датасет
От: VladD2 Российская Империя www.nemerle.org
Дата: 20.04.03 19:12
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Влад, но ведь форматтеры выполняют куда больше работы и они куда универсальнее. Просто по моему вместо того чтобы сделать нормальные сериализаторы они предлагают пользоваться форматтерами.


Понятно, что универсальнее. Вот только мне почему-то не хочется платить десятикратными тормозами за эту универсальность. Особенно если речь идет о передачи данных по сети. Где мне полностью плевать на версии, а нужна масимальная скорость.

Всю эту универсальность можно было бы реализовать и без таких страшных провалов производительности. Все что нужно было сделать — это генерировать код сериализации помпилятором, а возможно просто более качественно написать код сериализации. Дтя того же датасета ведь сереализация написана вручную. Но почему-то только в хмл, и почему то тормоза такие же как при использовании SOAP-форматера.

AVK>VDТакой же эффект происходит при некоторый перестановках тестов. Причем на другие способы перестановки влияют незначительно. Что-то там не так.


AVK>Ну так исходники есть.


И что толку с них?

AVK>IT вроде ротор собрал — можно попросить его прогнать форматтер профайлером и посмотреть где кака.


У меня нет нормального профайлера. Если есть что на примете, подкинь...


А вообще, сделать это точно надо. Мне уже просто интересно, что они там такое натварили.
... << RSDN@Home 1.0 beta 6a >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: Сериализация в дотнете 2 - самопальный датасет
От: VladD2 Российская Империя www.nemerle.org
Дата: 21.04.03 01:53
Оценка: 7 (3)
Здравствуйте, VladD2, Вы писали:

Модифицированная версия. Учтены замечания ТК.

Список изменений:

1. Поддерживаются DbNull-значения.
2. Поддерживаются версии строк.
3. Код разбит так чтобы можно было сериализовать отдельные DataTable-ы. Введены функции:
    static void SerializeDataSet(Stream stream, DataSet ds);
    static void SerializeDataTable(BinaryWriter bw, DataTable dt);
    static void SerializeDataTable(Stream stream, DataTable dt);
    static DataTable DeserializeTable(BinaryReader br);
    static DataTable DeserializeTable(Stream stream);
    static DataSet DeserializeDataSet(Stream stream);

4. Модифицирован тест:
4.1. Тесты переставлены местами и повторяются по 3 раза.
4.2. В DataTable вносятся записи поля в которых периодически содержат DbNull.
4.3. Добавлены записи с разными версиями (удаленные, добавленные, измененные, немодифицированные).

Результат:
1. Время сериализации несколько увеличилось, но не существенно (один фиг быстрее чем у Трентора).
2. Вырос объем данных: По 2 байта на строку плюс если есть другая версия, то ее размер.


Что не поддерживается сериализатором?
1. Индексы и представления (view). Сделать можно, но это тоже время...
2. Вычисляемые поля.
3. Констрэйны.

Возможно я пропустил еще что-то...

Но это все ерунда, так как много времени сериализация остального незаймет, так как остались исключительно декларативные вещи.

Уже в этом виде датасет можно смело использовать в 90% приложений, так как для предачи данных извращения с многотабличностью ненужны. Индексы можно строить и на клиенте (должен же он хоть что-то делать).


Буду признателен за тестирование и отлов багов.



Результаты:
  RsdnDetaSerializer
Serialization:   150 ms   Loading:   450   Length:   1 053 824 bytes
Serialization:   140 ms   Loading:   451   Length:   1 053 824 bytes
Serialization:   140 ms   Loading:   451   Length:   1 053 824 bytes

  BinaryFormatter
Serialization: 2 123 ms   Loading: 7 631   Length:   6 985 259 bytes
Serialization: 1 773 ms   Loading: 7 701   Length:   6 985 259 bytes
Serialization: 1 773 ms   Loading: 7 701   Length:   6 985 259 bytes

  XmlSerializer
Serialization: 1 793 ms   Loading: 7 591   Length:   9 309 050 bytes
Serialization: 1 652 ms   Loading: 7 561   Length:   9 309 050 bytes
Serialization: 1 662 ms   Loading: 7 561   Length:   9 309 050 bytes

  SoapFormatter
Serialization: 2 594 ms   Loading: 9 233   Length:  11 001 874 bytes
Serialization: 2 573 ms   Loading: 9 173   Length:  11 001 874 bytes
Serialization: 2 554 ms   Loading: 9 163   Length:  11 001 874 bytes

Done!


Кода:
using System;
using System.IO;
using System.Data;
using System.Collections;

namespace RSDN
{
    class DetaSerializer
    {
        static readonly DataRowVersion[] _aryVer = new DataRowVersion[2] 
                { DataRowVersion.Original, DataRowVersion.Current };

        // Массив для перемапливания TypeCode на Type
        // TypeCode это перечисление включающее константы для базовых типов.
        static readonly Type[] _TypeMap;

        // Статический конструктор. Нужен для инициализации _TypeMap.
        static DetaSerializer()
        {
            // TypeCode.DBNull и TypeCode.Empty пропущены, так как они 
            // по сути, не являются типами.
            _TypeMap = new Type[(int)TypeCode.String + 1];
            _TypeMap[(int)TypeCode.Object] = typeof(Object);
            _TypeMap[(int)TypeCode.Boolean] = typeof(Boolean);
            _TypeMap[(int)TypeCode.Char] = typeof(Char);
            _TypeMap[(int)TypeCode.SByte] = typeof(SByte);
            _TypeMap[(int)TypeCode.Byte] = typeof(Byte);
            _TypeMap[(int)TypeCode.Int16] = typeof(Int16);
            _TypeMap[(int)TypeCode.UInt16] = typeof(UInt16);
            _TypeMap[(int)TypeCode.Int32] = typeof(Int32);
            _TypeMap[(int)TypeCode.UInt32] = typeof(UInt32);
            _TypeMap[(int)TypeCode.Int64] = typeof(Int64);
            _TypeMap[(int)TypeCode.UInt64] = typeof(UInt64);
            _TypeMap[(int)TypeCode.Single] = typeof(Single);
            _TypeMap[(int)TypeCode.Double] = typeof(Double);
            _TypeMap[(int)TypeCode.Decimal] = typeof(Decimal);
            _TypeMap[(int)TypeCode.DateTime] = typeof(DateTime);
            _TypeMap[(int)TypeCode.String] = typeof(String);
        }

        ///////////////////////////////////////////////////////////////////////
        // Сериализация.

        public static void SerializeDataSet(Stream stream, DataSet ds)
        {
            BinaryWriter bw = new BinaryWriter(stream);
            DataTableCollection tables = ds.Tables;

            bw.Write(ds.DataSetName);
            bw.Write(tables.Count);
            foreach(DataTable dt in tables)
            {    // Вообще-то foreach-и лучше на всякий пожарный избегать.
                // Но мне было в лом.
                SerializeDataTable(bw, dt);
            }
        }

        public static void SerializeDataTable(BinaryWriter bw, DataTable dt)
        {
            DataColumnCollection columns = dt.Columns;
            int iColCount = columns.Count;
            TypeCode[] colTypeCodes = new TypeCode[iColCount];

            bool[] aryIsNullabl = new bool[iColCount];

            // Имя таблицы
            bw.Write(dt.TableName);

            bw.Write(iColCount);
            // Получаем и записываем описание колонок.
            for(int i = 0; i < iColCount; i++)
            {
                DataColumn dc = columns[i];
                // Получаем TypeCode для типа обрабатываемой колонки.
                TypeCode tc    = Type.GetTypeCode(dc.DataType);
                // Запоминаем TypeCode колонки в соотвествующем массиве.
                colTypeCodes[i] = tc;
                bw.Write(dc.ColumnName);
                // Записываем TypeCode как Int32. Можно было бы и 
                // сэкономить 3 байта. :)
                bw.Write((Int32)tc);

                // Создаем массив информации о поддержке колонками DBNull
                aryIsNullabl[i] = dc.AllowDBNull;
            }


            // Записываем битовое поле описывающее колонки поддерживающие
            // DBNull. Если бит поднят, значит, колонка поддерживает DBNull.
            BitArray bitsNull = new BitArray(aryIsNullabl);
            byte[] byteNull = new byte[(iColCount + 7) / 8];
            bitsNull.CopyTo(byteNull, 0);
            bw.Write(byteNull);

            ///////////////////////////////////////////////////////////////
            // add data

            // count rows
            bw.Write(dt.Rows.Count);

            // Записываем строки
            foreach(DataRow dr in dt.Rows)
            {
                byte verFlags = 0;
                int iVerStart;
                int iVerEnd;
                // Разбираемся, какие версии нужно писать.
                // Всего есть два варианта: Original и Current
                DataRowState state = dr.RowState;
                switch(state)
                {
                        // Original + Current и они равны!
                    case DataRowState.Unchanged: 
                        iVerStart = 0;
                        iVerEnd = 0;
                        verFlags = 0;
                        break;
                    case DataRowState.Deleted: // Только Original
                        iVerStart = 0;
                        iVerEnd = 0;
                        verFlags = 1;
                        break;
                    case DataRowState.Added: // Только Current
                        iVerStart = 1;
                        iVerEnd = 1;
                        verFlags = 2;
                        break;
                        // Original + Current и они НЕ равны!
                    case DataRowState.Modified:
                        iVerStart = 0;
                        iVerEnd = 1;
                        verFlags = 3;
                        break;
                    default:
                        throw new ApplicationException(
                            "Недопустимое состояние строки: " + state.ToString());
                }

                // Пишем описание версий. Временно, так как на этом мы
                // теряем байт на строку. Куда лучше писать дополнительные два
                // бита в битовое поле DbNull (хотя это и не красиво).
                bw.Write(verFlags);

                // Записываем версии текущей строки. Всего их может быть две.
                // в принципе можно было бы для случая DataRowState.Modified
                // писать только дельту данных. Но это как-нибудь потом. :)
                for(int iVetIndex = iVerStart; iVetIndex <= iVerEnd; iVetIndex++)
                {
                    DataRowVersion drv = _aryVer[iVetIndex];

                    // Создаем и заполняем битовое поле. Если бит поднят,
                    // значит, соответствующая колонка содержит DBNull.
                    bitsNull.SetAll(false);
                    for(int i = 0; i < iColCount; i++)
                    {
                        if(dr[i, drv] == DBNull.Value)
                            bitsNull.Set(i, true);
                    }
                    bitsNull.CopyTo(byteNull, 0);
                    // Записываем битовое поле в стрим.
                    bw.Write(byteNull);

                    // Перебираем колонки и пишем данные...
                    for(int i = 0; i < iColCount; i++)
                    {
                        // Если колонка содержит DBNull, записывать ее значение 
                        // ненужно.
                        object data = dr[i, drv];
                        if(data == DBNull.Value) // Учитываем версию!
                            continue;

                        // Записываем данные ячейки.
                        switch(colTypeCodes[i]) 
                        {    // Каждому типу соответствует переопределенная функция...
                            case TypeCode.Boolean: bw.Write((Boolean)data); break;
                            case TypeCode.Char: bw.Write((Char)data); break;
                            case TypeCode.SByte: bw.Write((SByte)data); break;
                            case TypeCode.Byte: bw.Write((Byte)data); break;
                            case TypeCode.Int16: bw.Write((Int16)data); break;
                            case TypeCode.UInt16: bw.Write((UInt16)data); break;
                            case TypeCode.Int32: bw.Write((Int32)data); break;
                            case TypeCode.UInt32: bw.Write((UInt32)data); break;
                            case TypeCode.Int64: bw.Write((Int64)data); break;
                            case TypeCode.UInt64: bw.Write((UInt64)data); break;
                            case TypeCode.Single: bw.Write((Single)data); break;
                            case TypeCode.Double: bw.Write((Double)data); break;
                            case TypeCode.Decimal: bw.Write((Decimal)data); break;
                            case TypeCode.DateTime:
                                // Для DateTime приходится выпендриваться особым образом.
                                bw.Write(((DateTime)(data)).ToFileTime());
                                break;
                            case TypeCode.String: bw.Write((String)data); break;
                            default:
                                // На всякий случай пробуем записать неопознанный тип
                                // виде строки.
                                bw.Write(data.ToString());
                                break;
                        }
                    }
                }
            }
        }

        public static void SerializeDataTable(Stream stream, DataTable dt)
        {
            BinaryWriter bw = new BinaryWriter(stream);
            SerializeDataTable(bw, dt);
        }

        ///////////////////////////////////////////////////////////////////////
        // Десериализация

        public static DataTable DeserializeTable(BinaryReader br)
        {
            DataColumn dc;
            DataRow dr;

            // Имя DataTable тоже передается в качестве параметра конструктора.
            DataTable dt = new DataTable(br.ReadString());
            dt.BeginLoadData();

            int iColCount = br.ReadInt32();

            // В этом массив будут записаны TypeCode-ы для колонок DataTable-а.
            TypeCode[] colTypeCodes = new TypeCode[iColCount];
            // А в этот массив будут записаны типы (Type) колонок DataTable-а
            // соотвествующие TypeCode-ам.
            for(int c = 0; c < iColCount; c++)
            {
                string colName = br.ReadString();
                // Считываем TypeCode.
                TypeCode tc = (TypeCode)br.ReadInt32();
                // Помещаем TypeCode в массив для дальнейшего использования.
                colTypeCodes[c] = tc;
                // Получаем (через мап) тип соответствующий TypeCode-у.
                Type type = _TypeMap[(int)tc];
                // Создаем колонку с полученным именем и типом.
                dc = new DataColumn(colName, type);
                dt.Columns.Add(dc);
            }

                
            // Считываем список nullabl-колонок.
            int iBitLenInBytes = (iColCount + 7) / 8;
            byte[] byteNull = new byte[iBitLenInBytes];
            br.Read(byteNull, 0, iBitLenInBytes);
            BitArray bitsNull = new BitArray(byteNull);
            bitsNull.Length = iColCount;
            bool[] aryIsNullabl = new bool[iColCount];
            bitsNull.CopyTo(aryIsNullabl, 0);

            object[] ad = new object[iColCount];


            int counRows = br.ReadInt32();
            DataRowCollection rows = dt.Rows;
            for(int r = 0; r < counRows; r++)
            {
                // Читаем описание версий. Временно, так как на этом мы
                // теряем байт на строку.
                byte verFlags = br.ReadByte();
                int iVerStart;
                int iVerEnd;
                DataRowState drs;
                switch(verFlags)
                {
                        // Original + Current и они равны!
                    case 0: // DataRowState.Unchanged
                        iVerStart = 0;
                        iVerEnd = 0;
                        drs = DataRowState.Unchanged;
                        break;
                    case 1: // DataRowState.Deleted Только Original
                        iVerStart = 0;
                        iVerEnd = 0;
                        drs = DataRowState.Deleted;
                        break;
                    case 2: // DataRowState.Added Только Current
                        iVerStart = 1;
                        iVerEnd = 1;
                        drs = DataRowState.Added;
                        break;
                        // Original + Current и они НЕ равны!
                    case 3: // DataRowState.Modified
                        iVerStart = 0;
                        iVerEnd = 1;
                        drs = DataRowState.Modified;
                        break;
                    default:
                        throw new ApplicationException(
                            "Недопустимое состояние строки. Сбой при загрузке.");
                }
                
                // Считываем данные.
                dr = dt.NewRow();
                rows.Add(dr);
                dr.BeginEdit();

                // Считываем версии текущей строки.
                for(int iVetIndex = iVerStart; iVetIndex <= iVerEnd; iVetIndex++)
                {
                    br.Read(byteNull, 0, iBitLenInBytes);
                    bitsNull = new BitArray(byteNull);

                    for(int i = 0; i < iColCount; i++)
                    {
                        if(bitsNull.Get(i))
                        {
                            dr[i] = DBNull.Value;
                            continue;
                        }

                        switch(colTypeCodes[i])
                        {
                            case TypeCode.Boolean: dr[i] = br.ReadBoolean(); break;
                            case TypeCode.Char: dr[i] = br.ReadChar(); break;
                            case TypeCode.SByte: dr[i] = br.ReadSByte(); break;
                            case TypeCode.Byte: dr[i] = br.ReadByte(); break;
                            case TypeCode.Int16: dr[i] = br.ReadInt16(); break;
                            case TypeCode.UInt16: dr[i] = br.ReadUInt16(); break;
                            case TypeCode.Int32: dr[i] = br.ReadInt32(); break;
                            case TypeCode.UInt32: dr[i] = br.ReadUInt32(); break;
                            case TypeCode.Int64: dr[i] = br.ReadInt64(); break;
                            case TypeCode.UInt64: dr[i] = br.ReadUInt64(); break;
                            case TypeCode.Single: dr[i] = br.ReadSingle(); break;
                            case TypeCode.Double: dr[i] = br.ReadDouble(); break;
                            case TypeCode.Decimal: dr[i] = br.ReadDecimal(); break;
                            case TypeCode.DateTime:
                                dr[i] = DateTime.FromFileTime(br.ReadInt64());
                                break;
                            case TypeCode.String: dr[i] = br.ReadString(); break;
                            default:
                                dr[i] = Convert.ChangeType(br.ReadString(), 
                                    colTypeCodes[i]);
                                break;
                        }
                    }
                    if(iVetIndex == 0)
                    {
                        dr.AcceptChanges();
                        if(iVerEnd > 0)
                            dr.BeginEdit();
                    }
                }

                if(drs == DataRowState.Deleted)
                    dr.Delete();

                dr.EndEdit();
            }
            dt.EndLoadData();
            return dt;
        }

        public static DataTable DeserializeTable(Stream stream)
        {
            BinaryReader br = new BinaryReader(stream);
            return DeserializeTable(br);
        }

        public static DataSet DeserializeDataSet(Stream stream)
        {
            BinaryReader br = new BinaryReader(stream);
            // Считываем имя DataSet и создаем его...
            DataSet ds = new DataSet(br.ReadString());
            //ds.BeginInit();
            
            int counTables = br.ReadInt32();

            DataTable dt;
            for(int t = 0; t < counTables; t++)
            {
                dt = DeserializeTable(br);
                ds.Tables.Add(dt);
            }
            //ds.AcceptChanges(); с версиями так нельзя... :)
            //ds.EndInit();
            return ds;
        }
    }
}


Тест:
using System;
using System.Data;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization.Formatters.Soap;
using RSDN;

namespace SerializeTest
{
    class Class1
    {

        static bool IsOriginalVer(DataRow dr)
        {
            DataRowState state = dr.RowState;
            switch(state)
            {
                    // Original + Current и они равны!
                case DataRowState.Unchanged: return true;
                case DataRowState.Deleted: return true;
                case DataRowState.Added: return false;
                case DataRowState.Modified: return true;
                default:
                    throw new ApplicationException(
                        "Недопустимое состояние строки: " + state.ToString());
            }
        }

        static bool IsCurrentVer(DataRow dr)
        {
            DataRowState state = dr.RowState;
            switch(state)
            {
                    // Original + Current и они равны!
                case DataRowState.Unchanged: return true;
                case DataRowState.Deleted: return false;
                case DataRowState.Added: return true;
                case DataRowState.Modified: return true;
                default:
                    throw new ApplicationException(
                        "Недопустимое состояние строки: " + state.ToString());
            }
        }

        static bool CmpDataSets(DataSet ds1, DataSet ds2)
        {
            if(!CmpDataTables(ds1.Tables[0], ds2.Tables[0]))
            {
                Console.WriteLine("DataTable НЕ одинаковые!!!");
                return false;
            }
            return true;
        }
        // Сравнивает два DataTable
        static bool CmpDataTables(DataTable dt1, DataTable dt2)
        {
            if(dt1.Rows.Count != dt1.Rows.Count)
                return false;
            if(dt1.Columns.Count != dt1.Columns.Count)
                return false;

            int iCols = dt1.Columns.Count;
            DataRowCollection rows1 = dt1.Rows;
            DataRowCollection rows2 = dt2.Rows;
            int iRows = rows1.Count;
            for(int r = 0; r < iRows; r++)
            {
                DataRow dr1 = rows1[r];
                DataRow dr2 = rows2[r];
                if(IsCurrentVer(dr1))
                {
                    if(!IsCurrentVer(dr2))
                        return false;
                    for(int i = 0; i < iCols; i++)
                    {
                        if(!dr1[i, DataRowVersion.Current].Equals(
                            dr2[i, DataRowVersion.Current]))
                            return false;
                    }
                }
                else
                    if(IsCurrentVer(dr2))
                        return false;

                if(IsOriginalVer(dr1))
                {
                    if(!IsOriginalVer(dr2))
                        return false;
                    for(int i = 0; i < iCols; i++)
                    {
                        if(!dr1[i, DataRowVersion.Original].Equals(
                            dr2[i, DataRowVersion.Original]))
                            return false;
                    }
                }
                else
                    if(IsOriginalVer(dr2))
                    return false;
            }
            return true;
        }

        // Вызывает суровую уборку мусора. :)
        static void ClearMem()
        {
            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }

        [STAThread]
        static void Main(string[] args)
        {
            DataSet ds = new DataSet("myDataSet");
            DataTable dt = new DataTable("Table1");
            dt.Columns.Add(new DataColumn("col0", typeof(Int32)));
            dt.Columns[0].AllowDBNull = false;
            dt.Columns.Add(new DataColumn("col1", typeof(string)));
            dt.Columns.Add(new DataColumn("col2", typeof(DateTime)));
            dt.Columns.Add(new DataColumn("col3", typeof(byte)));
            dt.Columns[3].AllowDBNull = false;

            DataRow dr;

            dt.BeginLoadData();

            for(int i = 0; i < 40000; i++)
            {
                dr = dt.NewRow();
                dr.BeginEdit();
                dr[0] = i;
                // В каждой 100-й строке колонка 1 заполняется DBNull-ом.
                dr[1] = i % 100 == 0 ? DBNull.Value : (object)("2test" + i.ToString());
                // В каждой 40-й строке колонка 2 заполняется DBNull-ом.
                dr[2] = i % 40 == 0 ? DBNull.Value : (object)DateTime.Now;
                dr[3] = (byte)i % 255;
                dt.Rows.Add(dr);
                if(i % 20 == 0)
                {
                    // Каждая 20 строка помечается как отредактированная
                    dr.AcceptChanges();
                    dr[0] = i;
                }
                else if(i % 21 == 0)
                {
                    // Каждая 21 строка помечается как удаленная
                    dr.AcceptChanges();
                    dr.Delete();
                }
                else if(i % 22 != 0)
                    // Все остальные строки кроме каждой 22 помечаются как 
                    // оригинальные (не модифицированные). Таким обрезом каждая 22 
                    // строка остается помеченной как добавленная.
                    dr.AcceptChanges();

                dr.EndEdit();
            }
            dt.EndLoadData();

            ds.Tables.Add(dt);
            //ds.AcceptChanges();


            //ds.Tables[0].Rows[0].HasVersion();

//            Console.WriteLine(ds.Tables[0].Rows[0].RowState);
//            ds.AcceptChanges();
//            Console.WriteLine(ds.Tables[0].Rows[0].RowState);


            RsdnDetaSerializer(ds);
            RsdnDetaSerializer(ds);
            RsdnDetaSerializer(ds);

            TestBinaryFormatter(ds);
            TestBinaryFormatter(ds);
            TestBinaryFormatter(ds);

            TestXmlSerializer(ds);
            TestXmlSerializer(ds);
            TestXmlSerializer(ds);

            TestSoapFormatter(ds);
            TestSoapFormatter(ds);
            TestSoapFormatter(ds);

            frmDataset frm = new frmDataset();
            frm.dataGrid1.DataSource = ds.Tables[0];
            frm.ShowDialog();
            
            Console.WriteLine("\nDone!");
            Console.ReadLine();
        }

        static void TestDataSetHandsSerializate(DataSet ds)
        {
            int start; // Используется для вычисления времени выполнения
            ClearMem(); // Вызов GC чтобы небыло налаженки...

            // Стрим для ручной сериализации через DetaSetCustomSerializer2.
            MemoryStream ms = new MemoryStream(1024);

            // Сериализация DataSetHandsSerializate
            start = Environment.TickCount;
            DataSetHandsSerializate.Serialize(ms, ds);
            int timeSerialize = Environment.TickCount - start;

            // Загрузка DataSetHandsSerializate
            ms.Seek(0, SeekOrigin.Begin);
            start = Environment.TickCount;
            DataSet dsLoaded = DataSetHandsSerializate.Deserialize(ms);
            Console.WriteLine(
                "\n  DataSetHandsSerializate\n"
                + "Serialization: {0,10:### ### ###} ms   "
                + "Loading: {1,10:### ### ###}\n"
                + "Length is:                  {2,10:### ### ###}",
                timeSerialize, Environment.TickCount - start, ms.Length);
            CmpDataSets(dsLoaded, ds);
        }

        static void RsdnDetaSerializer(DataSet ds)
        {
            int start; // Используется для вычисления времени выполнения
            ClearMem();

            // Стрим для ручной сериализации через DataSetHandsSerializate.
            MemoryStream ms = new MemoryStream(1024);

            // Сериализация DetaSetCustomSerializer2
            start = Environment.TickCount;
            DetaSerializer.SerializeDataSet(ms, ds);
            int timeSerialize = Environment.TickCount - start;

            // Загрузка DetaSetCustomSerializer2
            ms.Seek(0, SeekOrigin.Begin);
            start = Environment.TickCount;
            DataSet dsLoaded = DetaSerializer.DeserializeDataSet(ms);
            Console.WriteLine(
                "\n  RsdnDetaSerializer\n"
                + "Serialization: {0,10:### ### ###} ms   "
                + "Loading: {1,10:### ### ###}   "
                + "Length:  {2,10:### ### ###} bytes",
                timeSerialize, Environment.TickCount - start, ms.Length);

            // dsLoaded.Tables[0].Rows[2][0] = 123; // проверка CmpDataSets
            CmpDataSets(dsLoaded, ds);

            // тест.
            //frmDataset frm = new frmDataset();
            //frm.dataGrid1.DataSource = ds.Tables[0];
            //frm.ShowDialog();
        }

        static void TestBinaryFormatter(DataSet ds)
        {
            int start; // Используется для вычисления времени выполнения
            ClearMem();

            // Стрим для сериализации через BinaryFormatter
            MemoryStream ms = new MemoryStream(1024);
            BinaryFormatter bf = new BinaryFormatter();

            // Сериализация BinaryFormatter
            start = Environment.TickCount;
            bf.Serialize(ms, ds);
            int timeSerialize = Environment.TickCount - start;

            // Загрузка BinaryFormatter
            ms.Seek(0, SeekOrigin.Begin);
            start = Environment.TickCount;
            DataSet dsLoaded = (DataSet)bf.Deserialize(ms);
            Console.WriteLine(
                "\n  BinaryFormatter\n"
                + "Serialization: {0,10:### ### ###} ms   "
                + "Loading: {1,10:### ### ###}   "
                + "Length:  {2,10:### ### ###} bytes",
                timeSerialize, Environment.TickCount - start, ms.Length);
            CmpDataSets(dsLoaded, ds);
        }

        static void TestXmlSerializer(DataSet ds)
        {
            int start; // Используется для вычисления времени выполнения
            ClearMem();
            
            // Стрим для сериализации через XmlSerializer
            MemoryStream ms = new MemoryStream(1024);

            // Сериализация XmlSerializer
            start = Environment.TickCount;
            XmlSerializer serializer = new XmlSerializer(typeof(DataSet));
            serializer.Serialize(ms, ds);
            int timeSerialize = Environment.TickCount - start;

            // Загрузка XmlSerializer
            ms.Seek(0, SeekOrigin.Begin);
            start = Environment.TickCount;
            DataSet dsLoaded = (DataSet)serializer.Deserialize(ms);
            Console.WriteLine(
                "\n  XmlSerializer\n"
                + "Serialization: {0,10:### ### ###} ms   "
                + "Loading: {1,10:### ### ###}   "
                + "Length:  {2,10:### ### ###} bytes",
                timeSerialize, Environment.TickCount - start, ms.Length);
            CmpDataSets(dsLoaded, ds);
        }

        static void TestSoapFormatter(DataSet ds)
        {
            int start; // Используется для вычисления времени выполнения
            ClearMem();
            
            // Стрим для сериализации через SoapFormatter
            MemoryStream ms = new MemoryStream(1024);
            SoapFormatter sf = new SoapFormatter();

            // Сериализация SoapFormatter
            start = Environment.TickCount;
            sf.Serialize(ms, ds);
            int timeSerialize = Environment.TickCount - start;

            // Загрузка SoapFormatter
            ms.Seek(0, SeekOrigin.Begin);
            start = Environment.TickCount;
            DataSet dsLoaded = (DataSet)sf.Deserialize(ms);
            Console.WriteLine(
                "\n  SoapFormatter\n"
                + "Serialization: {0,10:### ### ###} ms   "
                + "Loading: {1,10:### ### ###}   "
                + "Length:  {2,10:### ### ###} bytes",
                timeSerialize, Environment.TickCount - start, ms.Length);
            CmpDataSets(dsLoaded, ds);
        }
    }
}
... << RSDN@Home 1.0 beta 6a >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re: Сериализация в дотнете 2 - самопальный датасет
От: TK Лес кывт.рф
Дата: 21.04.03 02:38
Оценка:
Hello, "VladD2"

> Итак, убил 5 часа на переписывания этого дела
Автор: Trantor
Дата: 15.04.03
под бинарный вид... отладку и тесты...

>

Кстати, в MSDN есть статья "Binary Serialization of ADO.NET Objects" хоть там и ничего особо специального и нет...
Posted via RSDN NNTP Server 1.5 beta
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Re[9]: Сериализация в дотнете 2 - самопальный датасет
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 21.04.03 06:32
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Всю эту универсальность можно было бы реализовать и без таких страшных провалов производительности. Все что нужно было сделать — это генерировать код сериализации помпилятором,


Это возможно если заранее известно какие конкретно объекты будут сериализоваться. А это приведет к XmlSerializer, для которого сериализуемые данные нужно заранее размечать.
... << RSDN@Home 1.0 beta 6a >>
AVK Blog
Re[11]: Сериализация в дотнете 2 - самопальный датасет
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 21.04.03 06:32
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Блин, твой необоснованный оптимизм просто поражает. Посмотри на результат Трентора. Он между прочем просто текст писал. Если в его код вставить теги и именами олей, то будет тот же эффект, что и с XMLWriter. С загрузкой то же фигня.


А при чем здесь запись? Вот если бы он читал.
... << RSDN@Home 1.0 beta 6a >>
AVK Blog
Re[12]: Сериализация в дотнете 2 - самопальный датасет
От: VladD2 Российская Империя www.nemerle.org
Дата: 21.04.03 10:07
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>А при чем здесь запись? Вот если бы он читал.


А он и читал. И запись и чтение на порядок быстрее чем... Вот только DbNull он не поддерживает, так что я его из тестов выбросил.
... << RSDN@Home 1.0 beta 6a >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[10]: Сериализация в дотнете 2 - самопальный датасет
От: VladD2 Российская Империя www.nemerle.org
Дата: 21.04.03 10:07
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Это возможно если заранее известно какие конкретно объекты будут сериализоваться. А это приведет к XmlSerializer, для которого сериализуемые данные нужно заранее размечать.


Вовсе нет. Просто код должен будет генерироваться для каждого типа плюс для массива содержащего этот тип. Если будет какой-нить object, нужно будет всего-лишь прочитать его тип и найти соотвествующий сериализатор. В общем хеш-таблицы нам помогут.

Интересно насколько тормозит сериализация в Яве?
... << RSDN@Home 1.0 beta 6a >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[2]: Сериализация в дотнете 2 - самопальный датасет
От: VladD2 Российская Империя www.nemerle.org
Дата: 21.04.03 10:07
Оценка:
Здравствуйте, TK, Вы писали:

TK>Кстати, в MSDN есть статья "Binary Serialization of ADO.NET Objects" хоть там и ничего особо специального и нет...


Там практически ничего нет. Я ее видел. Единственное что там сказано, что датасет сериализуется в хмл.
... << RSDN@Home 1.0 beta 6a >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[11]: Сериализация в дотнете 2 - самопальный датасет
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 21.04.03 11:22
Оценка:
Здравствуйте, VladD2, Вы писали:

VD>Вовсе нет. Просто код должен будет генерироваться для каждого типа плюс для массива содержащего этот тип. Если будет какой-нить object, нужно будет всего-лишь прочитать его тип и найти соотвествующий сериализатор. В общем хеш-таблицы нам помогут.


То есть ты предлагаешь генерить сериализатор при первом обращении к конкретному классу?

VD>Интересно насколько тормозит сериализация в Яве?


Вот как то не интересовался.
... << RSDN@Home 1.0 beta 6a >>
AVK Blog
Re[10]: Сериализация в дотнете 2 - самопальный датасет
От: mihailik Украина  
Дата: 21.04.03 15:09
Оценка:
VD>>Всю эту универсальность можно было бы реализовать и без таких страшных провалов производительности. Все что нужно было сделать — это генерировать код сериализации помпилятором,

AVK>Это возможно если заранее известно какие конкретно объекты будут сериализоваться. А это приведет к XmlSerializer, для которого сериализуемые данные нужно заранее размечать.



Вообще-то, можно было реализовывать ISerializable. Прямо там, где генерируется код для typed dataset, в Visual Studio. Можно было, но они на этом решили сэкономить. Ну и хрен с ним.
... << RSDN@Home 1.0 beta 6a >>
Re[12]: Сериализация в дотнете 2 - самопальный датасет
От: VladD2 Российская Империя www.nemerle.org
Дата: 21.04.03 17:59
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>То есть ты предлагаешь генерить сериализатор при первом обращении к конкретному классу?


Оптимально было бы это делать еже при компиляции типа. Встретили атрибут Serialazable... и сгенерили код сериализации. Оптимальный и шустрый. При этом ожно было бы создавать два варианта сериализации толернтный к версии и скоростной. Первый использовать при сериализации в файл, а второй в ремоутинге и других местах где скорость самый важный показатель.

VD>>Интересно насколько тормозит сериализация в Яве?


AVK>Вот как то не интересовался.


А было бы интересно сравнить. За одно сравнить с реализацией на плюсах. В дотнете даже BinaryReaded/BinaryWriter написаны далеко не оптимально (вместо адресной арифметики используются операторы сдвига).
... << RSDN@Home 1.0 beta 6a >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Re[13]: Сериализация в дотнете 2 - самопальный датасет
От: mihailik Украина  
Дата: 22.04.03 12:42
Оценка:
VD> В дотнете даже BinaryReaded/BinaryWriter написаны далеко не оптимально (вместо адресной арифметики используются операторы сдвига).

Ух ты, интерестно! А как это?
... << RSDN@Home 1.0 beta 6a >>
Re[14]: Сериализация в дотнете 2 - самопальный датасет
От: VladD2 Российская Империя www.nemerle.org
Дата: 22.04.03 20:17
Оценка: 1 (1)
Здравствуйте, mihailik, Вы писали:

VD>> В дотнете даже BinaryReaded/BinaryWriter написаны далеко не оптимально (вместо адресной арифметики используются операторы сдвига).


M>Ух ты, интерестно! А как это?


У тебя что Анакрины нет? Или исходников Ротора?

Вот так:
public virtual void Write(int value)
{
    this._buffer[0] = (byte) value;
    this._buffer[1] = (byte) value >> 8;
    this._buffer[2] = (byte) value >> 16;
    this._buffer[3] = (byte) value >> 24;
    this.OutStream.Write(this._buffer, 0, 4);
}


Вместо банального С++-ного:

public virtual void Write(int value)
{
    memcpy(_buffer, &value, sizeof(value));
}
... << RSDN@Home 1.0 beta 6a >>
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.