C# -4.0 응용 프로그램에서 동적으로 강력한 형식의 열 기반 테이블과 동일한 길이를 가진 강력한 형식의 ILists 사전이 있습니다. 사용자가 모든 행에 대해 집계 할 수있는 사용 가능한 열을 기반으로 하나 이상의 (파이썬 -) 표현식을 제공하기를 원합니다. 정적 컨텍스트에서는 다음과 같습니다.IronPython에서 대량 평가 식의 성능
IDictionary<string, IList> table;
// ...
IList<int> a = table["a"] as IList<int>;
IList<int> b = table["b"] as IList<int>;
double sum = 0;
for (int i = 0; i < n; i++)
sum += (double)a[i]/b[i]; // Expression to sum up
n = 10^7의 경우이 값은 랩톱 (win7 x64)에서 0.270 초입니다. 두 개의 int 인자를 가진 델리게이트로 표현식을 대체하기 위해서는 타입이없는 델리게이트 1.19 초 동안 0.580 초가 걸립니다. 5.5 요인 4 -
IDictionary<string, IList> table;
// ...
var options = new Dictionary<string, object>();
options["DivisionOptions"] = PythonDivisionOptions.New;
var engine = Python.CreateEngine(options);
string expr = "a/b";
Func<int, int, double> f = engine.Execute("lambda a, b : " + expr);
IList<int> a = table["a"] as IList<int>;
IList<int> b = table["b"] as IList<int>;
double sum = 0;
for (int i = 0; i < n; i++)
sum += f(a[i], b[i]);
와 IronPython의에서 대리자를 만들기는 3.2 초 (및 Func<object, object, object>
5.1 초) 소요됩니다. 이것은 내가하고있는 일에 대해 예상되는 오버 헤드입니까? 무엇을 개선 할 수 있습니까?
많은 열이있는 경우 위에서 선택한 방법으로는 충분하지 않습니다. 한 가지 해결책은 각 표현식에 필요한 열을 결정하고 인수로만 사용하는 것입니다. 필자가 시도하지 못한 다른 해결책은 ScriptScope를 사용하고 동적으로 열을 해결하는 것이 었습니다. 이를 위해 활성 행에 대한 RowIndex와 각 열에 대한 특성을 가진 RowIterator를 정의했습니다.
class RowIterator
{
IList<int> la;
IList<int> lb;
public RowIterator(IList<int> a, IList<int> b)
{
this.la = a;
this.lb = b;
}
public int RowIndex { get; set; }
public int a { get { return la[RowIndex]; } }
public int b { get { return lb[RowIndex]; } }
}
ScriptScope가 나는 C#의 동적에 의해 구현 될 것으로 예상 IDynamicMetaObjectProvider, 만들 수 있습니다 -하지만에서 런타임 engine.CreateScope (IDictionary)이 실패하는 호출을 시도하고 있습니다.
dynamic iterator = new RowIterator(a, b) as dynamic;
var scope = engine.CreateScope(iterator);
var expr = engine.CreateScriptSourceFromString("a/b").Compile();
double sum = 0;
for (int i = 0; i < n; i++)
{
iterator.Index = i;
sum += expr.Execute<double>(scope);
}
다음 나는 RowIterator이 DynamicObject에서 상속 수 있도록 노력하고 실행 예에 그것을 만들었다 - 끔찍한 성능 : 158 초.
class DynamicRowIterator : DynamicObject
{
Dictionary<string, object> members = new Dictionary<string, object>();
IList<int> la;
IList<int> lb;
public DynamicRowIterator(IList<int> a, IList<int> b)
{
this.la = a;
this.lb = b;
}
public int RowIndex { get; set; }
public int a { get { return la[RowIndex]; } }
public int b { get { return lb[RowIndex]; } }
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (binder.Name == "a") // Why does this happen?
{
result = this.a;
return true;
}
if (binder.Name == "b")
{
result = this.b;
return true;
}
if (base.TryGetMember(binder, out result))
return true;
if (members.TryGetValue(binder.Name, out result))
return true;
return false;
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
if (base.TrySetMember(binder, value))
return true;
members[binder.Name] = value;
return true;
}
}
TryGetMember가 속성의 이름으로 불려지는 것에 놀랐습니다. 설명서에서 TryGetMember는 정의되지 않은 속성에 대해서만 호출 될 것이라고 예상했습니다.
아마도 합리적인 성능을 위해 동적 CallSites를 사용하기 위해 RowIterator에 IDynamicMetaObjectProvider를 구현해야하지만, 적합한 예제를 찾을 수 없었습니다. 내 실험에서 나는 BindGetMember에 __builtins__
을 처리하는 방법을 알고하지 않았다 :
class Iterator : IDynamicMetaObjectProvider
{
IList<int> la;
IList<int> lb;
public Iterator(IList<int> a, IList<int> b)
{
this.la = a;
this.lb = b;
}
public int RowIndex { get; set; }
public int a { get { return la[RowIndex]; } }
public int b { get { return lb[RowIndex]; } }
public DynamicMetaObject GetMetaObject(Expression parameter)
{
return new MetaObject(parameter, this);
}
private class MetaObject : DynamicMetaObject
{
internal MetaObject(Expression parameter, Iterator self)
: base(parameter, BindingRestrictions.Empty, self) { }
public override DynamicMetaObject BindGetMember(GetMemberBinder binder)
{
switch (binder.Name)
{
case "a":
case "b":
Type type = typeof(Iterator);
string methodName = binder.Name;
Expression[] parameters = new Expression[]
{
Expression.Constant(binder.Name)
};
return new DynamicMetaObject(
Expression.Call(
Expression.Convert(Expression, LimitType),
type.GetMethod(methodName),
parameters),
BindingRestrictions.GetTypeRestriction(Expression, LimitType));
default:
return base.BindGetMember(binder);
}
}
}
}
내가 위에서 내 코드는 차선의 확신은, 적어도 아직 열 IDictionary를 처리하지 않습니다. 디자인 및/또는 성능을 향상시키는 방법에 대한 조언을 주시면 감사하겠습니다.
ScriptScope의 멤버로 IDMOP을 사용하는 대신 RowIterator를 ScriptScope에 삽입하거나 범위에서 벗어나는 델리게이트의 매개 변수로 삽입합니다. –
저는 ScriptScope의 _member_으로 IDMOP을 사용하지 않고 "컨텍스트"자체, 즉 "row.a/row.b"대신 "a/b"를 입력하고 싶습니다. 당신이 주사라고 부르는 것에 어떻게 이것을 할 수 있습니까? – Christian
@Dino : 대리자의 인수로 RowIterator를 사용하는 것이 성능과 관련하여 좋은 아이디어라고 생각합니다. 어떻게하면 "a/b"(또는 무엇이든)를 "row.a/row.b"로 대체 할 수 있습니다. 대신 실제로 컴파일 할 것입니다. – Christian