1

私が取り組んでいるゲーム用の汎用ソケット サーバーを作成しようとしています。SmartFox や Photon などの既に構築されたサーバーを十分に使用できることはわかっていますが、学習目的で自分でサーバーを作成するという苦労はしたくありません。

基本的なデータ型、その配列、および特別な GSObject をバイナリに変換し、それらをクライアント エンドでオブジェクト形式に戻すことができるように配置する、BSON にインスパイアされたプロトコルを考え出しました。コアでは、変換メソッドは .Net BitConverter クラスを利用して、基本的なデータ型をバイナリに変換します。とにかく、問題はパフォーマンスです。50,000 回ループし、約 5500 ミリ秒かかるたびに GSObject をバイナリに変換するとします (結果の byte[] は変換ごとにわずか 192 バイトです)。これは、1000 人の同時ユーザーで毎秒 5 ~ 10 の位置更新を送信する MMO には遅すぎると思います。はい、1 つのゲームで同時に 1000 人のユーザーが参加する可能性は低いことは承知していますが、前述したように、これは私にとって学習プロセスであるはずです。

そうです、誰かが他の変換テクニックを知っていたり、私がどこでパフォーマンスを落としているかを見たりしたら、助けていただければ幸いです。

GSBitConverter.cs

これはメインの変換クラスです。メインのデータ型に拡張メソッドを追加して、バイナリ形式に変換します。BitConverter クラスを使用して基本型を変換します。整数と整数配列を変換するコードのみを示しましたが、残りのメソッドはこれら 2 つのほとんどのレプリカであり、型をオーバーロードしているだけです。

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");

    }

上記のコードで使用されている他のいくつかのクラスのコードを次に示します。そのほとんどは繰り返しです。

GSオブジェクト

GSアレイ

GSWrappedObject

4

2 に答える 2

2

私の最初の予感は、たいしたことはありませんが、配列とリストを絶えず再作成することに多くの時間が費やされているということです。

常に配列を作成しようとするのではなく、Stream ベースのアプローチに移行する傾向があります。つまり、すべての GSBinary メソッドが Stream を受け入れてから、独自の配列を作成するのではなくそれに書き込むようにし、ローカルメモリでそれが必要な場合はMemoryStream、ベースで a を使用し、最後に配列を取得します (またはさらに良いこれをネットワーク化されたアプリケーションにする予定がある場合は、ネットワーク ストリームに直接書き込みます)。

Chris の以前のコメントによると、開始する最善の方法は、dotTrace や redgate の ANTS パフォーマンス プロファイラーなどのプロファイラーを実行して、非効率的ではあるものの小さなものにリファクタリングに時間を費やす前に、どのステップに最も時間がかかっているかを実際に調べることです。実際の時間の一部。

于 2012-04-02T23:24:44.813 に答える
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)など

于 2012-04-02T23:21:22.947 に答える