Foxtable(狐表)用户栏目专家坐堂 → 补充效率提升的另一种情况(有修正,从390秒提升到2秒,从2秒提升到不到1秒)


  共有19153人关注过本帖树形打印复制链接

主题:补充效率提升的另一种情况(有修正,从390秒提升到2秒,从2秒提升到不到1秒)

帅哥哟,离线,有人找我吗?
狐狸爸爸
  1楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:管理员 帖子:47497 积分:251403 威望:0 精华:91 注册:2008/6/17 17:14:00
补充效率提升的另一种情况(有修正,从390秒提升到2秒,从2秒提升到不到1秒)  发帖心情 Post By:2010/12/9 10:51:00 [只看该作者]

在C版的提醒下,补充一个效率提升的问题。

 

测试文件,每次测试都重新打开文件,不要保存:

 

 下载信息  [文件大小:   下载次数: ]
图片点击可在新窗口打开查看点击浏览该文件:代码优化.rar

 

表结构如下图:

 


图片点击可在新窗口打开查看此主题相关图片如下:spximage.jpg
图片点击可在新窗口打开查看

 

希望在累计支出列计算出每个项目截止到当日为止的累计支出,有了上一次的经验,我想当然写出了这样这样的代码:

 

Dim s As Date = Date.Now()
Dim dic As New Dictionary(of DataRow, String)
Dim drs  As List(of DataRow)
For Each dr1 As DataRow In DataTables("表A").DataRows
    Dim Filter As String
    Filter = "项目 = '" & dr1("项目") & "' And 日期 <= #" & dr1("日期") & "#"
    dic.Add(dr1, DataTables("表A").Compute("Sum(支出)",Filter))
Next
For Each dr As DataRow In dic.Keys
    dr("累计支出") = dic(dr)
Next
Output.show(“执行时间:" & (Date.Now -s).TotalSeconds)

 

在一万行,累计支出列无内容的情况下,花了390秒。

这样的结果不仅让人难以接受,也出乎意料,为什么之前的招数完全失效了呢?

 

仔细分析一下可以看出,和之前的例子不同,上面的查询条件不全是“等于”比较,其中日期的比较是“小于等于”比较。
实际上在查询中,“等于”比较是最高效的,其他任何形式的比较效率都要低很多,也许真的就是这个“小于等于”比较影响了代码的执行效率。

但是“小于等于”比较是必须,不可能不用,那么我们唯一能着手的就是缩小“小于等于”比较的范围,改良后的代码为:

 

Dim s As Date = Date.Now()
Dim dic As New Dictionary(of DataRow, String)
Dim drs  As List(of DataRow)
For Each dr1 As DataRow In DataTables("表A").DataRows
    Dim dv As Date = dr1("日期")
    Dim sm As Double = 0
    drs = DataTables("表A").Select("项目 = '" & dr1("项目") & "'", "日期")
    For Each dr2 As DataRow In drs
        If dr2("日期") <= dv Then
            sm = sm + dr2("支出")
        Else
            Exit For
        End If
    Next
    dic.Add(dr1, sm)
Next
For Each dr As DataRow In dic.Keys
    dr("累计支出") = dic(dr)
Next
Output.show("执行时间" & (Date.Now -s).TotalSeconds)

 

同样在一万行,累计支出列无内容的情况下,上述代码花了2秒,看来之前的判断完全正确。

代码的原理是,先筛选出相同项目的行,再比较这些行的日期,从而大大缩小了日期“小于等于”比较的范围,如果你的项目是100个,那么比较范围就缩小了100倍,从而大大提升了代码的运行效率。

 

既然比较是影响效率的关键,于是我彻底改写代码,完全不用比较,最新的代码为:

 

Dim s As Date = Date.Now()
Dim drs As List(of DataRow) = DataTables("表A").Select("", "项目, 日期")
drs(0)("累计支出") = drs(0)("支出")
For i As Integer = 1 To drs.Count - 1
    If drs(i)("项目") = drs(i - 1)("项目") Then
        drs(i)("累计支出") = drs(i-1)("累计支出") + drs(i)("支出")
    Else
        drs(i)("累计支出") = drs(i)("支出")
    End If
Next
Output.show("执行时间:" & (Date.Now -s).TotalSeconds)

 

经过测试,处理1万行数据只用了0.9秒,又提升了一倍有余,而且代码更为简洁易懂。

 

代码的效率提升是没有定式的,实际编码的过程中只有用排除法,找出可能影响效率的因素,然后有针对性地改进。

[此贴子已经被作者于2010-12-9 14:09:28编辑过]

[本帖被加为精华]
 回到顶部
帅哥哟,离线,有人找我吗?
foxor
  2楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:二尾狐 帖子:517 积分:4199 威望:0 精华:0 注册:2009/10/8 16:43:00
  发帖心情 Post By:2010/12/9 10:55:00 [只看该作者]

好用,顶!


 回到顶部
帅哥哟,离线,有人找我吗?
czy
  3楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信 一级勋章 三级勋章 二级勋章
等级:超级版主 帖子:6318 积分:33957 威望:0 精华:10 注册:2008/8/31 20:56:00
  发帖心情 Post By:2010/12/9 10:59:00 [只看该作者]

真给力,顶!

 回到顶部
帅哥哟,离线,有人找我吗?
czy
  4楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信 一级勋章 三级勋章 二级勋章
等级:超级版主 帖子:6318 积分:33957 威望:0 精华:10 注册:2008/8/31 20:56:00
  发帖心情 Post By:2010/12/9 11:04:00 [只看该作者]

这个问题很有代表性,也是佐证代码效率最好的范例,建议将示例加到模板中,帮助配以说明,让大家一起受益。

 回到顶部
帅哥哟,离线,有人找我吗?
狐狸爸爸
  5楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:管理员 帖子:47497 积分:251403 威望:0 精华:91 注册:2008/6/17 17:14:00
  发帖心情 Post By:2010/12/9 11:38:00 [只看该作者]

呵呵,帮助加进去了,示例文件就没有必要了吧。

 回到顶部
帅哥哟,离线,有人找我吗?
mr725
  6楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信 一级勋章
等级:MVP荣誉狐 帖子:5154 积分:31434 威望:0 精华:8 注册:2008/9/8 12:27:00
  发帖心情 Post By:2010/12/9 12:31:00 [只看该作者]

前一个代码执行时间:668.2183086

后一个代码执行时间:    3.9686992

 

比老大的慢了一倍了,电脑配置稍差些啊, 呵呵    。。。  但效果是相当明显的~~~

 

 


 回到顶部
帅哥哟,离线,有人找我吗?
狐狸爸爸
  7楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:管理员 帖子:47497 积分:251403 威望:0 精华:91 注册:2008/6/17 17:14:00
  发帖心情 Post By:2010/12/9 13:47:00 [只看该作者]

其实最优化的代码应该是,现在不到一秒了:

 

Dim s As Date = Date.Now()
Dim drs As List(of DataRow) = DataTables("表A").Select("", "项目, 日期")
drs(0)("累计支出") = drs(0)("支出")
For i As Integer = 1 To drs.Count - 1
    If drs(i)("项目") = drs(i - 1)("项目") Then
        drs(i)("累计支出") = drs(i-1)("累计支出") + drs(i)("支出")
    Else
        drs(i)("累计支出") = drs(i)("支出")
    End If
Next
Output.show("执行时间:" & (Date.Now -s).TotalSeconds)


 回到顶部
帅哥哟,离线,有人找我吗?
migold
  8楼 | QQ | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信
等级:一尾狐 帖子:447 积分:4572 威望:0 精华:0 注册:2009/1/11 11:00:00
  发帖心情 Post By:2010/12/9 13:55:00 [只看该作者]

太牛了!慢慢消化!建议加入帮助中放入示例

 回到顶部
帅哥哟,离线,有人找我吗?
kylin
  9楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信 F6
等级:狐精 帖子:3036 积分:19223 威望:0 精华:2 注册:2008/9/1 7:50:00
  发帖心情 Post By:2010/12/9 14:15:00 [只看该作者]

以下是引用czy在2010-12-9 11:04:00的发言:
这个问题很有代表性,也是佐证代码效率最好的范例,建议将示例加到模板中,帮助配以说明,让大家一起受益。

支持,连同“令人目瞪口呆的效率差别(有修正)”一起编入示例模版中


 回到顶部
帅哥哟,离线,有人找我吗?
mr725
  10楼 | 信息 | 搜索 | 邮箱 | 主页 | UC


加好友 发短信 一级勋章
等级:MVP荣誉狐 帖子:5154 积分:31434 威望:0 精华:8 注册:2008/9/8 12:27:00
  发帖心情 Post By:2010/12/9 14:16:00 [只看该作者]

以下是引用狐狸爸爸在2010-12-9 13:47:00的发言:

其实最优化的代码应该是,现在不到一秒了:

 

Dim s As Date = Date.Now()
Dim drs As List(of DataRow) = DataTables("表A").Select("", "项目, 日期")
drs(0)("累计支出") = drs(0)("支出")
For i As Integer = 1 To drs.Count - 1
    If drs(i)("项目") = drs(i - 1)("项目") Then
        drs(i)("累计支出") = drs(i-1)("累计支出") + drs(i)("支出")
    Else
        drs(i)("累计支出") = drs(i)("支出")
    End If
Next
Output.show("执行时间:" & (Date.Now -s).TotalSeconds)

这个更准确: 看图: 累计支出2 是上面最新代码计算的结果~~  


图片点击可在新窗口打开查看此主题相关图片如下:未命名.jpg
图片点击可在新窗口打开查看

真是更快、更好、更强啦~~

[此贴子已经被作者于2010-12-9 14:23:57编辑过]

 回到顶部
总数 26 1 2 3 下一页