使用CSharpCodeProvider进行慢速动态编译

时间:2013-01-15 13:24:18

标签: c# .net performance csharpcodeprovider

我试图将动态编译与标准编译进行比较。动态编译似乎要慢得多。根据我的基准测试,速度减慢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");
}

根据我的测试,动态编译速度慢得多。这可以以某种方式解决吗?也许问题在于我的基准测试方式?

1 个答案:

答案 0 :(得分: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;

你会发现差异消失了。对此没有简单的解决方法。问题不在于动态调用代码的速度慢,而是允许抖动优化器生成最佳机器代码使其速度极快。这当然是一个功能,而不是一个错误。