使用 C#: 打开包装! 快点!
来源:网络整理 责任编辑:栏目编辑 发表时间:2013-07-01 18:16 点击:次
请访问 MSDN 源代码中心,下载本专栏文章中示例的源代码(英文)http://msdn.microsoft.com/code/default.asp?URL=/code/sample.asp?url=/MSDN-FILES/026/002/254/msdncompositedoc.xml。
上个月,我们介绍了装箱和取消装箱的方法,以及什么时候会用到它们。这个月,我们将研究装箱对性能的影响,以及我们应当怎样将这种影响减少到最小。
装箱和性能
由于进行了装箱,所以 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 秒钟的时间内就完成了除了字数统计以外的所有任务。这意味着字数统计部分花费了几秒钟。
让程序运行得更快
我们的目标是取消整型数据的装箱和取消装箱过程。换句话说,我们希望能够不经过对整型数据进行解装箱以及随后再对其重新装箱的过程直接累加“散列表”中的值。要做到这
上个月,我们介绍了装箱和取消装箱的方法,以及什么时候会用到它们。这个月,我们将研究装箱对性能的影响,以及我们应当怎样将这种影响减少到最小。
装箱和性能
由于进行了装箱,所以 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 秒钟的时间内就完成了除了字数统计以外的所有任务。这意味着字数统计部分花费了几秒钟。
让程序运行得更快
我们的目标是取消整型数据的装箱和取消装箱过程。换句话说,我们希望能够不经过对整型数据进行解装箱以及随后再对其重新装箱的过程直接累加“散列表”中的值。要做到这
相关新闻>>
最新推荐更多>>>
- 发表评论
-
- 最新评论 更多>>