2013-12-09 5 views
12

У меня в моем коде ConcurrentBag<Point3DCollection>.Сериализация ConcurrentBag XAML

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

Сам по себе потенциально довольно большой и может быть сжат, чтобы ускорить чтение и запись на диск и с диска, но время отклика, которое мне нужно для этого, в основном в масштабе пользовательского интерфейса. Другими словами, I предпочитает двоичное форматирование по форматированию XAML-текста по соображениям производительности. (Существует хороший сериализатор XAML-текста, который является частью Helix 3D CodeProject, но он медленнее, чем мне хотелось бы.)

Это прецедент, когда я оставил свой собственный сериализатор или там что-то уже упаковано для такого рода данных?

+0

Я думаю, что это очень спорно. Point3D - 3 двойных значения. Двойной длиной 8 байтов, поэтому двоичный сериализованный Point3D равен 24 байтам. Многие двойные значения, созданные людьми (или даже Blend), используемые в XAML, могут быть закорочены, чем сериализованные как строка (я предполагаю, что вы напишете их как ANSI, а не UNICODE). Если это не так, это означает, что сжатие будет бесполезным, поскольку энтропия списка двоичных сериализованных двойных значений в этом случае будет высокой. Я предлагаю вам просто петлю над сумкой и использовать готовый класс Point3DCollectionConverter (метод ConvertTo). –

+0

@SimonMourier, значения, как правило, генерируются системами САПР, поэтому строки ANSI с двойной точностью будут намного длиннее 8 байтов. Но ваша благодарность Point3DCollectionConverter - это возможный ответ. Не могли бы вы написать это как ответ, разрешив мне присуждать премию и т. Д.? –

+0

Для большого количества данных, почему бы вам не рассмотреть Sqlite и т. Д., Которые могут хранить структурированные данные в файле. Я видел много 3d-программ, использующих базу данных для хранения структуры вместе с отношениями, которые позволяют им частично вставлять/обновлять/удалять данные. Преимущество Sqlite заключается в том, что вы можете использовать многопоточную сериализацию для повышения скорости, однако вам нужно немного поработать над sqlite, чтобы включить многопоточное SQL-соединение, или же вы можете использовать LocalDB SQL Express или даже Sql Compact. –

ответ

5

Вот некоторые методы расширений, которые обрабатывают строки и бинарную сериализацию Point3DCollection мешков. Как я уже сказал в своем комментарии, я не думаю, что во всех случаях это лучший способ, поэтому вы можете попробовать оба. Также обратите внимание, что они используют параметр Stream в качестве входных данных, поэтому вы можете связать их с вызовами GZipStream из DeflateStream.

public static class Point3DExtensions 
{ 
    public static void StringSerialize(this ConcurrentBag<Point3DCollection> bag, Stream stream) 
    { 
     if (bag == null) 
      throw new ArgumentNullException("bag"); 

     if (stream == null) 
      throw new ArgumentNullException("stream"); 

     StreamWriter writer = new StreamWriter(stream); 
     Point3DCollectionConverter converter = new Point3DCollectionConverter(); 
     foreach (Point3DCollection coll in bag) 
     { 
      // we need to use the english locale as the converter needs that for parsing... 
      string line = (string)converter.ConvertTo(null, CultureInfo.GetCultureInfo("en-US"), coll, typeof(string)); 
      writer.WriteLine(line); 
     } 
     writer.Flush(); 
    } 

    public static void StringDeserialize(this ConcurrentBag<Point3DCollection> bag, Stream stream) 
    { 
     if (bag == null) 
      throw new ArgumentNullException("bag"); 

     if (stream == null) 
      throw new ArgumentNullException("stream"); 

     StreamReader reader = new StreamReader(stream); 
     Point3DCollectionConverter converter = new Point3DCollectionConverter(); 
     do 
     { 
      string line = reader.ReadLine(); 
      if (line == null) 
       break; 

      bag.Add((Point3DCollection)converter.ConvertFrom(line)); 

      // NOTE: could also use this: 
      //bag.Add(Point3DCollection.Parse(line)); 
     } 
     while (true); 
    } 

    public static void BinarySerialize(this ConcurrentBag<Point3DCollection> bag, Stream stream) 
    { 
     if (bag == null) 
      throw new ArgumentNullException("bag"); 

     if (stream == null) 
      throw new ArgumentNullException("stream"); 

     BinaryWriter writer = new BinaryWriter(stream); 
     writer.Write(bag.Count); 
     foreach (Point3DCollection coll in bag) 
     { 
      writer.Write(coll.Count); 
      foreach (Point3D point in coll) 
      { 
       writer.Write(point.X); 
       writer.Write(point.Y); 
       writer.Write(point.Z); 
      } 
     } 
     writer.Flush(); 
    } 

    public static void BinaryDeserialize(this ConcurrentBag<Point3DCollection> bag, Stream stream) 
    { 
     if (bag == null) 
      throw new ArgumentNullException("bag"); 

     if (stream == null) 
      throw new ArgumentNullException("stream"); 

     BinaryReader reader = new BinaryReader(stream); 
     int count = reader.ReadInt32(); 
     for (int i = 0; i < count; i++) 
     { 
      int pointCount = reader.ReadInt32(); 
      Point3DCollection coll = new Point3DCollection(pointCount); 
      for (int j = 0; j < pointCount; j++) 
      { 
       coll.Add(new Point3D(reader.ReadDouble(), reader.ReadDouble(), reader.ReadDouble())); 
      } 
      bag.Add(coll); 
     } 
    } 
} 

И небольшая программа тест консольного приложения, чтобы играть с:

static void Main(string[] args) 
    { 
     Random rand = new Random(Environment.TickCount); 
     ConcurrentBag<Point3DCollection> bag = new ConcurrentBag<Point3DCollection>(); 
     for (int i = 0; i < 100; i++) 
     { 
      Point3DCollection coll = new Point3DCollection(); 
      bag.Add(coll); 

      for (int j = rand.Next(10); j < rand.Next(100); j++) 
      { 
       Point3D point = new Point3D(rand.NextDouble(), rand.NextDouble(), rand.NextDouble()); 
       coll.Add(point); 
      } 
     } 

     using (FileStream stream = new FileStream("test.bin", FileMode.Create)) 
     { 
      bag.StringSerialize(stream); // or Binary 
     } 

     ConcurrentBag<Point3DCollection> newbag = new ConcurrentBag<Point3DCollection>(); 
     using (FileStream stream = new FileStream("test.bin", FileMode.Open)) 
     { 
      newbag.StringDeserialize(stream); // or Binary 
      foreach (Point3DCollection coll in newbag) 
      { 
       foreach (Point3D point in coll) 
       { 
        Console.WriteLine(point); 
       } 
       Console.WriteLine(); 
      } 
     } 
    } 
} 
+0

Simon, это сделал. Благодарим вас за четко написанный ответ. –

+0

Как точка информации, для моих случаев использования я получаю сжатие 3 к 1 в двоичных gzipped-потоках. Я еще не протестировал тестовый gzip-подход. –

2

Сжатие может потенциально использовать повторяющиеся координаты. Сериализаторы часто используют ссылки для повторяющихся объектов, хотя я не уверен, что есть много возможностей для работы с структурами (например, Point3D). Во всяком случае, вот несколько примеров того, как сериализовать это. Чтобы использовать стандартные форматы, вам необходимо преобразовать тип данных в то, что большинство из них поддерживает: list/array. В приведенном ниже коде используются пакеты Nuget NUnit и Json.NET.

using Newtonsoft.Json; 
using Newtonsoft.Json.Bson; 
using NUnit.Framework; 
using System; 
using System.Collections.Concurrent; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.IO; 
using System.IO.Compression; 
using System.Linq; 
using System.Runtime.Serialization.Formatters.Binary; 
using System.Text; 
using System.Windows.Media.Media3D; 

namespace DemoPoint3DSerialize 
{ 
    [TestFixture] 
    class Tests 
    { 
     [Test] 
     public void DemoBinary() 
     { 
      // this shows how to convert them all to strings 
      var collection = CreateCollection(); 
      var data = collection.Select(c => c.ToArray()).ToList(); // switch to serializable types 
      var formatter = new BinaryFormatter(); 

      using (var ms = new MemoryStream()) 
      { 
       formatter.Serialize(ms, data); 
       Trace.WriteLine("Binary of Array Size: " + ms.Position); 
       ms.Position = 0; 
       var dupe = (List<Point3D[]>)formatter.Deserialize(ms); 
       var result = new ConcurrentBag<Point3DCollection>(dupe.Select(r => new Point3DCollection(r))); 
       VerifyEquality(collection, result); 
      } 
     } 

     [Test] 
     public void DemoString() 
     { 
      // this shows how to convert them all to strings 
      var collection = CreateCollection(); 
      IEnumerable<IList<Point3D>> tmp = collection; 
      var strings = collection.Select(c => c.ToString()).ToList(); 

      Trace.WriteLine("String Size: " + strings.Sum(s => s.Length)); // eh, 2x for Unicode 
      var result = new ConcurrentBag<Point3DCollection>(strings.Select(r => Point3DCollection.Parse(r))); 

      VerifyEquality(collection, result); 
     } 

     [Test] 
     public void DemoDeflateString() 
     { 
      // this shows how to convert them all to strings 
      var collection = CreateCollection(); 
      var formatter = new BinaryFormatter(); // not really helping much: could 
      var strings = collection.Select(c => c.ToString()).ToList(); 

      using (var ms = new MemoryStream()) 
      { 
       using (var def = new DeflateStream(ms, CompressionLevel.Optimal, true)) 
       { 
        formatter.Serialize(def, strings); 
       } 
       Trace.WriteLine("Deflate Size: " + ms.Position); 
       ms.Position = 0; 
       using (var def = new DeflateStream(ms, CompressionMode.Decompress)) 
       { 
        var stringsDupe = (IList<string>)formatter.Deserialize(def); 
        var result = new ConcurrentBag<Point3DCollection>(stringsDupe.Select(r => Point3DCollection.Parse(r))); 

        VerifyEquality(collection, result); 
       } 
      } 
     } 

     [Test] 
     public void DemoStraightJson() 
     { 
      // this uses Json.NET 
      var collection = CreateCollection(); 
      var formatter = new JsonSerializer(); 

      using (var ms = new MemoryStream()) 
      { 
       using (var stream = new StreamWriter(ms, new UTF8Encoding(true), 2048, true)) 
       using (var writer = new JsonTextWriter(stream)) 
       { 
        formatter.Serialize(writer, collection); 
       } 
       Trace.WriteLine("JSON Size: " + ms.Position); 
       ms.Position = 0; 
       using (var stream = new StreamReader(ms)) 
       using (var reader = new JsonTextReader(stream)) 
       { 
        var result = formatter.Deserialize<List<Point3DCollection>>(reader); 
        VerifyEquality(collection, new ConcurrentBag<Point3DCollection>(result)); 
       } 
      } 
     } 

     [Test] 
     public void DemoBsonOfArray() 
     { 
      // this uses Json.NET 
      var collection = CreateCollection(); 
      var formatter = new JsonSerializer(); 

      using (var ms = new MemoryStream()) 
      { 
       using (var stream = new BinaryWriter(ms, new UTF8Encoding(true), true)) 
       using (var writer = new BsonWriter(stream)) 
       { 
        formatter.Serialize(writer, collection); 
       } 
       Trace.WriteLine("BSON Size: " + ms.Position); 
       ms.Position = 0; 
       using (var stream = new BinaryReader(ms)) 
       using (var reader = new BsonReader(stream, true, DateTimeKind.Unspecified)) 
       { 
        var result = formatter.Deserialize<List<Point3DCollection>>(reader); // doesn't seem to read out that concurrentBag 
        VerifyEquality(collection, new ConcurrentBag<Point3DCollection>(result)); 
       } 
      } 
     } 

     private ConcurrentBag<Point3DCollection> CreateCollection() 
     { 
      var rand = new Random(42); 
      var bag = new ConcurrentBag<Point3DCollection>(); 

      for (int i = 0; i < 10; i++) 
      { 
       var collection = new Point3DCollection(); 
       for (int j = 0; j < i + 10; j++) 
       { 
        var point = new Point3D(rand.NextDouble(), rand.NextDouble(), rand.NextDouble()); 
        collection.Add(point); 
       } 
       bag.Add(collection); 
      } 
      return bag; 
     } 

     private class CollectionComparer : IEqualityComparer<Point3DCollection> 
     { 
      public bool Equals(Point3DCollection x, Point3DCollection y) 
      { 
       return x.SequenceEqual(y); 
      } 

      public int GetHashCode(Point3DCollection obj) 
      { 
       return obj.GetHashCode(); 
      } 
     } 

     private void VerifyEquality(ConcurrentBag<Point3DCollection> collection, ConcurrentBag<Point3DCollection> result) 
     { 
      var first = collection.OrderBy(c => c.Count); 
      var second = collection.OrderBy(c => c.Count); 
      first.SequenceEqual(second, new CollectionComparer()); 
     } 


    } 
} 
0

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

Я видел много трехмерных программ, использующих базу данных для хранения структуры вместе с отношениями, которые позволяют им частично вставлять/обновлять/удалять данные.

Преимущества Sqlite/database - многопоточная сериализация для повышения скорости, однако вам нужно немного поработать над sqlite для включения многопоточного подключения sqlite, иначе вы можете использовать LocalDB SQL Express или даже Sql Compact.

Также некоторые из загрузок данных загрузки могут быть выполнены с помощью запросов, которые будут хорошо индексироваться по базе данных. И многое может быть сделано на фоне рабочего, не мешая пользовательскому интерфейсу.

Sqlite имеет ограниченную поддержку несколько потоков, которые могут быть изучены здесь http://www.sqlite.org/threadsafe.html

Sql Compact является поточно и требует установок, которые могут быть установлены без администратора привилегий. И вы также можете использовать инфраструктуру Entity.

+0

Есть ли ссылка, которую вы могли бы добавить, чтобы описать работу, необходимую для многопоточности через sqlite? –

+0

http://www.sqlite.org/threadsafe.html –

2

Используйте протобуф-сеть Google. protobuf-net - это версия с открытым исходным кодом .net для бинарного формата сериализации протокола протокола Google, которая может использоваться в качестве замены для сериализатора BinaryFormatter. Вероятно, это будет самое быстрое решение и проще всего реализовать.

Вот ссылка на главную вики Google для protobuf-net. Слева вы найдете загрузки для всех самых обновленных бинарных файлов.

https://code.google.com/p/protobuf-net/

Вот большая статья, вы можете захотеть взглянуть на первый, чтобы почувствовать, как это работает.

http://wallaceturner.com/serialization-with-protobuf-net

Вот ссылка на обсуждение вики Google, о конкретной проблеме. Ответ находится в нижней части страницы. Вот где я получил код ниже и заменял детали из вашего сообщения.

https://code.google.com/p/protobuf-net/issues/detail?id=354

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

[ProtoContract] 
public class MyClass { 
    public ConcurrentQueue<Point3DCollection> Points {get;set;} 

    [ProtoMember(1)] 
    private Point3DCollection[] Items 
    { 
     get { return Points.ToArray(); } 
     set { Items = new ConcurrentBag<Point3DCollection>(value); } 
    } 
} 

Желаю вам удачи. Береги себя.

Смежные вопросы