2016-05-15 69 views
1

今天我正在尋找方法來存儲我的Wavefront模型,並希望能夠提高性能。我想看看序列化,主要是因爲我以前從未使用它。在我看來,序列化/反序列化應該比解析和重新初始化Wavefront模型更快,但是,我的基準測試顯示其他情況。爲什麼對象反序列化比原始重新初始化慢?

這裏是我的基準代碼:

using System; 
using System.Diagnostics; 
using GrimoireTactics.Framework.OpenGL.Modeling; 
using GrimoireTactics.Framework.Utilities; 

namespace GrimoireDevelopmentKit.DevelopmentKit 
{ 
    static class Program 
    { 
     /// <summary> 
     /// The main entry point for the application. 
     /// </summary> 
     [STAThread] 
     static void Main() 
     { 

      WavefrontModel model; 
      Stopwatch benchmark = new Stopwatch(); 
      // 
      // Benchmark Deserialization 
      // 

      // Do a warm up 
      for (int i = 0; i < 500; i++) 
      { 
       model = ResourceCompiler.ReadFromBinaryFile<WavefrontModel>("C:/Users/Krythic/Desktop/Compiled.sfg"); // Sfg is an extension I wanted to use 
      } 
      benchmark.Start(); 
      model = ResourceCompiler.ReadFromBinaryFile<WavefrontModel>("C:/Users/Krythic/Desktop/Compiled.sfg"); // Sfg is an extension I wanted to use 
      benchmark.Stop(); 
      Console.WriteLine("Deserialization: "+ benchmark.Elapsed); 
      benchmark.Reset(); 


      // 
      // Benchmark Plain Old Initialization 
      // 
      model = new WavefrontModel(); 

      // Do a Warm up 
      for (int i = 0; i < 500; i++) 
      { 
       model.Load("C:/Users/Krythic/Desktop/Closet.obj"); 
      } 
      benchmark.Start(); 
      model.Load("C:/Users/Krythic/Desktop/Closet.obj"); 
      benchmark.Stop(); 
      Console.WriteLine("Plain Old Initialization: " + benchmark.Elapsed); 
      Console.Read(); 
     } 
    } 
} 

,這裏是輸出:

enter image description here

這裏是序列化和反序列化(的代碼,我#2發現:

using System.IO; 
using System.Runtime.Serialization.Formatters.Binary; 

namespace GrimoireTactics.Framework.Utilities 
{ 
    public class ResourceCompiler 
    { 
     /// <summary> 
     /// Writes the given object instance to a binary file. 
     /// <para>Object type (and all child types) must be decorated with the [Serializable] attribute.</para> 
     /// <para>To prevent a variable from being serialized, decorate it with the [NonSerialized] attribute; cannot be applied to properties.</para> 
     /// </summary> 
     /// <typeparam name="T">The type of object being written to the XML file.</typeparam> 
     /// <param name="filePath">The file path to write the object instance to.</param> 
     /// <param name="objectToWrite">The object instance to write to the XML file.</param> 
     /// <param name="append">If false the file will be overwritten if it already exists. If true the contents will be appended to the file.</param> 
     public static void WriteToBinaryFile<T>(string filePath, T objectToWrite, bool append = false) 
     { 
      using (Stream stream = File.Open(filePath, append ? FileMode.Append : FileMode.Create)) 
      { 
       BinaryFormatter binaryFormatter = new BinaryFormatter(); 
       binaryFormatter.Serialize(stream, objectToWrite); 
      } 
     } 

     /// <summary> 
     /// Reads an object instance from a binary file. 
     /// </summary> 
     /// <typeparam name="T">The type of object to read from the XML.</typeparam> 
     /// <param name="filePath">The file path to read the object instance from.</param> 
     /// <returns>Returns a new instance of the object read from the binary file.</returns> 
     public static T ReadFromBinaryFile<T>(string filePath) 
     { 
      using (Stream stream = File.Open(filePath, FileMode.Open)) 
      { 
       BinaryFormatter binaryFormatter = new BinaryFormatter(); 
       return (T)binaryFormatter.Deserialize(stream); 
      } 
     } 
    } 
} 

這裏是我的WavefrontModel等級:

using System; 
using System.Collections.Generic; 
using System.IO; 
using GrimoireTactics.Framework.OpenGL.Texturing; 
using OpenTK; 
using OpenTK.Graphics.OpenGL; 

namespace GrimoireTactics.Framework.OpenGL.Modeling 
{ 
    [Serializable] 
    public class WavefrontModel 
    { 
     public Vector3[] Vertices; 
     public Vector2[] TexCoords; 
     public Vector3[] Normals; 
     public Face[] Faces; 
     public string ModelSource; 
     public string Name; 
     public Material Material; 
     /// <summary> 
     /// A static buffer used by all models when they are loaded. 
     /// </summary> 
     private static readonly string[] FileBuffer = new string[15]; 
     /// <summary> 
     /// A static buffer used by all models when they are loaded. 
     /// </summary> 
     private static readonly string[] IndiceBuffer = new string[3]; 
     /// <summary> 
     /// A static buffer used by all models when they are loaded. 
     /// </summary> 
     private static readonly FaceIndices[] VerticesIndexBuffer = new FaceIndices[3]; 

     /// <summary> 
     /// The Triangle Count of this model. 
     /// </summary> 
     public int TriCount 
     { 
      get 
      { 
       return Faces.Length; 
      } 
     } 

     public WavefrontModel() 
     { 

     } 

     public WavefrontModel(string modelPath, Material material) 
     { 
      this.ModelSource = modelPath; 
      this.Material = material; 
     } 

     public WavefrontModel(string modelPath) 
     { 
      this.ModelSource = modelPath; 
      this.Material = null; 
     } 

     public WavefrontModel(string[] data) 
     { 
      this.ModelSource = String.Empty; 
      this.Material = null; 
      Load(data); 
     } 

     public WavefrontModel(string[] data, Material material) 
     { 
      this.ModelSource = String.Empty; 
      this.Material = material; 
      Load(data); 
     } 

     /// <summary> 
     /// Loads a model from the desired Wavefront.obj source given 
     /// at constructor initialization. 
     /// </summary> 
     public void Load() 
     { 
      Load(this.ModelSource); 
     } 

     /// <summary> 
     /// Loads a model from a Wavefront.obj located on disk. 
     /// </summary> 
     /// <param name="file"></param> 
     public void Load(string file) 
     { 
      Parse(File.ReadAllLines(file), this); 
     } 

     /// <summary> 
     /// Initializes this model with the data provided. 
     /// </summary> 
     /// <param name="data"></param> 
     public void Load(string[] data) 
     { 
      Parse(data, this); 
     } 

     /// <summary> 
     /// Current Benchmarked time(Warm boot) 
     /// </summary> 
     /// <param name="data"></param> 
     /// <param name="model"></param> 
     public static void Parse(string[] data, WavefrontModel model) 
     { 
      // Create Header 
      int totalVertices = 0; 
      int totalNormals = 0; 
      int totalTextureCoordinates = 0; 
      int totalFaces = 0; 
      for (int i = 0; i < data.Length; i++) 
      { 
       switch (data[i][0]) 
       { 
        case 'v': // Geometric Parameter 
         switch (data[i][1]) 
         { 
          case ' ': // Detect Vertices 
           totalVertices++; 
           break; 
          case 't': // Detect TexCoords 
           totalTextureCoordinates++; 
           break; 
          case 'n': // Detect Normals 
           totalNormals++; 
           break; 
         } 
         break; 
        case 'f': 
         totalFaces++; 
         break; 
       } 
      } 
      // Create the Buffers 
      model.Vertices = new Vector3[totalVertices]; 
      model.Normals = new Vector3[totalNormals]; 
      model.TexCoords = new Vector2[totalTextureCoordinates]; 
      model.Faces = new Face[totalFaces]; 
      // Load the Data 
      // Iterators 
      int verticesIterator = 0; 
      int normalsIterator = 0; 
      int textureCoordinatesIterator = 0; 
      int facesIterator = 0; 
      for (int line = 0; line < data.Length; line++) 
      { 
       string[] lineTokens = SplitStringFast(data[line], ' ', FileBuffer); 
       switch (lineTokens[0]) 
       { 
        case "v": // Vector 
         Vector3 vertex = new Vector3 
         { 
          X = ParseFloatFast(lineTokens[1]), 
          Y = ParseFloatFast(lineTokens[2]), 
          Z = ParseFloatFast(lineTokens[3]) 
         }; 
         model.Vertices[verticesIterator] = vertex; 
         verticesIterator++; 
         break; 
        case "vt": // Texture Coordinate 
         Vector2 textureCoordinate = new Vector2 
         { 
          X = ParseFloatFast(lineTokens[1]), // U 
          Y = -ParseFloatFast(lineTokens[2]) // V (Inverted) 
         }; 
         model.TexCoords[textureCoordinatesIterator] = textureCoordinate; 
         textureCoordinatesIterator++; 
         break; 
        case "vn": // Normal 
         Vector3 normal = new Vector3 
         { 
          X = ParseFloatFast(lineTokens[1]), 
          Y = ParseFloatFast(lineTokens[2]), 
          Z = ParseFloatFast(lineTokens[3]) 
         }; 
         model.Normals[normalsIterator] = normal; 
         normalsIterator++; 
         break; 
        case "f": // Face (Triangle indices) 
         for (int i = 0; i < 3; i++) 
         { 
          string[] parameters = SplitStringFast(lineTokens[i + 1], '/', IndiceBuffer); 
          FaceIndices indices = new FaceIndices 
          { 
           Vertex = ParseUInt32Fast(parameters[0]) - 1, 
           Texture = ParseUInt32Fast(parameters[1]) - 1, 
           Normal = ParseUInt32Fast(parameters[2]) - 1 
          }; 
          VerticesIndexBuffer[i] = indices; 
         } 
         model.Faces[facesIterator] = new Face(VerticesIndexBuffer[0], VerticesIndexBuffer[1], VerticesIndexBuffer[2]); 
         facesIterator++; 
         break; 
       } 
      } 
     } 

     /// <summary> 
     /// A custom implementation of Int32.Parse. This 
     /// function is, on average, 5-6x faster than the one 
     /// offered by .NET. This function assumes that the string 
     /// given will yield a positive integer. 
     /// </summary> 
     /// <param name="value"></param> 
     /// <returns></returns> 
     private static int ParseUInt32Fast(string value) 
     { 
      int result = 0; 
      for (int i = 0; i < value.Length; i++) 
      { 
       result = 10 * result + (value[i] - 48); 
      } 
      return result; 
     } 

     /// <summary> 
     /// A custom implementation of String.Split(). Realistically, this 
     /// function is not much faster than what .NET offers; it gains speed 
     /// more from a preset buffer mechanism. 
     /// </summary> 
     /// <param name="value"></param> 
     /// <param name="delimiter"></param> 
     /// <param name="buffer"></param> 
     /// <returns></returns> 
     private static string[] SplitStringFast(string value, char delimiter, string[] buffer) 
     { 
      int resultIndex = 0; 
      int startIndex = 0; 
      for (int i = 0; i < value.Length; i++) 
      { 
       if (value[i] == delimiter) 
       { 
        buffer[resultIndex] = value.Substring(startIndex, i - startIndex); 
        resultIndex++; 
        startIndex = i + 1; 
       } 
      } 
      buffer[resultIndex] = value.Substring(startIndex, value.Length - startIndex); 
      return buffer; 
     } 

     /// <summary> 
     /// A custom implementation of Float.Parse. This 
     /// function is, on average, 5-6x faster than the one 
     /// offered by .NET 
     /// </summary> 
     /// <param name="inputData">The inputData.</param> 
     /// <returns></returns> 
     private static float ParseFloatFast(string inputData) 
     { 
      float result = 0; 
      int position = 0; 
      int inputLength = inputData.Length; 
      char firstCharacter = inputData[0]; 
      float negativeSign = 1; 
      if (firstCharacter == '-') 
      { 
       negativeSign = -1; 
       ++position; 
      } 
      while (true) 
      { 
       if (position >= inputLength) 
       { 
        return negativeSign * result; 
       } 
       firstCharacter = inputData[position++]; 
       if (firstCharacter < '0' || firstCharacter > '9') 
       { 
        break; 
       } 
       result = (float)((result * 10.0) + (firstCharacter - '0')); 
      } 
      float exponent = 0.1f; 
      while (position < inputLength) 
      { 
       firstCharacter = inputData[position++]; 
       result += (firstCharacter - '0') * exponent; 
       exponent *= 0.1f; 
      } 
      return negativeSign * result; 
     } 

     /// <summary> 
     /// Renders the Model using deprecated immediate mode. This 
     /// function exists only for testing purposes. 
     /// </summary> 
     public void Render() 
     { 
      GL.Enable(EnableCap.Texture2D); 
      GL.Color3(Material.AmbientColor); 
      GL.BindTexture(TextureTarget.Texture2D, Material.Diffuse); 
      GL.Begin(PrimitiveType.Triangles); 
      for (int i = 0; i < Faces.Length; i++) 
      { 
       for (int index = 0; index < Faces[i].Indices.Length; index++) 
       { 
        Vector3 v = Vertices[Faces[i].Indices[index].Vertex]; 
        Vector3 n = Normals[Faces[i].Indices[index].Normal]; 
        Vector2 tc = TexCoords[Faces[i].Indices[index].Texture]; 
        GL.Normal3(n.X, n.Y, n.Z); 
        GL.TexCoord2(tc.X, tc.Y); 
        GL.Vertex3(v.X, v.Y, v.Z); 
       } 
      } 
      GL.End(); 
     } 
    } 
} 

對不起,所有的代碼,我只是想告訴你們我所做的一切。現在,在我看來,反序列化應該比重新模型,創建數組等等更快。所以我的問題是:爲什麼deserializtaion不是更快?有什麼我可以做的更好,使角色轉變,以便反序列化變得更快?

+0

'BinaryFormatter'很慢。您的文本格式看起來與JSON類似,並且比大多數.Net JSON序列化程序更慢,請參閱http://james.newtonking.com/archive/2010/01/01/net-serialization-performance-comparison或http:// maxondev/serialization-performance-comparison -c-net-formats-frameworks-xmldatacontractserializer-xmlserializer-binaryformatter -json-newtonsoft-servicestack-text /或https://blogs.msdn.microsoft.com/youssefm/2009/07/ 10 /比較最性能的淨串行器/。另見http://stackoverflow.com/questions/703073 – dbc

+0

而且還http://stackoverflow.com/questions/4143421/fastest-way-to-serialize-and-deserialize-net-object/4143437 – dbc

回答

1

對於初學者,嘗試從內部循環中創建二進制格式化程序。請注意,「創建數組等等等等」也將由BinaryFormatter在反串行化時完成 - 沒有什麼神奇的方法可以避免對象圖的實例化。

你也可以看看更快的串行器,如Protobuf-net。退房http://maxondev.com/serialization-performance-comparison-c-net-formats-frameworks-xmldatacontractserializer-xmlserializer-binaryformatter-json-newtonsoft-servicestack-text/

+0

我創建了兩個爲了規避循環中BinaryFormatter的重新初始化,需要使用BinaryFormatter的重載。性能是相同的......可能是由於編譯器優化,無論如何都將其從循環中移除。我會盡量抽出時間來測試Protobuf。 – Krythic