新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论.NET,C#,ASP,VB技术
    [返回] 中文XML论坛 - 专业的XML技术讨论区计算机技术与应用『 Dot NET,C#,ASP,VB 』 → 使用 C#: 打开包装 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 3029 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: 使用 C#: 打开包装 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     admin 帅哥哟,离线,有人找我吗?
      
      
      
      威望:9
      头衔:W3China站长
      等级:计算机硕士学位(管理员)
      文章:5255
      积分:18406
      门派:W3CHINA.ORG
      注册:2003/10/5

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给admin发送一个短消息 把admin加入好友 查看admin的个人资料 搜索admin在『 Dot NET,C#,ASP,VB 』的所有贴子 点击这里发送电邮给admin  访问admin的主页 引用回复这个贴子 回复这个贴子 查看admin的博客楼主
    发贴心情 使用 C#: 打开包装


    发信人: Jobs (温少), 信区: DotNET        
    标  题: 使用 C#: 打开包装
    发信站: BBS 水木清华站 (Thu May 17 21:15:14 2001)


    MSDN 在线教学——使用 C#: 打开包装! 快点!  

    代码下载:
    http://sunrise.szu.edu.cn:8096/Article%20Source%20Code/csharp03152001.exe

    上个月,我们介绍了装箱和取消装箱的方法,以及什么时候会用到它们。这个月,
    我们将研究装箱对性能的影响,以及我们应当怎样将这种影响减少到最小。

    装箱和性能
    由于进行了装箱,所以 C# 中的对象模型非常简单明了。但是采用经过装箱的数值
    类型会导致性能的降低。在大多数情况下,对象模型的简化比性能的降低更为重要
    。对于一般的软件而言的确是这样。节省开发和维护软件的时间是最需要进行优化
    的地方,同时正是这些优化措施能够最大程度地改善程序的性能。

    最佳的解决方案可能是使用一个通用的 ArrayList。这样我们可以声明一个  
    Arraylist<int>,该对象就可以不经过装箱直接存储 int 值。不幸的是,在 C#  
    的第一版本中没有通用数据类型(尽管人们正研究在后续版本中提供这种功能),
    所以这种解决方案于事无补。

    有时候可以用别的没有装箱损耗的对象来代替。除了利用 ArrayList 来保存  
    int 值以外,还可以(需要使用较少的额外代码来添加新的元素)使用 int[],或
    者还可以编写一个 ArrayListInt 来封装这些额外代码。但是,这并不是最简便的
    解决方案。

    还有很多其他的选择。为了更好地对它们进行说明需要举一个具体的实例。

    字数统计
    这是我以前编写的一个 C# 程序,它可以将一个测试文件分解成一个个的单词,再
    统计每个单词出现的次数。我写这个程序的目的是为了了解常用表达式类在框架中
    的工作方式,并将用 C# 编写的程序与用 Perl 编写的类似程序进行比较。

    如果您还没有接触过 Perl 的话, 这是一种专门用于文本处理的编程语言,所以特
    别适用于这样的任务。

    最容易想到的、统计单词出现次数的方法是将单词作为关键字,利用“散列表”存
    储单词出现的次数。代码的内循环如下所示:

       while ((line = stream.ReadLine()) != null)
       {
          foreach (string word in regexSplit.Split(line.ToLower()))
          {
             int count = 0;
             object value = wordTable[word];
             if (value != null)
                count = (int) value;
             wordTable[word] = count + 1;
          }
       }

    在内循环中, 我们从“散列表”中获得某个关键字的当前值。如果该值不是空值,
    就将它转换成一个 int。随后重新将正确的值存储到“散列表”中。

    编写这段代码很容易,但是如果某个单词已经存在的话,它将会造成相当大的性能
    损耗。仅仅为了累加某个值,我们不仅要对它进行解装箱,还需要为每个字符串计
    算两次散列码。

    尽管会产生这些损耗,但是这段程序的性能仍然是相当不错的。为了获得一些具体
    的性能指标,我需要使用一些比较合适的文本文件。我首先下载了由 David Weber
    撰写的《On Basilisk Station》(英文),其中包括 160,000 个单词。对于一
    次比较全面的测试而言这个文件显得有些小。用 Perl 编写的程序可以在不到一秒
    钟的时间里将它处理完毕。随后我从 Project Gutenberg(英文)下载了《战争与
    和平》(英文),其中包括大约 600,000 个单词。这个要稍好一些。

    用 Perl 编写的程序花费了大约 4 秒钟,就完成了对《战争与和平》的字数统计
    。而使用了被装箱的 int 的 C# 程序花了大约 10 秒钟。每秒钟处理 60,000 个
    单词已经很不错了,但是我对到底可以将它的速度提高到多少非常感兴趣。

    确定基准
    在我们开始尝试不同的方法以前,我们需要知道这个 C# 程序的基准时间是多少。
    换句话说,读取文件的所有行,并将它们分解成一个个的单词而不进行字数统计,
    这个过程需要多长时间。这样做非常重要,因为只有这样我们才能只比较源代码中
    的统计部分所用的时间。

    如果我们编写实现上述任务的代码,我们发现在大约 7 秒钟的时间内就完成了除
    了字数统计以外的所有任务。这意味着字数统计部分花费了几秒钟。

    让程序运行得更快
    我们的目标是取消整型数据的装箱和取消装箱过程。换句话说,我们希望能够不经
    过对整型数据进行解装箱以及随后再对其重新装箱的过程直接累加“散列表”中的
    值。要做到这一点,一种显而易见的方法是使用一种引用类型,而不是数值类型。
    我们可以将整型统计值装箱在类中。这个类非常简单:  

    class IntHolderClass
    {
       int count;
       public IntHolderClass()
       {
          count = 1;
       }
       public int Count
       {
          get
          {
             return(count);
          }
          set
          {
             count = value;
          }
       }
       public override string ToString()
       {
          return(count.ToString());
       }
    }

    当为这个类创建一个新的实例时,统计值设为 1。统计值可以通过 Count 属性递
    增。主循环被修改成如下形式:

       foreach (string word in regexSplit.Split(line.ToLower()))
       {
          wordCount++;
          IntHolderClass value = (IntHolderClass) wordTable[word];
          if (value == null)
          {
             wordTable[word] = new IntHolderClass();
          }
          else
             value.Count++;
          }
       }

    如果单词不存在,就创建一个新的装箱类实例,并将其放入“散列表”中。如果单
    词已存在,就累加其计数。

    当我们运行此版本时,我们发现所花时间不到一秒钟。这大约是采用了装箱的程序
    所花时间的 30%。刚开始我对这个结果有些惊讶,因为表面上看起使用类应当造成
    更多的损耗。但是经过仔细思考,我们知道,很显然,尽管创建一个被装箱的 int
    与创建一个装箱类所造成的损耗是相同的,但是对于被装箱的 int,我们为每个
    单词(总共有 600,000个)而不是为每个唯一的单词(大约有 19,000 个)创建了
    一个装箱。

    这种技术所带来的性能改善的程度取决于我们在数值保存在集合类中时所执行的操
    作的次数。如果我们只是找到该对象并将其取出,那么不会带来任何性能上的改善
    。例如,如果我用一个 ArrayList 来保存用于后续处理的整型值,那么采用这种
    技术对于性能将没有任何帮助。

    当您编写代码时,最好使用尽可能简便的方法。在实现这种方法以后,如果需要更
    快的速度,则可以再考虑其他的方法来提高运行速度。

    另一种技术
    还有一种类似的方法可获得同样的结果。数值类型可以作为接口,但是因为接口都
    是引用类型,所以您只能使用一个引用某个被装箱的数值类型的接口。我们可以定
    义一个具有 Increment() 成员的接口,并使用该数值类型实现它。利用这种方法
    ,我们可以直接通过被装箱的数值类型获得接口,然后再调用 Increment() 函数
    ,这一切都不需要取消装箱。接口和数值类型如下:

    interface IIncrement
    {
       void Increment();
    }

    struct IntHolderStruct: IIncrement
    {
       int value;

       public IntHolderStruct(int value)
       {
          this.value = value;
       }
       public int Value
       {
          get
          {
             return(value);
          }
       }

       public void Increment()
       {
          value++;
       }

       public override string ToString()
       {
          return(value.ToString());
       }
    }

    我很希望宣布这是由我自己独立完成的,但实际上它是我在最近的一次会议上遇到
    的一个与会者编写的。这种实现的主循环的工作方式与采用装箱类的那段程序非常
    相似。这个程序运行时所花费的时间大约是采用被装箱的 int 的程序所花时间的
    32%。换句话说,它只比采用装箱类的程序稍慢一点。

    全部结果
    以下内容是对全部结果的总结。

    表 1《On Basilisk Station》的结果


    实现方式 所用时间 相对于被装箱 Int 的比例  
    被装箱 Int 0.64 1.00  
    装箱类 0.28 0.43  
    装箱结构和接口 0.29 0.45  



    表 2《战争与和平》的结果


    实现方式 所用时间 相对于被装箱 Int 的比例  
    被装箱 Int 3.01 1.00  
    装箱类 0.92 0.31  
    装箱结构和接口 0.97 0.32  



    各种实现和驱动程序代码可以在示例代码中找到。为了在归档时不至于太大,我在
    这里省去了这些文本文件。我还引用了一个 Perl 文件作为参考。如果您要运行这
    个 Perl 程序,您需要从 http://www.activestate.com站点下载 ActivePerl 软
    件(这是一个免费软件)。

    总结
    我希望您喜欢这篇关于装箱的介绍。通常,尽管装箱会导致性能上的一些损耗,但
    是并没有太大关系,因为相比之下简化更为重要。但是有时候,可能需要使用一个
    装箱类来减少由于装箱所带来的性能损耗。

    网站摸彩袋  
    在这个摸彩袋中有相当多的站点,所以我使用我的秘密的随机数生成器来挑出其中
    的五个。它们是:

    CSharpFree.com


    csharpindex.com


    C# Corner


    The .NET Enhance Project


    Technical Lead  
    再次需要指出的是,这些都是没有保证的站点。我所能保证的是当您点击上面的  
    URL 地址时会显示一个网页。

      


    ------------------------------------------------------------------------
    --------

    Eric Gunnerson 是 C# 编译器组的 QA 领导,C# 设计组的成员,以及“A  
    Programmer's Introduction to C#”(英文)一书的作者。他从事编程工作的时
    间很长,他甚至知道什么是 8 英寸磁盘,还能用一只手装磁带。



    --

    ※ 来源:·BBS 水木清华站 smth.org·[FROM: 210.39.3.110]
    上一篇
    返回上一页
    回到目录
    回到页首
    下一篇


       收藏   分享  
    顶(0)
      




    ----------------------------------------------

    -----------------------------------------------

    第十二章第一节《用ROR创建面向资源的服务》
    第十二章第二节《用Restlet创建面向资源的服务》
    第三章《REST式服务有什么不同》
    InfoQ SOA首席编辑胡键评《RESTful Web Services中文版》
    [InfoQ文章]解答有关REST的十点疑惑

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2004/11/9 2:25:00
     
     xmzhy 帅哥哟,离线,有人找我吗?狮子座1978-8-18
      
      
      威望:5
      等级:计算机学士学位
      文章:385
      积分:2003
      门派:XML.ORG.CN
      注册:2003/10/22

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给xmzhy发送一个短消息 把xmzhy加入好友 查看xmzhy的个人资料 搜索xmzhy在『 Dot NET,C#,ASP,VB 』的所有贴子 引用回复这个贴子 回复这个贴子 查看xmzhy的博客2
    发贴心情 
    请问WebForm如何打包?
    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2004/12/27 17:17:00
     
     GoogleAdSense狮子座1978-8-18
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 Dot NET,C#,ASP,VB 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/5/3 10:53:09

    本主题贴数2,分页: [1]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    933.594ms