2012-04-02 2 views
1

내가 작업중인 게임을위한 범용 소켓 서버를 작성하려고합니다. SmartFox 및 Photon과 같은 이미 구축 된 서버를 사용할 수는 있지만, 학습 목적으로 직접 만드는 것의 어려움을 겪지 않을 것입니다.이진 변환을 개선하고 C#으로 돌아 가기

BSON에서 영감을받은 프로토콜을 사용하여 기본 데이터 유형, 해당 배열 및 특수 GSObject를 바이너리로 변환 한 다음이를 다시 배열하여 객체 형식으로 다시 배치 할 수 있도록했습니다. 클라이언트 끝. 핵심에서 변환 방법은 .Net BitConverter 클래스를 사용하여 기본 데이터 형식을 이진 형식으로 변환합니다. 어쨌든, 문제는 성능입니다. 반복 횟수를 50,000 회 돌리고 내 GSObject를 5500ms가 될 때마다 바이너리로 변환합니다. 결과 바이트 []는 변환 당 192 바이트입니다. 나는 이것이 1000 명의 동시 사용자로 초당 5-10 개의 위치 업데이트를 보내는 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"); 

    } 

위의 코드에서 사용되는 다른 클래스의 코드는 다음과 같습니다. 대부분은 반복적입니다.

+0

시간을 어디에서 사용했는지 프로필러에서 확인해 보았습니까? 필자는 개인적으로 dotTrace (http://www.jetbrains.com/profiler/)를 선호하지만 프로파일 작성기는 그렇게 할 것입니다. –

+0

@ChrisShain 아니, 나는 그것을 할 수 있다는 것을 실제로 알지 못했다. 그러나 나는 그것을 조사 할 것이다. 제안에 감사한다. –

+1

이 게시물은 멀리, 너무 오래 됐어. 문제의 관련 부분을 집중하여 코드를 다시 10 %로 줄이십시오. – Gray

답변

1

1

GSWrappedObject

GSArrayGSObject

) ElementAt 매우 비싸다. for (int i = 0; i < length; i++) .. .ElementAt(i) ..

대신 foreach (var v in value)을 사용하십시오. 2) ToGsBinary 메서드는 배열을 자주 복사하기 때문에 값이 비쌉니다. 에 서명 사용 void WriteToGsBinary(Stream stream) 대신

3 byte[] ToGsBinary()의) 배열에 대한 과부하를 추가 : void WriteToGsBinary(Stream stream, byte[] values), void WriteToGsBinary(Stream stream, short[] values)

+0

고마워, 그게 문제 중 하나라고 생각해. ToArray를 호출 할 필요가 없으므로 IEnumerable 대신 배열 매개 변수를 수락한다고 생각하십니까? –

+0

@SaadImran. .Net에서 가장 빠른 컬렉션은 직접 액세스 할 수있는 배열입니다. List로 List, IEnumerable로 List, IList로 List, IEnumerable로 배열 등은 Array보다 Array가 더 느립니다. –

2

내 첫 직감, 많은없이 꺼져 갈은 많은 시간이 지속적으로 침몰되고 있다는 것 배열과리스트를 다시 생성합니다.

나는 배열을 끊임없이 만들려고 시도하는 대신 스트림 기반 접근 방식으로 옮겨 가고 싶다.즉, 모든 GSBinary 메서드가 스트림을 받아들이고 자신의 배열을 만드는 것이 아니라 로컬 메모리에 넣고 싶다면 기본에 MemoryStream을 사용하고 마지막에 배열을 꺼내십시오 (또는 이 기능을 네트워크로 연결된 응용 프로그램으로 계획하고 있다면 네트워크 스트림에 직접 기록하십시오.

Chris의 의견에 따르면 이전에 가장 좋은 방법은 dotTrace 또는 redgate의 ANTS 성능 프로파일 러와 같은 프로파일 러를 실행하여 비효율적 인 리팩토링 시간을 투자하기 전에 어느 단계가 가장 많은 시간을 들여야하는지 실제로 확인하는 것입니다. 실제 시간의 아주 작은 부분 일뿐입니다.

+0

제안 해 주셔서 감사합니다. 내가 수행 한 인터넷 검색에서 찾은 것 중 하나였습니다. 나는 그것을 들여다 볼 것이다. –