Hello World
Spiga

各种URL生成方式的性能对比(结论及分析)

2009-11-02 00:16 by 老赵, 18908 visits

上次我们设计了一个实验,比较三种不同URL生成方式的性能。您运行了吗?如果运行的话,有没有对结果进行一些的分析呢?现在我们就来详细观察及分析这次试验的结果,并给出我的分析。如果您有一些其他的看法,也请进行一些补充。

结论

我使用每种方式各生成1000次页面,并输出每生成100次的时候所耗费的时间。每种方式测试三次,并取平均值,结果如下:

从中我们可以得出结论,各种方式所消耗的时间大约是:

url generation benchmark

我们能够很轻易的推断出字符串拼接URL的方式最快,使用Lambda表达式生成URL最慢,但是您正确估计出它们之间的差距了吗?我没有,得到这个结果以后也让我吃了一惊,我也没想到Lambda表达式的性能会如此之差,而关键更在于……

这个性能可以令人接受吗?

上一篇文章里有朋友回复到“这点性能在实际应用程序中可以忽略不计”——这是真的吗?为了更好的进行判断,我们可以简单计算出这三种方式究竟每秒可以生成多少页面,结果如下:

pages per second

我们的测试样本基本上是模拟了链接数量处于“平均”水平的页面,并不过分——要知道真实应用中还会输出其他内容,以及做一些HTML编码之类的工作。从结果中我们可以看出,使用Lambda表达式每秒可以生成不到50次页面。自然,这是在我的性能比较一般的笔记本中“单核”的测试的结果,如果我们使用一台双核的机器来运行一个网站,它每秒大约可以生成100-150次页面。如果您的网站性能大约是50 requests/second请求的话(中到中上水平,不算太高),这大约意味着三分之一到二分之一的运算能力是交给单纯的页面生成。而对于一个性能要求较高的网站来说(100 r/q),页面生成所占用的系统资源则会更多。

因此我认为,目前使用Lambda表达式生成页面的性能并不能令人满意。要知道单纯的页面生成操作,按照普通观点来说应该是可以忽略不计的。例如直接拼接字符串的作法,单核每秒可以生成超过600次的页面,只占一点点运算资源。而使用Route的方式,每秒超过100次,也勉强可以令人接受——虽然我认为ASP.NET Routing的Route类性能还可以有更进一步的提高,有机会我会尝试一下。

性能分析

Lambda表达式生成URL的方式性能很难令人接受,但是它也有许多好处啊:静态检查,功能内聚,易于开发和测试。如果直接放弃这种做法实在令人心有不甘,那么我们又该怎么办呢?最简单的做法是减少生成链接的次数,这里可以用到MvcPatch中提供的页面片断缓存。使用这种方式可以将生成好的HTML缓存起来,在下一次请求时便可以直接输出,而不需要重新生成链接了,此时性能会比拼接字符串的方式更高。而且,页面片断缓存使用起来并不困难,对于一些不需要及时更新的内容,这个做法再合适不过了。

只不过,这个方式也只是权宜之计,治标不治本,而且总有一些内容是不适合使用片断缓存的。因此,我们还是要设法优化Lambda表达式生成URL的性能。为此,我们要分析,究竟是什么原因造成了性能问题。那么我们先来回味一下,生成“一个”链接需要经过哪些步骤:

  1. 构造一个表达式树。
  2. 对表达式树进行解析和运算,获得一个RouteValueDictionary。
  3. 使用Route配置,根据RouteValueDictionary生成URL。

为此,我们至少可以得出两个结论:

  • 需要为每个链接构造一个表达式树。
  • 使用Lambda表达式生成URL的做法,其实包含了使用Route生成URL的方式。

这意味着我们可以得到上面3个步骤所占的比例。于是乎,我们可以将ToPost等方法修改为如下形式在进行试验:

public static string ToPost(this UrlHelper helper, Blog blog, Post post)
{
    Expression<Action<BlogController>> expr = c => c.Post(blog, post);
    return helper.ToPostByRaw(blog, post);
}

经试验,修改后的做法所花时间大约是8.0正负0.3秒,由此减去ByRaw所消耗的时间后便是单纯用于构造Lambda表达式的时间。也就是说,仅仅是用来创建表达式树并很快地释放引用,也需要大约6.5秒的时间。看来在计算密集的情况下,生成表达式树的开销也是比较客观的,毕竟有闭包的存在,一个表达式树的节点总是比想象中要略多一些。此时,我们再将Lambda生成URL所用的总时间(22.6秒),减去创建表达式树的时间(6.5秒),再减去ByRoute所消耗的时间(9.4秒),可以得出第2个步骤所消耗的时间大约是6.7秒

现在,我们便大致得出了3个步骤分别消耗的时间:

  1. 构造表达式树:6.5秒,约占29%
  2. 解析和运算表达式树:6.7秒,约占30%
  3. 使用Route生成URL:9.4秒,约占41%

换句话说,除了第3个步骤属于无法回避的性能开销,其他两个步骤所消耗的时间大致相同。那么,您觉得从哪个地方开始优化比较合适呢?对我来说,则似乎有些为难。

生成Lambda表达式树其实只是创建了一些对象,它的性能其实很高,但是它还是占据了比较明显的开销。此外,解析和运算表达式树的开销和创建表达式树差不多,这意味着我们在这一块已经做的很不错了。事实上,第一次进行性能测试的时侯,我发现在解析和运算表达式树这边耗时特别长,而现在的结果已经是我进行了一些性能优化的结果(主要是避免了大量反射操作),节省了大约70%的时间(要知道原来生成1000次页面需要40-50秒)。在这个阶段,我还使用了FastLambda类库,否则这部分的时间消耗会有数量级的提升(20倍左右)。

所以我认为,性能已经很难有明显的提升了。除非我们可以避免为每个链接都生成一个表达式树,并且不对表达式树进行计算。这意味着我们需要做出根本性的,方向性地调整。

展望

在这里如何设计出又好用,又性能高的API着实让我头疼了一把。例如我想过,是否可以借鉴PostSharp的做法,修改编译后的程序集,把对UrlHelper.Action的调用修改为静态的数据操作(例如ByRoute的形式),这样就避免了表达式树的生成和解析。但是这也和PostSharp有所不同,PostSharp只要分析元数据即可,而它需要分析IL的逻辑,这要麻烦得多。或者,我们可以在运行时hook并替换掉ToPost的实现,这其实也就是前一种做法的“动态”版本——可惜的是,我不知道该如何进行。

最终我退而求其次,设计了这样一种做法。此时ToPost方法便会修改为:

// API签名
public static string Action(this UrlHelper helper, Expression template, params object[] args)

// 使用方式
private readonly static Expression<...> ToPostTemplate = (c, blog, post) => c.Post(blog, post);
public static string ToPost(this UrlHelper helper, Blog blog, Post post)
{
    return helper.Action(ToPostTemplate, blog, post);
}

由于准备了额外的“模板”,我们便省下了每次都生成表达式树的开销。在第一次调用Action方法时,会根据模板的内容生成与ByRoute静态实现差不多的操作,并根据模板对象进行缓存。这样,最后得到的性能便和ByRoute的做法没有多少区别了。只不过,这个做法需要生成独立的模板,从“美观度”上说实在比不上原来的做法。而且——这个“模板”对象的签名实在比较麻烦。

您对此有什么看法呢?我们不妨一起讨论一下如何做到“既美观,又高效”。如果您有更理想的做法也请告诉我。

相关文章

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

45 条回复

  1. AlexChen
    *.*.*.*
    链接

    AlexChen 2009-11-02 08:20:00

    好早哦...

  2. .....[未注册用户]
    *.*.*.*
    链接

    .....[未注册用户] 2009-11-02 08:52:00

    超过dudu,排名第二了...- -

  3. 老赵
    admin
    链接

    老赵 2009-11-02 09:05:00

    .....:超过dudu,排名第二了...- -


    是哎,好吧,朝特瑞李进发。

  4. CoolCode
    *.*.*.*
    链接

    CoolCode 2009-11-02 09:18:00

    实在想不到,看来“革命”还得继续

  5. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-11-02 09:25:00

    怎么生成表达式树不是编译期做的事情么?运行时应该只需要完成捕获闭包变量才对啊?我去测试下。

  6. 老赵
    admin
    链接

    老赵 2009-11-02 09:30:00

    @Ivony...
    是编译器作的事情啊,但是每次生成这么一颗树(指创建n个对象)是免不了的。

  7. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-11-02 09:34:00

    Jeffrey Zhao:
    @Ivony...
    是编译器作的事情啊,但是每次生成这么一颗树(指创建n个对象)是免不了的。




    第一次发现创建对象是件这么耗时的事情。。。。。。

  8. 老赵
    admin
    链接

    老赵 2009-11-02 09:34:00

    CoolCode:实在想不到,看来“革命”还得继续


    呵呵,是没想到。

  9. 紫色阴影
    *.*.*.*
    链接

    紫色阴影 2009-11-02 10:00:00

    知道创建表达式树慢,但是没有想到这么慢。。。

  10. 老赵
    admin
    链接

    老赵 2009-11-02 10:08:00

    @紫色阴影
    创建表达式不慢的,创建任何对象,每次创建几千个都耗时间的……

  11. 老赵
    admin
    链接

    老赵 2009-11-02 13:18:00

    怪怪呢?

  12. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-11-02 13:29:00

    刚吃饭回来的路上想到了一个方法可以避开表达式树而又有漂亮的语法,不过我还要测试一下,,,LZ耐心等待哈。。。。

  13. 老赵
    admin
    链接

    老赵 2009-11-02 13:30:00

    @Ivony...
    哈哈,好。

  14. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-11-02 13:47:00

    汗,类型推导失败,此路不通,要找其他路了。

  15. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-11-02 13:58:00

    现在实现的效果是这样的:

    ActionInfo a = A<int>( Test )( 5 );


    原理是通过将方法和参数分开来,用自己的逻辑直接构造一个对象(ActionInfo)出来,从而避免创建表达式树对象。

    现在的问题是那个<int>,很丑陋,本来的想法是根据方法的签名自动推断出参数的类型,但做不到。

  16. 老赵
    admin
    链接

    老赵 2009-11-02 14:00:00

    @Ivony...
    这……A是什么,Test是什么?

  17. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-11-02 14:05:00

    A是一个这样的辅助方法:

        public static Func<T, ActionInfo> A<T>( Action<T> methodInfo )
        {
          return arg => new ActionInfo( methodInfo.Method, arg );
        }
    


    接受一个委托作为参数,然后返回一个委托,接收作为参数的那个委托的参数。从而将方法和参数分开来,自行创建ActionInfo。

    可以认为就是把:
    return helper.Action(c => c.Post(blog, post));
    改写为
    return helper.Action( A( controller.Post )( blog, post ) );
    甚至于直接:
    return helper.Action( controller.Post )( blog, post );

    所以Test就是你要执行的Post方法。

    当然写成A( controller.Post, blog, post )也可以,不过反正类型推导不成立,要手动写泛型是很糟糕的。

  18. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-11-02 14:11:00

    好吧,我错了,我过分追求那啥了。。

    return helper.Action( controller.Post, blog, post );

    写成这样是可行的,就是比lambda难看点。

  19. 老赵
    admin
    链接

    老赵 2009-11-02 14:14:00

    @Ivony...
    这个Action方法的签名是什么啊,我怎么觉得搞不出来的……

  20. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-11-02 14:23:00

    为了避免和系统的Action委托混淆,我们把我们的方法命名为A,当然实际环境中你可以命名为Action只要不冲突。方法的签名如下:


    public ActionInfo A<T>( Action<T>, T arg );
    public ActionInfo A<T1, T2>( Action<T1, T2>, T1 arg1, T2 arg2 );
    public ActionInfo A<T1, T2, T3>( Action<T1, T2, T3>, T1 arg1, T2 arg2, T3 arg3 );

    由于事实上我们只需要用委托来传递一个方法的信息,所以调用A的时候controller实例是不必须的。


    实例吧。

    我们来看看老赵的使用方式:

    Url.Action<ArticleController>(c => c.Detail(article))

    把这种形式改一下(注意这里的A的返回值就应该是解析后的url了,与上面的A的签名不同):
    Url.A( ((ArticleController) null).Detail, article )

    当然,如果你喜欢,也可以
    Url.A( C<ArticleController>().Detail, article )

    方法C的实现为:
    public T C<T>()
    {
    return default( T );
    }

    优势,彻底的避免了表达式树,现在只不过是创建一个委托对象而已。

  21. 老赵
    admin
    链接

    老赵 2009-11-02 14:27:00

    @Ivony...
    感觉还是不好看……更关键的是……你拿到一个委托以后,又咋个进行分析?是不是要挖掘委托对象内部的私有字段了啊?

  22. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-11-02 14:29:00

    Jeffrey Zhao:
    @Ivony...
    感觉还是不好看……更关键的是……你拿到一个委托以后,又咋个进行分析?是不是要挖掘委托对象内部的私有字段了啊?



    委托有个Method属性。。。。

  23. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-11-02 14:34:00

    恼怒,运行时报错。null对象的方法不能创建委托,即使不去执行这个委托。

    Controller享元吧,单例吧,啊啊啊啊啊。。。。。

  24. 老赵
    admin
    链接

    老赵 2009-11-02 15:03:00

    @Ivony...
    那么C<HomeController>总归可以吧。

  25. 梦想永存
    *.*.*.*
    链接

    梦想永存 2009-11-02 15:41:00

    SpaceBuilder是这样处理的:
    1、优先使用RouteName获取Url,其次通过Action;
    2、禁用匿名对象传递数据,只能通过RouteValueDictionary;
    3、对url pattern进行缓存(主要优化途径);
    代码如下(由于最多限4000字,因此把SPBUrlHelper的Action实际实现省省略了),抛砖引玉,共同探讨最佳的解决办法


    public class SPBUrlHelper
    {
        private static readonly string ControllerKey = "controller";
        private static readonly string ActionKey = "action";
    
    
        /// <summary>
        /// 通过routeName获取url
        /// </summary>
        /// <param name="routeName">routeName</param>
        /// <returns>url</returns>
        public static string RouteUrl(string routeName)
        {
            return RouteUrl(routeName, null);
        }
    
        /// <summary>
        /// 通过routeName获取url
        /// </summary>
        /// <param name="routeName">routeName</param>
        /// <param name="routeValueDictionary">路由数据</param>
        /// <returns>url</returns>
        public static string RouteUrl(string routeName, RouteValueDictionary routeValueDictionary)
        {
            string cacheKey = string.Format("RouteUrl::{0}", routeName);
    
            RouteValueDictionary routeParameters = new RouteValueDictionary();
            string[] values = null;
            if (routeValueDictionary != null)
            {
                if (routeValueDictionary.ContainsKey(ControllerKey))
                {
                    routeParameters[ControllerKey] = routeValueDictionary[ControllerKey];
                    routeValueDictionary.Remove(ControllerKey);
                    cacheKey += "-" + ControllerKey + ":" + routeParameters[ControllerKey];
                }
                if (routeValueDictionary.ContainsKey(ActionKey))
                {
                    routeParameters[ActionKey] = routeValueDictionary[ActionKey];
                    routeValueDictionary.Remove(ActionKey);
                    cacheKey += "-" + ActionKey + ":" + routeParameters[ActionKey];
                }
    
                values = new string[routeValueDictionary.Count];
                int index = 0;
                foreach (KeyValuePair<string, object> pair in routeValueDictionary)
                {
                    cacheKey += "-" + pair.Key + ":{" + index + "}";
    
                    if (pair.Value == null)
                        values[index] = string.Empty;
                    else
                        values[index] = pair.Value.ToString();
    
                    routeParameters[pair.Key] = "{" + index + "}";
                    index++;
                }
            }
    
            string url = WebCache.Get(cacheKey) as string;
            if (url == null)
            {
                url = GenerateRouteUrl(routeName, routeParameters);
    
                //替换UrlEncode编码
                url = url.Replace("%7B", "{").Replace("%7D", "}");
    
                WebCache.Max(cacheKey, url);
            }
    
            if (values != null)
                return string.Format(url, values);
            else
                return url;
        }
    
        /// <summary>
        /// 通过routeName生成url
        /// </summary>
        /// <param name="routeName">routeName</param>
        /// <param name="routeValueDictionary">路由数据</param>
        /// <returns>url</returns>
        private static string GenerateRouteUrl(string routeName, RouteValueDictionary routeValueDictionary)
        {
            RouteBase route = RouteTable.Routes[routeName];
            if (route == null)
                return string.Empty;
    
            RequestContext requestContext = new RequestContext(new HttpContextWrapper(System.Web.HttpContext.Current), new RouteData());
            VirtualPathData pathData = route.GetVirtualPath(requestContext, routeValueDictionary);
            if (pathData == null)
                return string.Empty;
    
    
            string absolutePath = pathData.VirtualPath;
    
            if (!absolutePath.StartsWith("/"))
                absolutePath = "/" + absolutePath;
    
            string appPath = requestContext.HttpContext.Request.ApplicationPath;
    
            if (!absolutePath.StartsWith(appPath))
                absolutePath = appPath + absolutePath;
    
            return absolutePath;
        }
    
        /// <summary>
        /// 通过Action/Controller获取url
        /// </summary>
        public static string Action(string actionName, string controllerName)
        
    ......
    
    }
    

  26. 老赵
    admin
    链接

    老赵 2009-11-02 15:43:00

    @梦想永存
    还没仔细看,不过这已经没有强类型优势了呢。

  27. 梦想永存
    *.*.*.*
    链接

    梦想永存 2009-11-02 15:53:00

    @Jeffrey Zhao
    Url Route 规则本身就没有强类型可言,需要强类型可以做自己的辅助类
    SPBUrlHelper可以看做是UrlHelper的替代

  28. 老赵
    admin
    链接

    老赵 2009-11-02 15:58:00

    @梦想永存
    但我的做法就是强类型的啊:
    Url.Action<BlogController>(c => c.Post(blog, post));
    这返回一个面向BlogController.Post这个Action的URL。

  29. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-11-02 16:03:00

    Jeffrey Zhao:
    @Ivony...
    那么C<HomeController>总归可以吧。



    如果你的controller是struct,或者提供无参构造函数或者有办法只通过类型创造实例,就是可行的。

    不过Controller做成享元或单例没什么问题吧,或者提供无参构造函数,都能解决问题。但总的来说,不好看。为了实现创建一个不用的实例也很无谓。。。。。

  30. 老赵
    admin
    链接

    老赵 2009-11-02 16:41:00

    @Ivony...
    不过你这倒提示我了……

    public static class UrlHelperExtensions
    { 
        public static ActionOf<TController> Of<TController>(this UrlHelper helper) where TController : new()
        {
            return new ActionOf<TController>(helper);
        }
    }
    
    public class ActionOf<TController> where TController : new()
    {
        public ActionOf(UrlHelper helper)
        {
        }
    
        public string Action<T1, T2>(Func<TController, Action<T1, T2>> action, T1 arg1, T2 arg2)
        {
            return null;
        }
    }
    
    使用时:
    helper.Of<HomeController>().Action(c => c.Post, blog, post);
    
    会多创建个委托等小对象,不知道会不会比表达式树构建好多少,但应该比Eval要强。
    // 想了想,仔细打理的话,也可以少创建很多对象,呵呵。

  31. 老赵
    admin
    链接

    老赵 2009-11-02 16:42:00

    那个,我无责任猜测,表达式树性能较差的原因之一可能是卫语句太多,检查频繁了点,创建委托可能会快一些。

  32. 老赵
    admin
    链接

    老赵 2009-11-02 16:50:00

    @Ivony...
    写了这个之后我才明白为什么你说null没法使用了……
    这个可能不太好啊,因为创建一个Controller还是挺麻烦的,因为里面可能会有一些逻辑,创建一个的话会不会有影响……
    或者就是:
    Action<T1, T2>(Expression<Func<TController, Action<T1, T2>>> action, T1 arg1, T2 arg2)
    但这又要创建表达式树了……

  33. 老赵
    admin
    链接

    老赵 2009-11-02 17:59:00

    @Ivony...
    不过这样还有个问题,就是……原本可以通过It.IsAny<int>来“忽略”一个参数,现在怎么办……
    不过其实这也不是大问题,那么这个方法应该可行吧,我想。

  34. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-11-02 18:29:00

    Jeffrey Zhao:
    @Ivony...
    不过这样还有个问题,就是……原本可以通过It.IsAny<int>来“忽略”一个参数,现在怎么办……
    不过其实这也不是大问题,那么这个方法应该可行吧,我想。




    你可以做一个忽略所有参数的重载,o(∩_∩)o 。。。。
    会不会与无参的冲突(⊙_⊙)?

    选择性忽略参数的话,的确是很麻烦。关键还是怎么避免明确的写泛型类型。


    顺便提一句,Q拼很不错的说,比如说输入这种火星文:╰( ̄▽ ̄)╮。

  35. 老赵
    admin
    链接

    老赵 2009-11-02 18:38:00

    @Ivony...
    我想了想,忽略问题还是从Binder上下手吧,比如实现个IIgnoreRouteBinder作为标记等等,呵呵。

    // 我觉得这个拼音输入法很强大…… http://pinyin.sogou.com/cloud/

  36. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-11-02 18:50:00

    Jeffrey Zhao:
    @Ivony...
    我想了想,忽略问题还是从Binder上下手吧,比如实现个IIgnoreRouteBinder作为标记等等,呵呵。

    // 我觉得这个拼音输入法很强大…… http://pinyin.sogou.com/cloud/




    嗯,看起来很强大,但拼音输入法这玩意儿,准确率自是不说,增值功能也很重要,Q拼在这方面就做的很好(腾讯在把别人的东西拿来再扩展的工作一直做的很好),尽管在准确率方面好像还差点。


    顺便提一句,委托的这种方式,因为类型是从参数推导的而不是从方法推导的。所以个别情况下会与实际的方法调用的重载决策不一致,这是比lambda糟糕的地方。

    比如说对于方法Test( long )而言,Action( Test, 5 )就是不成立的。因为编译器从5推导出委托类型Action<int>,而Test( long )不能自动转换为Action<int>。但实际上Test( 5 )绝对是成立的。lambda表达式也可以成立。。。。可能还有别的问题吧,一时没想到,但总的来说问题都不大。

  37. 老赵
    admin
    链接

    老赵 2009-11-02 18:55:00

    @Ivony...
    嗯,这点应该不会造成问题,这也和ASP.NET MVC的性质有关,一般不会有那样的Action重载,如果有了就显示Cast吧,问题也不大。
    回头我比较一下这种做法和Lambda表达式构造时的性能。

  38. 老赵
    admin
    链接

    老赵 2009-11-02 18:58:00

    @Ivony...
    对了,其实我可以用Emit来生成TController的子类啊。
    用Emit的好处就是不会受C#编译器的限制,可以不调用基类的任何构造函数,那么就不会影响到Controller的逻辑了。
    现在的要求一下子就变成了“非sealed”,这个我想实在不应该是问题了。

  39. 老赵
    admin
    链接

    老赵 2009-11-02 19:34:00

    @Ivony...
    简单试验了一下:

    int iteration = 1000 * 1000;
    
    CodeTimer.Time("Lambda Expression", iteration,
        () => helper.Action<HomeController>(c => c.Post(blog, post)));
    
    CodeTimer.Time("Fluent Interface", iteration,
        () => helper.Of<HomeController>().Action(c => c.Post, blog, post));
    结果是:
    Lambda Expression
            Time Elapsed:   9,353ms
            CPU Cycles:     20,339,339,141
            Gen 0:          373
            Gen 1:          0
            Gen 2:          0
    
    Fluent Interface
            Time Elapsed:   3,041ms
            CPU Cycles:     6,614,088,228
            Gen 0:          97
            Gen 1:          0
            Gen 2:          0
    省了2/3时间。

  40. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-11-02 20:12:00

    32%,呃,,,,我真没想到那棵树那么慢。。。。。

    还真是与对象的数量成正比。。。。。373/97,话说没理解错这是GC统计的零代对象数吧⊙﹏⊙‖∣。

    如果参数多了,效果应该会更明显。。。。



    话说你都把Emit大神请出来了,我们能干的似乎就不是这么点儿了。只要方法是虚的,<( ̄︶ ̄)>。。。。

  41. 老赵
    admin
    链接

    老赵 2009-11-02 21:04:00

    @Ivony...
    Gen 0是第0代的收集次数。
    不过虚方法要求高了点,能不Emit就不Emit吧,现在我说的Emit也就几行代码,而且能Expression Tree的还是Expression Tree算了……

  42. 装配脑袋
    *.*.*.*
    链接

    装配脑袋 2009-11-03 11:46:00

    你们这好折腾啊……

  43. 老赵
    admin
    链接

    老赵 2009-11-12 14:26:00

    现在是了一下,有大约40%的性能提升……

  44. 沐枫
    *.*.*.*
    链接

    沐枫 2009-11-19 12:15:00

    其实,我不明白为什么C#不在编译期间生成表达式树。也就是说,C#的优化工作严重不足啊。

  45. 老赵
    admin
    链接

    老赵 2009-11-19 12:23:00

    @沐枫
    因为表达式树里是包含动态内容的

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我