2014-01-13 7 views
3

우리는 네트워크에서 지속적으로 엄청난 양의 데이터를 읽는 응용 프로그램을 보유하고 있습니다. 우리는 GC (gen0 컬렉션의 누적 효과조차도 발견했습니다. 우리는 결과를 뒷받침하기 위해 ETW 수집을 사용했습니다.)를 가장 큰 병목으로 인식하여 메모리 풀링을 수행하여 콜렉션이 실행되지 않도록했습니다.모든 메모리가 사전 할당되어 풀링 된 전선에서 기본 유형 (int, decimal, Datetime ...)으로 변환 - System.String을 우회하는 방법

우리는 거대한 바이트 배열을 할당없이 네트워크에서 연속적으로 읽으려면 인코딩 배열을 사용하여 변환을 피하기 위해 인코딩 배열에서 동일한 작업을 수행 할 수 있지만 기본 유형 (int, decimal ..)으로 변환하는 방법은없는 것처럼 보입니다. .) BCL이 TryParse 메서드에서 수행하는 작업을 다시 구현하는 것을 의미하거나) 쓰레기를 만들지 않고 (char []를 버려지는 문자열로 변환하지 않고).

  • 어떻게 든 문자열에 문자 배열을 삽입, 또는 기타 메모리의 재사용 가능한 풀에서 메모리를 할당 할 수있는 문자열을 강제 할 수 있는가 :

    그래서 여기 내 질문입니까? 나는 문자열의 반영 내부에보고되었고, 불가능한 작업이 될 것 같다,하지만 난

또는

  • 이 가능한 기본 유형으로 변환하는 몇 가지 표준 변환 기능을 활용하는 것입니다 어떤 제안을 환영 char [] (또는 다른 텍스트이지만 System.String 형식은 아님)? 다시 - 나는 System.Number의 반사 된 코드를 조사하고 있었다. 위장 함수가 char *을 취하는 것처럼 보이므로 반사를 통해 호출 할 수있다. 그러나 DateTime 변환은 여전히 ​​문자열을 사용합니다.

모든 의견을 환영합니다.

+0

나에게 C#을 사용할 수없는 것 같습니다. – Sinatr

+0

프로토콜을 변경하여 이러한 유형이 처음에는 텍스트 표현으로 변환되지 않도록 할 수 있습니까? 아니면이 한쪽 끝만 통제 할 수 있습니까? –

+0

@Damien_The_Unbeliever 아주 좋은 지적입니다. 불행히도 구독 엔드 포인트를 제어 할뿐입니다. 그리고 프로토콜은 엄격한 텍스트 표현입니다. – Jan

답변

1

안전하지 않은 코드가 응용 프로그램에 대한 대체 가능한 대체 코드 인 경우 문자열의 내용과 길이를 다시 쓸 수 있습니다. 이렇게하면 다시 할당 할 수있는 문자열을 재사용 할 수 있으므로 가비지 수집을 피할 수 있습니다.

AC 번호 문자열과 같은 메모리에 뻗어 :

int Capacity; 
int Length; 
char FirstCharacter; 
// remaining characters follow 

문자 데이터가 널 종료하고 현재 길이, 최대 용량 (단독 C/C++ 코드 간 동작 용이성) 인 pesky buffer overrun problems을 피할 수 있도록 저장됩니다. 당신이 재활용에 같은 문자열을 다시 구문 분석 할 수있는 장소에두고

static unsafe void RecycleString(string s, char[] newcontents) 
    { 
     // First, fix the string so the GC doesn't move it around on us, and get a pointer to the character data. 
     fixed (char* ps = s) 
     { 
      // We need an integer pointer as well, to check capacity and update length. 
      int* psi = (int*)ps; 
      int capacity = psi[-2]; 

      // Don't overrun the buffer! 
      System.Diagnostics.Debug.Assert(capacity > newcontents.Length); 
      if (capacity > newcontents.Length) 
      { 
       for (int i = 0; i < newcontents.Length; ++i) 
       { 
        ps[i] = newcontents[i]; 
       } 

       // Add null terminator and update length accordingly. 
       ps[newcontents.Length] = '\0'; 
       psi[-1] = newcontents.Length; 
      } 
     } 
    } 

, : 여기

새로운 메모리를 할당하지 않고 기존 문자열에 새로운 내용을 주입하는 방법입니다 너의 마음의 내용.하나의 희망으로

private static void ReusableStringTest() 
    { 
     char[] intFromWire = new char[] { '9', '0', '0', '0' }; 
     char[] floatFromWire = new char[] { '3', '.', '1', '4', '1', '5' }; 

     string reusableBuffer = new string('\0', 128); 

     RecycleString(reusableBuffer, intFromWire); 
     int i = Int32.Parse(reusableBuffer); 
     Console.WriteLine("Parsed integer {0}", i); 

     RecycleString(reusableBuffer, floatFromWire); 
     float f = Single.Parse(reusableBuffer); 
     Console.WriteLine("Parsed float {0}", f); 
    } 

생성 된 출력은 다음과 같습니다 : 여기에 보여주기 위해 간단한 예제

 
Parsed integer 9000 
Parsed float 3.1415 

을 그리고 안전하지 않은 코드가 긴장하게하는 경우, 단지 우리가 C 및 C 프로그래밍 보낸 모든 년을 기억 ++ , 모든 것 안전하지 않았습니다!

+1

어쨌든 잡기가 거의 없습니다. 문자열 내부는 문서화되어 있지 않으므로 최신 버전의 .NET에서는 변경되지 않습니다. 문자열의 legth (s [-1] = newLength)를 변경하면 GC 알고리즘으로 어떤 것을 엉망으로 만든 것처럼 보입니다. 이 접근법을로드하고 GC.Collect() 호출을 주입하면 GC 실행 중에 처리되지 않은 예외가 발생합니다. 그런 이유로 나는 재사용 가능한 풀링 된 바이트 버퍼 위에 opearate하기로 결정하고 기본 형식으로 /에서 내 자신의 변환기를 작성했습니다. – Jan

+0

흥미 롭습니다. 나는 아직 개념의 증거 (sic)를 넘어서 가지 못했다. 팁을 주셔서 감사한다. 추가 조사 및보고. – yoyo