StringBuilder의 이점을 이해합니다.
그러나 두 문자열을 연결하려면 StringBuilder없이 수행하는 것이 더 낫다고 가정합니다. 이 올바른지?
어느 시점 (문자열 수)에서 StringBuilder를 사용하는 것이 더 좋습니까?
StringBuilder의 이점을 이해합니다.
그러나 두 문자열을 연결하려면 StringBuilder없이 수행하는 것이 더 낫다고 가정합니다. 이 올바른지?
어느 시점 (문자열 수)에서 StringBuilder를 사용하는 것이 더 좋습니까?
답변:
Jeff Atwood가 쓴 The Sad Tragedy of Micro-Optimization Theatre 를 읽어 보시기 바랍니다 .
Simple Concatenation 대 StringBuilder 대 다른 메서드를 처리합니다.
이제 숫자와 그래프를 보려면 링크를 따르십시오.)
그러나 두 문자열을 연결하려면 StringBuilder없이 수행하는 것이 더 낫다고 가정합니다. 이 올바른지?
그것은 참으로 정확합니다, 당신은 왜 정확하게 설명되었는지 찾을 수 있습니다 :
http://www.yoda.arachsys.com/csharp/stringbuilder.html
요약 : 문자열을 한 번에 연결할 수 있다면
var result = a + " " + b + " " + c + ..
복사본이 만들어 질 때만 StringBuilder를 사용하지 않는 것이 좋습니다 (결과 문자열의 길이는 미리 계산됩니다.);
같은 구조를 위해
var result = a;
result += " ";
result += b;
result += " ";
result += c;
..
새 객체는 매번 생성되므로 StringBuilder를 고려해야합니다.
마지막으로이 기사는 이러한 경험 규칙을 요약합니다.
경험의 규칙
그렇다면 언제 StringBuilder를 사용해야하고 언제 문자열 연결 연산자를 사용해야합니까?
사소하지 않은 루프에서 연결할 때 반드시 StringBuilder를 사용하십시오. 특히 루프를 통해 반복 할 수있는 반복 횟수를 (컴파일시) 확실하지 않은 경우 특히 그렇습니다. 예를 들어, 한 번에 한 문자 씩 파일을 읽고 + = 연산자를 사용하여 문자열을 작성하는 것은 잠재적으로 성능 자살입니다.
하나의 명령문에서 연결해야하는 모든 것을 (읽기 쉽게) 지정할 수있는 경우 연결 연산자를 반드시 사용하십시오. (연결할 배열이있는 경우 String.Concat을 명시 적으로 호출하거나 구분 기호가 필요한 경우 String.Join을 호출하는 것이 좋습니다.)
리터럴을 여러 개의 연결된 비트로 나누는 것을 두려워하지 마십시오. 결과는 동일합니다. 예를 들어 성능에 영향을주지 않고 긴 리터럴을 여러 줄로 나누면 가독성을 높일 수 있습니다.
다음 연결 반복을 제공하는 것 이외의 다른 연결에 대한 중간 결과가 필요한 경우 StringBuilder가 도움이되지 않습니다. 예를 들어, 이름과 성에서 전체 이름을 만든 다음 마지막에 세 번째 정보 (별명)를 추가하면 StringBuilder를 사용하지 않는 경우에만 혜택을받을 수 있습니다. 다른 목적을 위해 (이름 + 성) 문자열이 필요합니다 (Person 객체를 생성하는 예제에서와 같이).
몇 개의 연결 만 수행 할 수 있고이를 별도의 문으로 수행하려는 경우 어느 방향으로 가는지는 중요하지 않습니다. 어떤 방법이 더 효율적인지는 관련된 문자열의 크기와 연결되는 순서에 따라 달라집니다. 만약 당신이 정말로 그 코드가 성능 병목 현상이라고 생각한다면 프로파일 링하거나 벤치마킹하십시오.
System.String은 변경 불가능한 개체입니다. 즉, 콘텐츠를 수정할 때마다 새 문자열이 할당되며 시간과 메모리가 필요합니다. StringBuilder를 사용하면 새 개체를 할당하지 않고 개체의 실제 콘텐츠를 수정할 수 있습니다.
따라서 문자열을 많이 수정해야 할 때 StringBuilder를 사용하십시오.
별로 ... 큰 문자열 을 연결 하거나 루프와 같이 연결이 많은 경우 StringBuilder를 사용해야합니다 .
StringBuilder
루프 또는 연결이 사양에 대한 성능 문제인 경우에만 사용해야 합니다.
string s = "abcd"
마지막 일이 그 적어도, 나는 들었지만 변수를 사용하면 Concat 일 가능성이 큽니다.
a + "hello" + "somethingelse"
했고 그것에 대해 걱정할 필요가 없었습니다. 문제가된다면 StringBuilder를 사용하겠습니다. 하지만 처음에는 걱정하지 않았고 글을 쓰는 데 시간을 덜 들였습니다.
요점을 증명하는 간단한 테스트 앱은 다음과 같습니다.
class Program
{
static void Main(string[] args)
{
const int testLength = 30000;
var StartTime = DateTime.Now;
//TEST 1 - String
StartTime = DateTime.Now;
String tString = "test string";
for (int i = 0; i < testLength; i++)
{
tString += i.ToString();
}
Console.WriteLine((DateTime.Now - StartTime).TotalMilliseconds.ToString());
//result: 2000 ms
//TEST 2 - StringBuilder
StartTime = DateTime.Now;
StringBuilder tSB = new StringBuilder("test string");
for (int i = 0; i < testLength; i++)
{
tSB.Append(i.ToString());
}
Console.WriteLine((DateTime.Now - StartTime).TotalMilliseconds.ToString());
//result: 4 ms
Console.ReadLine();
}
}
결과 :
30,000 회 반복
1000 회 반복
500 회 반복
의역하다
그러면 너는 더도 더도 더도 더도 더도 덜도 안된다. 세 개는 세어야 할 숫자이고 세는 수는 세 개입니다. 네가 세지 말고 두 세도 세지 말라. 단 세 명으로 넘어가는 것 외에는 말이다. 세 번째 숫자 인 세 번째 숫자에 도달하면 안티오크의 성스러운 수류탄을 로브 베스트하십시오
나는 일반적으로 세 개 이상의 문자열을 연결하는 코드 블록에 문자열 작성기를 사용합니다.
그러나 2 개의 문자열을 연결하려면 StringBuilder없이 연결하는 것이 더 좋고 빠르다고 가정합니다. 이 올바른지?
예. 그러나 더 중요한 것은 그러한 상황에서 바닐라를 사용하는 것이 훨씬 더 읽기 쉽다 는 것입니다 String
. 반면에 루프에서 사용하는 것은 의미가 있으며 연결만큼 읽을 수도 있습니다.
특정 수의 연결을 임계 값으로 인용하는 경험 규칙을주의해야합니다. 루프 (및 루프에만 해당)에서 사용하는 것은 아마도 유용하고 기억하기 쉽고 더 합리적 일 것입니다.
의견에 영향을받지 않거나 자존심의 싸움이 뒤 따르지 않는 이에 대한 설명을 찾기가 어렵 기 때문에 직접 테스트하기 위해 LINQpad에 약간의 코드를 작성하려고 생각했습니다.
i.ToString ()을 사용하는 대신 작은 크기의 문자열을 사용하면 응답 시간이 변경된다는 것을 발견했습니다 (작은 루프에서 볼 수 있음).
이 테스트는 서로 다른 반복 순서를 사용하여 시간 측정을 현명하게 비교할 수있는 범위로 유지합니다.
마지막에 코드를 복사하여 직접 시도해 볼 수 있습니다 (results.Charts ... Dump ()는 LINQPad 외부에서 작동하지 않습니다).
출력 (X 축 : 테스트 된 반복 횟수, Y 축 : 소요 시간 (틱)) :
반복 순서 : 2, 3, 4, 5, 6, 7, 8, 9, 10
반복 순서 : 10, 20, 30, 40, 50, 60, 70, 80
반복 순서 : 100, 200, 300, 400, 500
코드 (LINQPad 5를 사용하여 작성) :
void Main()
{
Test(2, 3, 4, 5, 6, 7, 8, 9, 10);
Test(10, 20, 30, 40, 50, 60, 70, 80);
Test(100, 200, 300, 400, 500);
}
void Test(params int[] iterationsCounts)
{
$"Iterations sequence: {string.Join(", ", iterationsCounts)}".Dump();
int testStringLength = 10;
RandomStringGenerator.Setup(testStringLength);
var sw = new System.Diagnostics.Stopwatch();
var results = new Dictionary<int, TimeSpan[]>();
// This call before starting to measure time removes initial overhead from first measurement
RandomStringGenerator.GetRandomString();
foreach (var iterationsCount in iterationsCounts)
{
TimeSpan elapsedForString, elapsedForSb;
// string
sw.Restart();
var str = string.Empty;
for (int i = 0; i < iterationsCount; i++)
{
str += RandomStringGenerator.GetRandomString();
}
sw.Stop();
elapsedForString = sw.Elapsed;
// string builder
sw.Restart();
var sb = new StringBuilder(string.Empty);
for (int i = 0; i < iterationsCount; i++)
{
sb.Append(RandomStringGenerator.GetRandomString());
}
sw.Stop();
elapsedForSb = sw.Elapsed;
results.Add(iterationsCount, new TimeSpan[] { elapsedForString, elapsedForSb });
}
// Results
results.Chart(r => r.Key)
.AddYSeries(r => r.Value[0].Ticks, LINQPad.Util.SeriesType.Line, "String")
.AddYSeries(r => r.Value[1].Ticks, LINQPad.Util.SeriesType.Line, "String Builder")
.DumpInline();
}
static class RandomStringGenerator
{
static Random r;
static string[] strings;
public static void Setup(int testStringLength)
{
r = new Random(DateTime.Now.Millisecond);
strings = new string[10];
for (int i = 0; i < strings.Length; i++)
{
strings[i] = Guid.NewGuid().ToString().Substring(0, testStringLength);
}
}
public static string GetRandomString()
{
var indx = r.Next(0, strings.Length);
return strings[indx];
}
}
사용할 때와 사용하지 않을 때 사이에 미세한 경계가 있다고 생각하지 않습니다. 물론 누군가가 황금 조건을 얻기 위해 광범위한 테스트를 수행하지 않는 한.
나를 위해 2 개의 거대한 문자열을 연결하는 경우 StringBuilder를 사용하지 않을 것입니다. 결정적이지 않은 개수의 루프가있는 경우 루프가 작은 개수 일 수도 있습니다.
단일 연결은 StringBuilder를 사용할 가치가 없습니다. 일반적으로 경험상 5 개의 연결을 사용했습니다.