2

動的コンパイルと標準コンパイルを比較しようとしていました。また、動的コンパイルはかなり遅いようです。私のベンチマークによると、1400% 遅くなります。

動的にコンパイルされたコードを呼び出すデリゲートを生成するメソッドを次に示します。

public static Function Eval(string sCSCode)
{
    CSharpCodeProvider c = new CSharpCodeProvider();
    ICodeCompiler icc = c.CreateCompiler();
    CompilerParameters cp = new CompilerParameters();

    cp.ReferencedAssemblies.Add("system.dll");

    cp.CompilerOptions = "/optimize";
    //cp.GenerateInMemory = true;
    StringBuilder sb = new StringBuilder("");

    sb.Append("using System;\n");

    sb.Append("namespace CSCodeEvaler{ \n");
    sb.Append("public class CSCodeEvaler{ \n");
    sb.Append("public double sin(double x) { return Math.Sin(x); }");
    sb.Append("public double cos(double x) { return Math.Cos(x); }");
    sb.Append("public double sqrt(double x) { return Math.Sqrt(x); }");
    sb.Append("public const double PI = Math.PI;");
    sb.Append("public double Calculate(double x, double y){\n");
    sb.Append("return " + sCSCode + "; \n");
    sb.Append("} \n");
    sb.Append("} \n");
    sb.Append("}\n");

    CompilerResults cr = icc.CompileAssemblyFromSource(cp, sb.ToString());
    if (cr.Errors.Count > 0)
    {
        throw new Exception("ERROR: " + cr.Errors[0].ErrorText);
        return null;
    }

    System.Reflection.Assembly a = cr.CompiledAssembly;
    object o = a.CreateInstance("CSCodeEvaler.CSCodeEvaler");
    Type t = o.GetType();
    MethodInfo mi = t.GetMethod("Calculate");

    return delegate(double x, double y)
    {
        object[] oParams = new object[2];
        oParams[0] = x;
        oParams[1] = y;
        object s = mi.Invoke(o, oParams);
        return (double)s;
    };
}

パフォーマンスをテストするために作成したコードは次のとおりです。

private double f1(double x, double y)
{
    return Math.Sin(x) + Math.Cos(y);
}

private void button1_Click(object sender, EventArgs e)
{
    double sum = 0;
    long t1 = DateTime.Now.Ticks;                
    for (double x = 0; x <= 500; x += 0.1)
    {
        for (double y = 0; y <= 500; y += 0.1)
        {
            sum += f1(x, y);
        }
    }
    t1 = DateTime.Now.Ticks - t1;

    sum = 0;
    var f2 = FunctionConstructor.Eval("Math.Sin(x) + Math.Cos(y)");
    long t2 = DateTime.Now.Ticks;            
    for (double x = 0; x <= 500; x += 0.1)
    {
        for (double y = 0; y <= 500; y += 0.1)
        {
            sum += f2(x, y);
        }
    }
    t2 = DateTime.Now.Ticks - t2;
    long dt = t2 - t1;
    double q = dt / (double)t1;
    MessageBox.Show("WITHOUT dynamic compilation: " + t1 + "\n" + "WITH dynamic compilation: " + t2 + "\n" + "Calculation without dynamic compilation was " + dt + " ticks (" + (q-1)*100 + "%) slower");
}

私のテストによると、動的コンパイルは非常に遅くなります。これは何とか解決できますか?おそらく問題は、ベンチマークの方法にあるのでしょうか?

4

1 に答える 1

4

ほとんど時間がかからないメソッドを呼び出しています。これにより、メソッドを呼び出す際のオーバーヘッドがコードの速度の主要な要因になります。動的な呼び出しとメソッドをインライン化できないことの両方が時間のキラーです。Calculate メソッド内で内側のループを移動すると、違いを確認できます。例えば:

    sb.Append("public double Calculate(double x, double y){\n");
    sb.Append("double sum = 0; for (; y <= 500; y += 0.1) {\n");
    sb.Append("sum += " + sCSCode + ";\n");
    sb.Append("} return sum;\n");

    long t2 = DateTime.Now.Ticks;
    for (double x = 0; x <= 500; x += 0.1) {
        sum += f2(x, 0);
    }
    t2 = DateTime.Now.Ticks - t2;

そして、あなたは違いが消えるのを見るでしょう. これを簡単に修正する方法はありません。問題は、コードの動的呼び出しが遅いということだけではなく、ジッター オプティマイザーが最適なマシン コードを生成できるようにすることで、非常に高速になることです。もちろん、これは機能であり、バグではありません。

于 2013-01-15T13:59:58.393 に答える