C# 性能优化之斤斤计较
来源:互联网 发布:菜鸟网络会员 编辑:程序博客网 时间:2024/04/26 08:26
今天,我想跟大家聊一聊C#的性能优化,当然,这里并不谈基本的原则,这些都假设你已经非常精通了,本文聊的是要争取几个毫秒的程序。关于基本的性能优化,可以参考园子里的文章。比如:
.NET 性能优化方法总结
先说说我的测试环境:
一台典型的笔记本电脑,Windows 7中文版,.net Framework用的是4.5版本,VS是现在VS11 beta版。我也是用VS2008这样的环境测试了下面的所有场景,发现没有任何区别,所以就以VS11为基准了。
所有测试数据都是编译为Relase,且不包含PDB,直接双击运行而非在VS环境下执行。点击这里下载源代码。
言归正传,先测试第一点:
静态方法比实例方法快吗?
我们总是从各个渠道听说:静态方法比实例方法要快,所以,我想亲自试试。测试方法很简单,循环调用实例方法和静态方法。
01.
/// <summary>
02.
/// 这是一个普通类,调用实例的方法
03.
/// </summary>
04.
public
class
C1 {
05.
public
void
DoLoop() {
06.
for
(
int
i = 0; i <
int
.MaxValue; i++) {
07.
DoIt();
08.
}
09.
}
10.
11.
private
void
DoIt() {
12.
}
13.
}
14.
15.
/// <summary>
16.
/// 使用静态方法调用。
17.
/// </summary>
18.
public
static
class
C2 {
19.
public
static
void
DoLoop() {
20.
for
(
int
i = 0; i <
int
.MaxValue; i++) {
21.
DoIt();
22.
}
23.
}
24.
25.
private
static
void
DoIt() {
26.
}
27.
}
测试结果如下:
测试多次,基本偏差不大,只能说,静态方法比实例方法快那么可怜的一点点,鉴于实例方法的灵活性远大于静态方法,所以还是一般使用实例方法吧。
也实验过,在方法中访问实例字段和静态字段,发现也没有区别,所以不再单独罗列代码。
避免方法内创建实例的情况
这个要讨论的问题有点难说明,我们还是先看一看.net内部的代码吧,下面是一段Collection<T>.Add的方法:
01.
public
void
Add(T item)
02.
{
03.
if
(
this
.items.IsReadOnly)
04.
{
05.
ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_ReadOnlyCollection);
06.
}
07.
int
count =
this
.items.Count;
08.
this
.InsertItem(count, item);
09.
}
注意ThrowHelper类,如果换成我们自己写,一句话就搞定了:throw new NotSupportedException。为什么微软要这么写呢?
老外有解释:Why does SortedList implementation use ThrowHelper instead of throwing directly?
其实,我也是信奉此真理,而且就在前一段时间,一位同事还找我问,两段几乎一样的代码,为什么测试性能有差距,结果我按照此原理,将异常抛出放在外面,结果真的变好了。
现在,我还要再次测试一下,我相信的是数据:
01.
class
C1 {
02.
private
Dictionary<
int
,
int
> _dict =
new
Dictionary<
int
,
int
>() ;
03.
public
C1() {
04.
_dict.Add(1, 1);
05.
_dict.Add(2, 2);
06.
}
07.
08.
public
void
Do1() {
09.
object
obj =
new
object
();
10.
for
(
int
i = 0; i <
int
.MaxValue/100; i++) {
11.
GetItOne(1);
12.
}
13.
}
14.
15.
//这个方法,在内部可能创建实例
16.
private
int
GetItOne(
int
key) {
17.
int
value;
18.
if
(!_dict.TryGetValue(key,
out
value)) {
19.
throw
new
ArgumentOutOfRangeException(
"key"
);
20.
}
21.
return
value;
22.
}
23.
24.
public
void
Do2() {
25.
for
(
int
i = 0; i <
int
.MaxValue/100; i++) {
26.
GetItTwo(1);
27.
}
28.
}
29.
30.
//这个方法,将创建实例的代码移动到外部
31.
private
int
GetItTwo(
int
key) {
32.
int
value;
33.
if
(!_dict.TryGetValue(key,
out
value)) {
34.
ThrowArgumentOutOfRangeException();
35.
}
36.
return
value;
37.
}
38.
39.
private
static
void
ThrowArgumentOutOfRangeException() {
40.
throw
new
ArgumentOutOfRangeException(
"key"
);
41.
}
42.
}
测试结果是:
基本上,会快0.06秒左右,但是如此大的循环得到的好处并不是那么的明显,但有作用。这种写法还是比较舒服的,所以还是建议大家用吧。
枚举数组和普通枚举性能差异
有些人可能知道,.net在处理枚举时,对于数组有特别的优化,所以,当枚举的集合是一个数组时,性能会好些。例如下面的测试代码:
01.
class
C1 {
02.
03.
public
void
Do1() {
04.
int
[] array = { 1, 2, 3, 4 };
05.
for
(
int
i = 0; i <
int
.MaxValue/100; i++) {
06.
DoIt1(array);
07.
}
08.
}
09.
10.
private
void
DoIt1<T>(IEnumerable<T> array) {
11.
foreach
(var item
in
array) {
12.
13.
}
14.
}
15.
16.
public
void
Do2() {
17.
int
[] array = { 1, 2, 3, 4 };
18.
for
(
int
i = 0; i <
int
.MaxValue/100; i++) {
19.
DoIt2(array);
20.
}
21.
}
22.
23.
private
void
DoIt2(
int
[] array) {
24.
foreach
(var item
in
array) {
25.
26.
}
27.
}
28.
}
第23行的方法中,编译器提前已知是一个数组的枚举,所以会优化指令。那么,到底这种优化差距有多大呢?我需要试验一下。
第一个是Do1的结果,第二个是Do2的结果,显而易见,差距还是相当大的,为什么呢?从反编译的IL代码来看,第一个方法使用标准的GetEnumerator机制,需要创建实例,而且要调用Current和MoveNext两个方法,更何况,Array的GetValue实现实在不够快。而第二个方法使用了for的机制,无需创建实例,不断累加和一个判断语句即可,性能当然高了。
在Linq to Object中,其实是有这样考虑的代码的。例如:
01.
public
static
IEnumerable<TResult> Select<TSource, TResult>(
this
IEnumerable<TSource> source, Func<TSource, TResult> selector)
02.
{
03.
if
(source ==
null
)
04.
{
05.
throw
Error.ArgumentNull(
"source"
);
06.
}
07.
if
(selector ==
null
)
08.
{
09.
throw
Error.ArgumentNull(
"selector"
);
10.
}
11.
if
(source
is
Enumerable.Iterator<TSource>)
12.
{
13.
return
((Enumerable.Iterator<TSource>)source).Select<TResult>(selector);
14.
}
15.
if
(source
is
TSource[])
16.
{
17.
return
new
Enumerable.WhereSelectArrayIterator<TSource, TResult>((TSource[])source,
null
, selector);
18.
}
19.
if
(source
is
List<TSource>)
20.
{
21.
return
new
Enumerable.WhereSelectListIterator<TSource, TResult>((List<TSource>)source,
null
, selector);
22.
}
23.
return
new
Enumerable.WhereSelectEnumerableIterator<TSource, TResult>(source,
null
, selector);
24.
}
创建类和结构的性能差异以及属性和字段的性能差异
下面我还要试验创建类实例和结构类型,其性能差异到底有多大?会不会.net的垃圾回收超级厉害,基本上差异不大呢?当然,我也顺手测试了访问属性和访问字段的差别。
01.
class
C1 {
02.
public
void
Do1() {
03.
for
(
int
i = 0; i <
int
.MaxValue/10; i++) {
04.
var p =
new
PointClass() { X = 1, Y = 2 };
05.
}
06.
}
07.
public
void
Do2() {
08.
for
(
int
i = 0; i <
int
.MaxValue/10 ; i++) {
09.
var p =
new
PointClass2() { X = 1, Y = 2 };
10.
}
11.
}
12.
public
void
Do3() {
13.
for
(
int
i = 0; i <
int
.MaxValue/10; i++) {
14.
var p =
new
Point() { X = 1, Y = 2 };
15.
}
16.
}
17.
public
void
Do4() {
18.
for
(
int
i = 0; i <
int
.MaxValue / 10; i++) {
19.
var p =
new
Point() { XP = 1, YP = 2 };
20.
}
21.
}
22.
}
23.
24.
25.
class
PointClass {
26.
public
int
X {
get
;
set
; }
27.
public
int
Y {
get
;
set
; }
28.
}
29.
30.
class
PointClass2 {
31.
public
int
X;
32.
public
int
Y;
33.
}
34.
35.
struct
Point {
36.
public
int
X;
37.
public
int
Y;
38.
39.
public
int
XP {
get
{
return
X; }
set
{ X = value; } }
40.
public
int
YP {
get
{
return
Y; }
set
{ Y = value; } }
41.
}
测试结果如下:
- C# 性能优化之斤斤计较
- 斤斤计较
- 斤斤计较
- C#基础 之 DataTable操作性能优化
- C#第一篇之性能优化
- 该股股价斤斤计较斤斤计较斤斤计较
- 建军节建军节建军节斤斤计较斤斤计较斤斤计较斤斤计较
- C#性能优化
- C#性能优化实践
- C#性能优化实践
- C#性能优化总结
- C#性能优化实践
- C#性能优化实践
- C#性能优化实践
- C#性能优化实践
- C#性能优化总结
- C# string 性能优化
- C#性能优化总结
- 寄存器
- 第四周 课后实践:项目一——三角形类的构造函数(1)
- [Error] ADC.SchDoc Compiler Net NetU1_AD1_14 contains floating input pins (Pin U1_AD1-14) 19:27:
- base64 自定义码表 实现加密解密
- 互联网原创表情论坛举行 探索设计师盈利新生态
- C# 性能优化之斤斤计较
- Handler引起的内存泄露
- javaee学习之路(十七)MYSQL数据库
- div层调整z-index属性在IE中无效原因分析及解决方法
- thinkandroid的学习
- 【Leetcode】Intersection of Two Linked List
- 【读书分享】读《设计中的设计》有感
- java字符串日期类型转换成日期类型(日期类型相加、日期类型间隔天数)
- hdu 2007 平方和与立方和