1. Math Expression Parser 비교
1.1 DataTable
.Net에서 제공하는 DataTable Class의 Compute 메서드를 이용해 식을 계산한 코드이다. 먼저 a, b 문자를 값으로 치환한 후 계산을 진행한다. Compute 메서드의 반환값은 object로써 type에 맞게 캐스팅 해주어야하는 단점이 있다. 테스트는 아래와 같이 진행하였다.
object res = null; DataTable dt = new DataTable(); string exp = expression; foreach (KeyValuePair<string, string> item in values) { exp = exp.Replace(item.Key, item.Value); } Stopwatch st = Stopwatch.StartNew(); for (int i = 0; i < 100000; i++) { res = dt.Compute(exp, null); Type type = res.GetType(); if (type == typeof(int)) { int a = (int)res; } else { double b = (double)res; } } st.Stop(); Console.WriteLine(string.Format("DataTable = {0} : {1}", st.Elapsed.ToString(), res.ToString())); |
1.2 Ncalc
CodePlex에 등록되어 있는 오픈소스로써 System.Math 클래스의 메서들과 사용자 정의 함수를 지원한다. 사용자 정의 함수는 이벤트 방식으로 함수명으로 비교해서 개발자가 직접 처리를 해주어야 한다.
Debug.Assert(0 == new Expression("Sin(0)").Evaluate()); Debug.Assert(2 == new Expression("Sqrt(4)").Evaluate()); Debug.Assert(0 == new Expression("Tan(0)").Evaluate());
//함수 처리 Expression e = new Expression("SecretOperation(3, 6)"); e.EvaluateFunction += delegate(string name, FunctionArgs args) { if (name == "SecretOperation") args.Result= (int)args.Parameters[0].Evaluate() + (int)args.Parameters[1].Evaluate(); }; Debug.Assert(9 == e.Evaluate()); |
테스트는 아래와 같이 진행하였다.
object res = null; NCalc.Expression ex = new NCalc.Expression(expression); ex.Parameters.Clear(); Dictionary<string, object> v = new Dictionary<string, object>(); foreach (KeyValuePair<string, string> item in values) { ex.Parameters.Add(item.Key, Convert.ToSingle(item.Value)); } Stopwatch st = Stopwatch.StartNew(); for (int i = 0; i < 100000; i++) { res = ex.Evaluate(); } st.Stop(); Console.WriteLine(string.Format("NCalc = {0} : {1}", st.Elapsed.ToString(), res.ToString())); |
1.3 Flee
CodePlex에 등록되어 있는 오픈 소스로써 Fast Lightweight Expression Evaluator라는 이름과 같이 테스트한 오픈소스 중에서는 가장 빠르다. System.Math Class 및 타 Class를 등록해서 사용 할 수있고, 사용자 정의 함수 기능을 제공한다. 사용자 정의 함수는 문자열로 정의 할 수 있지만, 매개변수가 바뀔시 Recalculate라는 메서드를 호출하여 엔진 내부를 수정 해야 하는 문제가 있다.
// 함수 테스트
|
테스트는 아래와 같이 진행하였다.
ExpressionContext context = new ExpressionContext(); double res = double.NaN; context.Variables.Clear(); foreach (KeyValuePair<string, string> item in values) { context.Variables[item.Key] = Convert.ToDouble(item.Value); } IGenericExpression<double> exp = context.CompileGeneric<double>(expression); Stopwatch st = Stopwatch.StartNew(); for (int i = 0; i < 100000; i++) { res = exp.Evaluate(); } st.Stop(); Console.WriteLine(string.Format("Flee = {0} : {1}", st.Elapsed.ToString(), res.ToString())); |
1.4 MathParseNet
CodeProject에 등록되어 있는 오픈 소스로써 sin, cos, tg, ctg 등 기본적인 수학 함수를 제공한다. 식의 해석과 동시에 값을 반환하는 형식이여서 오픈 소스 중에 가장 느렸고, 반환 값의 타입은 int, double 두개의 타입만 존재한다.
|
1.5 테스트
컴파일 모드 : Release
테스트한 식 : (a * b) / 2 + 3 - 2 * 321
테스트 값 : float a = 2, float b = 3
호출 테스트 수 : 100,000번
테스트 결과:
CSharp |
Lambda |
DataTable |
Ncalc |
Flee |
MathParseNet | |
Test1 |
0.000352 |
0.000498 |
0.433453 |
0.205334 |
0.017759 |
14.45983 |
Test2 |
0.000259 |
0.0005 |
0.350152 |
0.204596 |
0.017735 |
14.61664 |
Test3 |
0.00028 |
0.000492 |
0.34059 |
0.154331 |
0.017455 |
14.54161 |
Test4 |
0.000243 |
0.000489 |
0.339338 |
0.154261 |
0.017464 |
14.52951 |
Test5 |
0.000251 |
0.000552 |
0.3525 |
0.206181 |
0.018056 |
14.5986 |
평균 |
0.000277 |
0.000506 |
0.363207 |
0.184941 |
0.017694 |
14.54924 |
CSharp 기준(배수) |
1 |
1.827653 |
1311.215 |
667.656 |
63.87588 |
52524.33 |
표 3 Parser 테스트 결과
1.6 결론
Flee 오픈소스가 가장 빠르지만, CSharp으로 하드코딩한 연산에 비해 63배나 느렸다. Parser들은 속도에 민간한 프로젝트에 적용하기는 힘들 것 같다.
프로젝트 :AboutCalculParser.zip
'.Net > Winform' 카테고리의 다른 글
C# 멀티 헤더 DataGridView (0) | 2014.08.14 |
---|---|
C# CodeDom과 Lambda Exprssion 비교 (0) | 2014.07.14 |
C# Excel 2007 추가기능(AddIn) 만들기 (0) | 2014.07.14 |
C# 마이크로(us) 단위의 Sleep 기능 구현 (0) | 2014.07.01 |
C# DataGridView AutoCompleteMode (0) | 2014.04.25 |