2012-04-02 59 views
1

我正在嘗試爲正在編寫的遊戲編寫通用套接字服務器。我知道我可以很好地使用像SmartFox和Photon這樣已經構建好的服務器,但是我不想經歷爲了學習目的而自己創建一個服務器的痛苦。改進二進制轉換並返回C#

我想出了一個BSON啓發式協議來將基本數據類型,它們的數組和一個特殊的GSObject轉換爲二進制,並以一種方式安排它們,以便它可以一起放回到對象形式客戶端。在覈心,轉換方法利用.Net BitConverter類將基本數據類型轉換爲二進制。無論如何,問題是性能,如果我循環5萬次,並將我的GSObject轉換爲二進制,每次大約需要5500毫秒(結果字節[]僅爲每轉換192字節)。我認爲這對於一個每秒發送5-10個位置更新並有1000個併發用戶的MMO來說太慢了。是的,我知道一款遊戲不太可能同時擁有1000名用戶,但就像我之前所說的那樣,這對我來說應該是一個學習過程,我想要走出自己的路,並建立一些能夠很好地擴展並且可以處理至少幾千個用戶。

所以,如果任何人知道其他轉換技術或看到我失去表現,我將不勝感激幫助。

GSBitConverter.cs

這是主要的轉換類,它增加了擴展方法,主數據類型轉換爲二進制格式。它使用BitConverter類來轉換基類型。我只顯示了用於轉換整數和整數數組的代碼,但其餘的方法幾乎都是這兩者的複製品,它們只是重載了這個類型。

public static class GSBitConverter 
{ 
    public static byte[] ToGSBinary(this short value) 
    { 
     return BitConverter.GetBytes(value); 
    } 

    public static byte[] ToGSBinary(this IEnumerable<short> value) 
    { 
     List<byte> bytes = new List<byte>(); 
     short length = (short)value.Count(); 

     bytes.AddRange(length.ToGSBinary()); 
     for (int i = 0; i < length; i++) 
      bytes.AddRange(value.ElementAt(i).ToGSBinary()); 

     return bytes.ToArray(); 
    } 

    public static byte[] ToGSBinary(this bool value); 
    public static byte[] ToGSBinary(this IEnumerable<bool> value); 

    public static byte[] ToGSBinary(this IEnumerable<byte> value); 

    public static byte[] ToGSBinary(this int value); 
    public static byte[] ToGSBinary(this IEnumerable<int> value); 

    public static byte[] ToGSBinary(this long value); 
    public static byte[] ToGSBinary(this IEnumerable<long> value); 

    public static byte[] ToGSBinary(this float value); 
    public static byte[] ToGSBinary(this IEnumerable<float> value); 

    public static byte[] ToGSBinary(this double value); 
    public static byte[] ToGSBinary(this IEnumerable<double> value); 

    public static byte[] ToGSBinary(this string value); 
    public static byte[] ToGSBinary(this IEnumerable<string> value); 

    public static string GetHexDump(this IEnumerable<byte> value); 
} 

Program.cs的 這裏是我正在轉換爲二進制在一個循環的對象。

class Program 
{ 
    static void Main(string[] args) 
    { 
     GSObject obj = new GSObject(); 
     obj.AttachShort("smallInt", 15); 
     obj.AttachInt("medInt", 120700); 
     obj.AttachLong("bigInt", 10900800700); 
     obj.AttachDouble("doubleVal", Math.PI); 
     obj.AttachStringArray("muppetNames", new string[] { "Kermit", "Fozzy", "Piggy", "Animal", "Gonzo" }); 

     GSObject apple = new GSObject(); 
     apple.AttachString("name", "Apple"); 
     apple.AttachString("color", "red"); 
     apple.AttachBool("inStock", true); 
     apple.AttachFloat("price", (float)1.5); 

     GSObject lemon = new GSObject(); 
     apple.AttachString("name", "Lemon"); 
     apple.AttachString("color", "yellow"); 
     apple.AttachBool("inStock", false); 
     apple.AttachFloat("price", (float)0.8); 

     GSObject apricoat = new GSObject(); 
     apple.AttachString("name", "Apricoat"); 
     apple.AttachString("color", "orange"); 
     apple.AttachBool("inStock", true); 
     apple.AttachFloat("price", (float)1.9); 

     GSObject kiwi = new GSObject(); 
     apple.AttachString("name", "Kiwi"); 
     apple.AttachString("color", "green"); 
     apple.AttachBool("inStock", true); 
     apple.AttachFloat("price", (float)2.3); 

     GSArray fruits = new GSArray(); 
     fruits.AddGSObject(apple); 
     fruits.AddGSObject(lemon); 
     fruits.AddGSObject(apricoat); 
     fruits.AddGSObject(kiwi); 

     obj.AttachGSArray("fruits", fruits); 

     Stopwatch w1 = Stopwatch.StartNew(); 
     for (int i = 0; i < 50000; i++) 
     { 
      byte[] b = obj.ToGSBinary(); 
     } 
     w1.Stop(); 

     Console.WriteLine(BitConverter.IsLittleEndian ? "Little Endian" : "Big Endian"); 
     Console.WriteLine(w1.ElapsedMilliseconds + "ms"); 

    } 

下面是我在上面的代碼中使用的一些其他類的代碼。大部分是重複的。

GSObject

GSArray

GSWrappedObject

+0

您是否曾嘗試在探查器中運行此功能以查看您的時間正在用於何處?我個人喜歡dotTrace(http://www.jetbrains.com/profiler/),但任何分析器都可以。 – 2012-04-02 22:30:35

+0

@ChrisShain不,我沒有,我真的不知道我可以做到這一點,但我會考慮這一點,謝謝你的建議。 – 2012-04-02 22:35:19

+1

這篇文章太遠了,太長了。請重點關注問題的相關部分,將代碼重新降至10%。 – Gray 2012-04-02 22:40:55

回答

1

1)的ElementAt是非常昂貴的。使用foreach (var v in value)而不是for (int i = 0; i < length; i++) .. .ElementAt(i) ..

2)ToGsBinary方法是昂貴的,因爲它們經常拷貝數組。 使用簽名的void WriteToGsBinary(Stream stream)代替byte[] ToGsBinary()

3)添加重載數組:void WriteToGsBinary(Stream stream, byte[] values)void WriteToGsBinary(Stream stream, short[] values)

+0

謝謝,我認爲這可能是其中一個問題。你認爲接受數組參數而不是IEnumerable會有所幫助,因爲我不需要調用ToArray? – 2012-04-02 23:29:56

+0

@SaadImran。 .net中最快的集合是直接訪問的數組。 List as List,List as IEnumerable,List as IList,Array as IEnumerable等等比數組Array更慢。 – 2012-04-02 23:38:45

2

我的第一直覺,沒有太多的走下車,將是您大量的時間被陷入不斷重新創建數組和列表。

我會傾向於轉向基於流的方法,而不是試圖不斷創建數組。也就是說,讓所有的GSBinary方法接受一個Stream然後寫入它而不是自己創建數組,然後如果你想在本地內存中使用它,可以在底部使用一個MemoryStream,然後在最後使用你的數組如果你打算將它作爲一個網絡應用程序,直接寫入網絡流更好)。

然而,根據克里斯的評論,最好的方法是在dotTrace或redgate的ANTS性能分析器上運行一個分析器,以便在投入時間重構某個效率不高的情況下,只是實際時間的一小部分。

+0

感謝您的建議,這是我從Google一直在做的搜索引擎中發現的一件事。我會研究這一點。 – 2012-04-02 23:31:41