在C#-4.0應用程序中,我有一個長度相同的強類型IList字典 - 一個動態強類型的基於列的表。 我希望用戶提供一個或多個(python-)表達式,這些表達式基於將在所有行上聚合的可用列。在靜態上下文將是: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這個運行在0.270秒我的筆記本電腦(Win7的X64)。用具有兩個int參數的委託來替換表達式,它需要0.580秒,對於非類型委託1.19秒。 與
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秒(和5.1秒,Func<object, object, object>
) - 因子4至5.5。這是我在做什麼的預期開銷?有什麼可以改進的?
如果我有很多列,上面選擇的方法將不再是足夠的。一種解決方案可能是爲每個表達式確定所需的列並僅使用那些作爲參數。我嘗試過的其他解決方案是使用ScriptScope並動態解析列。爲此,我定義了一個RowIterator,它有一個活動行的RowIndex和每個列的屬性。
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可以從IDynamicMetaObjectProvider,我預計到C#的動態被實現來創建 - 但在運行時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只會被調用爲未定義的屬性。
可能爲了一個合理的性能,我需要爲我的RowIterator實現IDynamicMetaObjectProvider以使用動態CallSites,但是找不到適合我的例子。在我的實驗,我不知道如何處理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的呢。對於如何改進設計和/或性能的建議,我將不勝感激。
而不是使用IDMOP作爲ScriptScope的成員,我會將RowIterator注入到ScriptScope中,或者甚至作爲參數傳遞給範圍外的委託。 – 2011-03-22 01:32:11
我沒有使用IDMOP作爲ScriptScope的_member_,而是作爲「上下文」本身,即在我的表達式中,我想輸入「a/b」而不是「row.a/row.b」。這怎麼可以用你所謂的注入來完成呢? – Christian 2011-03-22 10:50:03
@迪諾:我認爲使用RowIterator作爲委託的參數是一個關於性能的好主意。我如何將用戶表達式「a/b」(或其他)替換爲「row.a/row.b」,我將實際編譯它? – Christian 2011-04-04 16:40:53