C\#StringBuilder 메모리 조각 이 성능 에 미 치 는 영향

StringBuilder 내 부 는 다단 계 char[]로 구 성 된 반자동 링크 이기 때문에 중간 에서 StringBuilder 를 자주 수정 하면 원래 연속 적 인 메모 리 를 다단 계 로 구분 하여 읽 기/옮 겨 다 니 기 성능 에 영향 을 줄 수 있 습 니 다.
연속 메모리 와 불 연속 메모리 의 성능 이 떨 어 지면 1600 배 에 이 를 수 있다.
배경
StringBuilder 를 사용 하 는 사용 자 는 대부분 StringBuilder 로 html/json 템 플 릿 을 연결 하고 동적 SQL 을 조립 하 는 등 정상 적 인 작업 을 하려 고 할 수 있 습 니 다.그러나 일부 특수 한 장면 에서-예 를 들 어 특정한 프로 그래 밍 언어 에 언어 서 비 스 를 쓰 거나 부 텍스트 편집 기 를 쓸 때 StringBuilder 는 여전히 쓸모 가 있 으 며,안의 Insert/Remove 두 가지 방법 으로 수정 할 수 있다.
테스트 방법
Talk is cheap, show me the code:

int docLength = 10000;
void Main()
{
  (from power in Enumerable.Range (1, 16)
  let mutations = (int) Math.Pow (2, power)
  select new
  {
    mutations,
    PerformanceRatio = Math.Round (GetPerformanceRatio (docLength, mutations), 1)
  }).Dump();
}

float GetPerformanceRatio (int docLength, int mutations)
{
  var sb = new StringBuilder ("".PadRight (docLength));
  var before = GetPerformance (sb);
  FragmentStringBuilder (sb, mutations);
  var after = GetPerformance (sb);
  return (float) after.Ticks / before.Ticks;
}

void FragmentStringBuilder (StringBuilder sb, int mutations)
{
  var r = new Random(42);
  for (int i = 0; i < mutations; i++)
  {
    sb.Insert (r.Next (sb.Length), 'x');
    sb.Remove (r.Next (sb.Length), 1);
  }
}

TimeSpan GetPerformance (StringBuilder sb)
{
  var sw = Stopwatch.StartNew();
  long tot = 0;
  for (int i = 0; i < sb.Length; i++)
  {
    char c = sb[i];
    tot += (int) c;
  }
  sw.Stop();
  return sw.Elapsed;
}

이 코드 에 대해 다음 과 같은 몇 가 지 를 주의 하 십시오.
  • .PadRight(n)를 통 해 n 길이 의 공백 문자열 을 직접 만 들 고 new string(',n)으로 대체 할 수 있 습 니 다.
  • new Random(42)에서 저 는 무 작위 인 자 를 지정 하여 매번 분 리 된 후에 분 리 된 위치 가 똑 같 고 대조 팀 에 유리 하도록 합 니 다.
  • 저 는 문자열 을 각각 2^1~2^16 번 수정 하여 이렇게 여러 번 수정 한 후의 성능 차 이 를 비교 하 였 습 니 다.
  • 저 는 sb[i]를 사용 하여 StringBuilder 의 위 치 를 하나씩 방문 하여 메모리 의 불 연속 성 을 더욱 돋 보이 게 합 니 다.
  • 실행 결과
    mutations
    PerformanceRatio
    2
    1
    4
    1
    8
    1
    16
    1
    32
    1
    64
    1.1
    128
    1.2
    256
    1.8
    512
    5.2
    1024
    19.9
    2048
    81.3
    4096
    274.5
    8192
    745.8
    16384
    1578.8
    32768
    1630.4
    65536
    930.8
    이 를 통 해 알 수 있 듯 이 StringBuilder 중간 에 대량의 수정 을 하면 그 성능 이 급 격 히 떨 어 집 니 다.32768 번 의 수정 을 주의 깊 게 보면 한 번 에 1630.4 배 에 달 하 는 성능 이 떨 어 집 니 다!
    해결 방법
    StringBuilder 를 사용 해 야 한다 면 일정 횟수 를 수정 한 후에 새로운 StringBuilder 를 다시 만 드 는 것 을 고려 하여 접근 할 때 가장 좋 은 메모리 연속 성 을 얻 도록 하면 이 문 제 를 해결 할 수 있 습 니 다.
    
    void FragmentStringBuilder (StringBuilder sb, int mutations)
    {
      var r = new Random(42);
      for (int i = 0; i < mutations; i++)
      {
        sb.Insert (r.Next (sb.Length), 'x');
        sb.Remove (r.Next (sb.Length), 1);
        
        //   
        const int defragmentCount = 250;
        if (i % defragmentCount == defragmentCount - 1)
        {
          string buf = sb.ToString();
          sb.Clear();
          sb.Append(buf);
        }
      }
    }
    
    위 와 같이 250 번 의 수정 을 거 칠 때마다 원래 의 StringBuilder 를 삭제 하고 새로운 StringBuilder 를 다시 만 듭 니 다.이 때 실행 효 과 는 다음 과 같 습 니 다.
    mutations
    PerformanceRatio
    2
    1.2
    4
    0.7
    8
    1
    16
    1
    32
    1
    64
    1.1
    128
    1.2
    256
    1
    512
    1
    1024
    1
    2048
    1
    4096
    1.1
    8192
    1.5
    16384
    1.3
    32768
    1
    65536
    1
    이 를 통 해 알 수 있 듯 이 거의 모든 상황 에서 메모리 의 불 연속 으로 인 한 접근 성능 문 제 를 해결 하 는 동시에 250 은 상대 적 으로 합 리 적 인 숫자 로 성능 과 조회/스 트 리밍 성능 을 삽입 하여 균형 을 잡 을 수 있다.
    반성 과 총 결
    string 의 불변성 으로 인해 대량의 문자열 을 연결 할 때 대량의 메모 리 를 낭비 하 는 것 은 잘 알려 져 있다.하지만 StringBuilder 를 사용 하 는 것 도 그 구 조 를 알 아야 합 니 다.
    StringBuilder 가 이렇게 체인 구 조 를 만 드 는 데 는 이유 가 없 는 것 이 아니 라 삽입 성능 을 고려 하면 체인 인 터 페 이 스 를 만 드 는 것 이 가장 우수 하 다.그러나 조회 성능 을 고려 하면 체인 구 조 는 매우 불리 하 다.만약 에 비 체인 구조 로 설계 하면 중간 에 삽입 할 때 StringBuilder 의 메모리 공간 이 부족 할 수 있 기 때문에 메모 리 를 재분배 해 야 한다.이 는 StringBuilder 를 string 으로 격 하 하 하 는 것 과 같 기 때문에 StringBuilder 가'빈번 한 삽입'에 적합 하 다 는 장점 을 완전히 잃 었 다.
    본 고 는 사실 매우 특수 한 예 이다.현실 에서 언어 서비스,편집 기 를 제외 하고 이런 삽입 이 빠 르 고 빠 른 장면 도 자주 수정 해 야 한다.간단하게 하려 면 StringBuilder 를 사용 하 는 것 이 조건 부 해결 방안 이 될 것 입 니 다.더 적합 한 해결 방안 은 당연히 전문 적 인 데이터 구조 인 PieceTable 입 니 다.마이크로소프트 는 VSCode 편집기 에서 큰 파일 편집 성능 을 확보 하기 위해 이 데이터 구 조 를 사용 하여 좋 은 성 과 를 거 두 었 습 니 다.링크 참조:Text Buffer Reimplementation.
    StringBuilder 메모리 조각 이 성능 에 미 치 는 영향 에 관 한 이 글 은 여기까지 소개 되 었 습 니 다.StringBuilder 메모리 조각 에 관 한 더 많은 내용 은 이전 글 을 검색 하거나 아래 글 을 계속 찾 아 보 세 요.앞으로 도 많은 응원 부탁드립니다!

    좋은 웹페이지 즐겨찾기