2012-03-30 43 views
11

Tôi có rất nhiều dữ liệu địa lý đại diện trong cấu trúc đối tượng đơn giản chỉ bao gồm các cấu trúc. Tất cả các trường của tôi đều thuộc loại giá trị.Tuần tự hóa nhanh/giải tuần tự hóa các cấu trúc

public struct Child 
{ 
    readonly float X; 
    readonly float Y; 
    readonly int myField; 
} 

public struct Parent 
{ 
    readonly int id; 
    readonly int field1; 
    readonly int field2; 
    readonly Child[] children; 
} 

Dữ liệu được chia thành các phần nhỏ Parent[] -s. Mỗi mảng chứa một vài nghìn trường hợp Gốc. Tôi có quá nhiều dữ liệu để giữ tất cả trong bộ nhớ, vì vậy tôi cần phải trao đổi những phần này cho đĩa qua lại. (Một tệp sẽ có kết quả xấp xỉ 2-300KB).

Cách hiệu quả nhất để tuần tự hóa/deserializing các Parent[] thành byte[] cho dumpint vào đĩa và đọc lại là gì? Liên quan đến tốc độ, Tôi đặc biệt quan tâm đến deserialization nhanh, tốc độ ghi không phải là quan trọng.

Có đơn giản BinarySerializer đủ tốt không? Hoặc tôi có nên hack xung quanh với StructLayout (see accepted answer) không? Tôi không chắc chắn nếu điều đó sẽ làm việc với lĩnh vực mảng của Parent.children.

CẬP NHẬT: Trả lời nhận xét - Có, các đối tượng không thay đổi (mã được cập nhật) và thực sự trường children không phải là loại giá trị. 300KB âm thanh không nhiều nhưng tôi có hàng chục tập tin như vậy, vì vậy tốc độ không thành vấn đề.

+4

_Tất cả các trường của tôi đều thuộc loại giá trị_ - Trường 'trẻ em' không phải là loại giá trị. –

+1

300KB là số tiền nhỏ, số tiền này được deserialized/serialized trong 0.1s w/o tối ưu hóa –

+0

Là tất cả dữ liệu của bạn chỉ đọc? – usr

Trả lời

10

BinarySerializer là một bộ nối tiếp rất chung chung. Nó sẽ không thực hiện cũng như triển khai tùy chỉnh.

May mắn thay cho bạn, dữ liệu của bạn chỉ bao gồm các cấu trúc. Điều này có nghĩa rằng bạn sẽ có thể sửa một structlayout cho Child và chỉ cần bit-copy mảng con bằng cách sử dụng mã không an toàn từ một byte [] bạn đã đọc từ đĩa.

Đối với cha mẹ, điều đó không dễ dàng vì bạn cần đối xử riêng với trẻ em. Tôi khuyên bạn nên sử dụng mã không an toàn để sao chép các trường có thể sao chép bit từ byte [] bạn đọc và deserialize các trẻ em một cách riêng biệt.

Bạn có xem xét ánh xạ tất cả các trẻ em vào bộ nhớ bằng cách sử dụng các tệp ánh xạ bộ nhớ không? Sau đó, bạn có thể sử dụng lại cơ sở bộ nhớ cache của hệ điều hành và không giải quyết việc đọc và viết.

Zero-copy-deserializing một trẻ em [] trông như sau:

byte[] bytes = GetFromDisk(); 
fixed (byte* bytePtr = bytes) { 
Child* childPtr = (Child*)bytePtr; 
//now treat the childPtr as an array: 
var x123 = childPtr[123].X; 

//if we need a real array that can be passed around, we need to copy: 
var childArray = new Child[GetLengthOfDeserializedData()]; 
for (i = [0..length]) { 
    childArray[i] = childPtr[i]; 
} 
} 
+0

Tôi tra cứu các tệp ánh xạ bộ nhớ, chúng trông tuyệt vời để quản lý truy cập đĩa! Bạn có thể viết một ví dụ cho phân khúc không an toàn không? Làm thế nào tôi có thể "bỏ" một byte [] vào một Child [] ở chế độ không an toàn? Bởi vì khi bạn nói nó sẽ mất thời gian không. – user256890

+1

Tôi đã thêm một ví dụ. Nếu bạn muốn không sao chép, bạn cần phải thay đổi ứng dụng của bạn để sử dụng con trỏ hoặc IO không an toàn bằng cách sử dụng ReadFile (trực tiếp đọc vào một đứa trẻ hiện có []). Nhưng tôi đoán là một lần sao chép duy nhất thực sự là không có gì. CPU là tốt ở đó. – usr

10

Nếu bạn không ưa thích đi xuống viết đường serializer riêng bạn, bạn có thể sử dụng protobuf.net serializer. Dưới đây là kết quả từ một chương trình thử nghiệm nhỏ:

Using 3000 parents, each with 5 children 
BinaryFormatter Serialized in: 00:00:00.1250000 
Memory stream 486218 B 
BinaryFormatter Deserialized in: 00:00:00.1718750 

ProfoBuf Serialized in: 00:00:00.1406250 
Memory stream 318247 B 
ProfoBuf Deserialized in: 00:00:00.0312500 

Nó nên được khá tự giải thích. Đây chỉ là một lần chạy, nhưng khá là biểu thị cho tốc độ tôi thấy (3-5x).

Để làm cho cấu trúc của bạn có thể tuần tự (với protobuf.net), chỉ cần thêm các thuộc tính sau:

[ProtoContract] 
[Serializable] 
public struct Child 
{ 
    [ProtoMember(1)] public float X; 
    [ProtoMember(2)] public float Y; 
    [ProtoMember(3)] public int myField; 
} 

[ProtoContract] 
[Serializable] 
public struct Parent 
{ 
    [ProtoMember(1)] public int id; 
    [ProtoMember(2)] public int field1; 
    [ProtoMember(3)] public int field2; 
    [ProtoMember(4)] public Child[] children; 
} 

UPDATE:

Trên thực tế, viết một serializer tùy chỉnh được khá dễ dàng, đây là một việc thực hiện trần xương:

class CustSerializer 
{ 
    public void Serialize(Stream stream, Parent[] parents, int childCount) 
    { 
     BinaryWriter sw = new BinaryWriter(stream); 
     foreach (var parent in parents) 
     { 
      sw.Write(parent.id); 
      sw.Write(parent.field1); 
      sw.Write(parent.field2); 

      foreach (var child in parent.children) 
      { 
       sw.Write(child.myField); 
       sw.Write(child.X); 
       sw.Write(child.Y); 
      } 
     } 
    } 

    public Parent[] Deserialize(Stream stream, int parentCount, int childCount) 
    { 
     BinaryReader br = new BinaryReader(stream); 
     Parent[] parents = new Parent[parentCount]; 

     for (int i = 0; i < parentCount; i++) 
     { 
      var parent = new Parent(); 
      parent.id = br.ReadInt32(); 
      parent.field1 = br.ReadInt32(); 
      parent.field2 = br.ReadInt32(); 
      parent.children = new Child[childCount]; 

      for (int j = 0; j < childCount; j++) 
      { 
       var child = new Child(); 
       child.myField = br.ReadInt32(); 
       child.X = br.ReadSingle(); 
       child.Y = br.ReadSingle(); 
       parent.children[j] = child; 
      } 

      parents[i] = parent; 
     } 
     return parents; 
    } 
} 

Và đây là đầu ra của nó khi chạy trong một thử nghiệm tốc độ đơn giản:

Custom Serialized in: 00:00:00 
Memory stream 216000 B 
Custom Deserialized in: 00:00:00.0156250 

Rõ ràng, nó ít linh hoạt hơn nhiều so với các cách tiếp cận khác, nhưng nếu tốc độ thực sự quan trọng thì nó nhanh hơn khoảng 2-3 lần so với phương pháp protobuf. Nó cũng tạo ra kích thước tệp tối thiểu, vì vậy việc ghi vào đĩa sẽ nhanh hơn.

+1

Protobuf là một sự cân bằng tuyệt vời giữa tính dễ sử dụng và hiệu năng trong hầu hết các trường hợp. Nếu bạn muốn đi hạt nó vẫn không thể đánh bại hiệu suất của một giải pháp tùy chỉnh. Đặc biệt là một trong những bit-blittable mà có thể có một chi phí chính xác bằng không! – usr

Các vấn đề liên quan