구문 분석 성능(If, TryParse, Try-Catch)
문제
나는 정보를 얻기 위해 텍스트를 구문 분석하는 다양한 방법을 잘 알고 있습니다.예를 들어 정수를 구문 분석하는 경우 어떤 종류의 성능을 기대할 수 있습니까?이에 대한 좋은 통계를 아는 사람이 있는지 궁금합니다.나는 이것을 테스트한 누군가로부터 몇 가지 실수를 찾고 있습니다.
다음 중 어떤 상황에서 최고의 성능을 제공하는 것은 무엇입니까?
Parse(...) // Crash if the case is extremely rare .0001%
If (SomethingIsValid) // Check the value before parsing
Parse(...)
TryParse(...) // Using TryParse
try
{
Parse(...)
}
catch
{
// Catch any thrown exceptions
}
해결책
항상 사용 T.TryParse(문자열 str, out T 값).예외를 던지는 것은 비용이 많이 들기 때문에 상황을 처리할 수 있다면 피해야 합니다. 선험적으로.성능을 "절약"하기 위해 try-catch 블록을 사용하는 것은(잘못된 데이터 비율이 낮기 때문에) 유지 관리 가능성과 좋은 코딩 방법을 희생하면서 예외 처리를 남용하는 것입니다.건전한 소프트웨어 엔지니어링 개발 관행을 따르고, 테스트 사례를 작성하고, 애플리케이션을 실행한 다음 벤치마킹하고 최적화하세요.
"약 97%의 경우 작은 효율성은 잊어야 합니다. 성급한 최적화는 모든 악의 근원이다.하지만 우리는 중요한 3%에 있는 기회를 놓쳐서는 안 됩니다." -Donald Knuth
따라서 탄소 배출권에서처럼 임의로 try-catch의 성능을 할당합니다. 더 나쁜 TryParse의 성능은 더 나은.애플리케이션을 실행하고 일종의 속도 저하가 있음을 확인한 후에야문자열 구문 분석에는 TryParse 이외의 다른 것을 사용하는 것도 고려해 볼 수 있습니다.
(편집하다:질문자는 타이밍 데이터가 좋은 조언과 함께 제공되기를 원한 것으로 보이므로 여기에 요청된 타이밍 데이터가 있습니다)
사용자가 입력한 10,000개의 다양한 실패율에 대한 시간(믿지 않는 경우):
Failure Rate Try-Catch TryParse Slowdown
0% 00:00:00.0131758 00:00:00.0120421 0.1
10% 00:00:00.1540251 00:00:00.0087699 16.6
20% 00:00:00.2833266 00:00:00.0105229 25.9
30% 00:00:00.4462866 00:00:00.0091487 47.8
40% 00:00:00.6951060 00:00:00.0108980 62.8
50% 00:00:00.7567745 00:00:00.0087065 85.9
60% 00:00:00.7090449 00:00:00.0083365 84.1
70% 00:00:00.8179365 00:00:00.0088809 91.1
80% 00:00:00.9468898 00:00:00.0088562 105.9
90% 00:00:01.0411393 00:00:00.0081040 127.5
100% 00:00:01.1488157 00:00:00.0078877 144.6
/// <param name="errorRate">Rate of errors in user input</param>
/// <returns>Total time taken</returns>
public static TimeSpan TimeTryCatch(double errorRate, int seed, int count)
{
Stopwatch stopwatch = new Stopwatch();
Random random = new Random(seed);
string bad_prefix = @"X";
stopwatch.Start();
for(int ii = 0; ii < count; ++ii)
{
string input = random.Next().ToString();
if (random.NextDouble() < errorRate)
{
input = bad_prefix + input;
}
int value = 0;
try
{
value = Int32.Parse(input);
}
catch(FormatException)
{
value = -1; // we would do something here with a logger perhaps
}
}
stopwatch.Stop();
return stopwatch.Elapsed;
}
/// <param name="errorRate">Rate of errors in user input</param>
/// <returns>Total time taken</returns>
public static TimeSpan TimeTryParse(double errorRate, int seed, int count)
{
Stopwatch stopwatch = new Stopwatch();
Random random = new Random(seed);
string bad_prefix = @"X";
stopwatch.Start();
for(int ii = 0; ii < count; ++ii)
{
string input = random.Next().ToString();
if (random.NextDouble() < errorRate)
{
input = bad_prefix + input;
}
int value = 0;
if (!Int32.TryParse(input, out value))
{
value = -1; // we would do something here with a logger perhaps
}
}
stopwatch.Stop();
return stopwatch.Elapsed;
}
public static void TimeStringParse()
{
double errorRate = 0.1; // 10% of the time our users mess up
int count = 10000; // 10000 entries by a user
TimeSpan trycatch = TimeTryCatch(errorRate, 1, count);
TimeSpan tryparse = TimeTryParse(errorRate, 1, count);
Console.WriteLine("trycatch: {0}", trycatch);
Console.WriteLine("tryparse: {0}", tryparse);
}
다른 팁
나는 개인적으로 다른 방법을 프로파일하지 않았지만이 챕터는 다음과 같습니다.
Try-Catch는 항상 느리게됩니다. TryParse가 더 빠릅니다.
IF와 TryParse는 동일합니다.
여기에 있습니다 성능 차이를 프로파일 링 한 또 다른 챕터, 그리고 몇 가지 다른 데이터 유형으로, (전혀) 얼마나 많은 시간을 확인하면, 그것들은 본질적으로 성능에 영향을 미칩니다 : int, bool 및 dateTime.
Option 1: Will throw an exception on bad data.
Option 2: SomethingIsValid() could be quite expensive - particularly if you are pre-checking a string for Integer parsability.
Option 3: I like this. You need a null check afterwards, but it's pretty cheap.
Option 4 is definitely the worst.
예외 처리는 비교적 비싸므로 가능하다면 피하십시오.
특히, 예외적 인 것이 아니라 나쁜 입력이 예상 되므로이 상황에 사용해서는 안됩니다.
(TryParse 이전에는 최선의 선택 일 수 있습니다.)