RSS 2.0 Feed
2005-12 Entries
摘要:----- update on 12/19/2005 泛型解决了boxing/unboxing的问题,但System.Collections.Generic下面的集合类没有解决性能问题(这个性能问题不是boxing/unboxing带来的,是数据复制带来的)。这应该是更精确的描述(换句话说,前面我说的是不对的)。 所以不要把大量的值类型大对象放到集合里,这个.NET 1.x里面的适用的结论在有了泛型的.NET 2.0依然适用。(这个结论对于明白人来说是显而易见的。而我的这个post出于论坛,是为了纠正论坛里的一些错误言论,见回复) ----- update on 12/18/2005 原贴名为“C#范型的用处不是很大”,现在改为“C# 2.0泛型探讨”。 我原来确实认为C#泛型用处不会很大,因为我看了很多介绍泛型的文章,像这个,把C#泛型的好处主要归纳为这三点:类型安全、二进制代码重用、性能。更好的类型安全是显而易见的,但后面两个好处并不明显。 关于“二进制代码重用”,是建立在“不使用泛型就得为一个功能的实现写多个版本的代码”的假设上的,然而实际上用List Generic能完成的事情ArrayList也能做到,只是类型安全很难保证。这样最终还是类型安全的好处。 关于泛型的性能,你可以看到这个帖子引来了这么多的讨论,我现在觉得这个 问题得分应用场合而讨论。 对于用泛型实现的集合类,比如System.Collections.Generic namespace下面那些,范型可以提升一些性能,但仍然无法避免boxing/unboxing带来的性能问题,所以依然不可滥用值类型。  — 这是我认为泛型在性能上也无太大优势的主要原因。最后我总结道:我写这个blog并不是说范型没用,而是说范型可能没有很多人想象中的那么有用。特别是,不要认为范型能解决boxing/unboxing带来的性能问题。就这样。 而对于其它一些领域的范型应用,比如我在回复中提到的quick sort算法,泛型确实可以提高很多的性能,我的测试代码显示它提高了三倍的性能。  — 这些领域的泛型应用大概是Ninputer反驳我的主要原因。他这样总结道:使用“object来支持任意类型”的做法相比,能够消除unboxing/boxing,因此在使用object后unboxing/boxing在你的程序里影响了性能的时候,泛型能够帮助你避免它。 这两种应用场合完全不同,对范型性能问题的研究自然有不同的结果。所以在各自的应用场合下,我和Ninputer的结论都应该是正确的。(这大概反映了每个程序员的关注点都很不一样)我首先关注的是最常用和最重要的部分,对于.NET泛型,给程序员带来最大好处的肯定非System.Collections.Generic莫属。结果却是忽略了其他的一些方面。 但我原来的标题里面的大结论是错误的(原来我认为泛型用处不大。换句话说,就是.NET的泛型玩不出什么花样),Ninputer用他的VBF有力的证明了这一点。所以现在改了一下标题。同时为update之前的错误言论造成的误导而道歉。 ----- update on 12/17/2005 我的这段果然成功的让很多人不满意了 要完整地评估泛型在性能上的问题,可以做这样一个表来对比: Int32 LargeDataStruct LargeDataClass ArrayList 87.7 2808.7 76.9 List Generic 31.3 1903.1 70.5 Athlon64 2800+/Biostar NForce4/Apacer 512M DDR400 x 2 这是在我的机器上的测试,把数据装入集合再从集合获取数据,循环50w次得到的结果。注意横向的比较(标为红色的部分)。 所以我的结论是: 泛型总是能带来性能提升,但提升不大;或者说仅在int这样很小的数据类型上才有很明显的作用 泛型并不能解决boxing/unboxing的问题,大型的值类型,还是应该设计为引用类型 这回应该说清楚了吧 ----- original 前些天尝试让CSDN的.NET论坛增加一点讨论的气氛,于是发了这个帖子: C# 2.0会给我们带来什么 说来说去还是说到了泛型。但我觉得这个feature真的没什么,最起码没有想象中的那么有用,和C++模板实际上差别很大的,为什么说到C# 2.0就都要先说泛型。最后我这样回复: .NET的泛型,除了能用编译器更好的进行类型检查之外,别的地方用处不大。并且,性能提升并不明显,装箱/拆箱的过程,不要和类型转换相混淆。以装箱为例:int i = 5;object o = i;这里实际上做了两步操作,一个是把数据从线程堆栈复制到GC堆上,另一步是类型转换。这两步形成了装箱的操作。 泛型只能避免第二步,得到一点点性能提升;而最影响效率的地方却是第一步:把数据从线程堆栈复制到GC堆上。 C#的泛型也远不如C++模板那样灵活,要做“模板元编程”之类的高级用法,在C#根本就不可能。各位有兴趣的话可以比较一下C#和C++在这方面的差别。 PS.我这段大概要让人失望了,但这是事实,C#泛型被讨论的实在太多了,而实际上它的用处不大。其好处主要在编译时更好的类型检查,减少人为错误的可能性。 我目前能看到的好处仅在于“编译时更好的类型检查”而已 — 当然,这个好处也非常重要,但别的真的就没什么了。特别是,很多网友把C#泛型当作解决boxing/unboxing性能问题的药方,实际上根本就不是这样,这种错误的观点会带来更大的性能问题。 误导来自于这种测试代码:int i = 5; ArrayList list1 = new ArrayList(); list1.Add(i); i = (int)list1[0]; List<int> list2 = new List<int>(); list2.Add(i); i = list2[0]; 没错,后者的性能几乎要比前面的高100%。这让人产生了泛型可以解决boxing/unboxing性能问题的错觉,但实际上这是因为这里是小小的int,boxing/unboxing和类型转换的代价是差不多的。但是可以从反面证明我的观点: 假如泛型能解决boxing/unboxing性能问题,那么这两段代码的运行速度应该是大致相当的:struct LargeDataStruct ...{ decimal A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z; } class LargeDataClass ...{ decimal A, B, C, D, E,......[阅读全文]

posted @ | Feedback (78) | Filed Under [ Inside CLR ]

摘要: 太faint了... ?写这个post肯定要让很多人不高兴了... 但这个问题早晚都会有人说的吧 这是我刚才任务管理器的截图,按照内存占用排序,前三个依次是VS2005(Team Suite),SQL2005管理器,ASP.NET工作进程。 我的机器1G内存,现在可用内存总是在100M上下徘徊。(不过内存占用虽大,速度倒是挺快的) 感觉用VS2005开发,1G内存是下限。(不知道Express版本的VS会不会好一点)...[阅读全文]

posted @ | Feedback (5) | Filed Under [ Deployment and Configuration ]

摘要:1、Array in stack 对于这样的struct:typedef struct { int XY[2]; } Point2D; 要在.NET为一个非托管函数传递这样一个结构体,原来得这样定义:struct Point2D ...{ [MarshalAs(UnmanagedType.ByValArray, SizeConst=2)] public int[] XY; } 现在可以这么写(不过得用unsafe上下文):unsafe struct Point2D ...{ public fixed int XY[2]; } 不过这个功能还非常有限,不知道是出于什么原因考虑,只允许在strcut里面定义这样的数组,并且只能使用bool, byte, short, int, long, char, sbyte, ushort, uint, ulong, float和double这样的primitive类型。 也可以把数组作为局部变量分配在堆栈上,只是语法不太一样,那就是stackalloc关键字:int* fib = stackalloc int[100];,也要unsafe上下文。这可以提高不少效率。这是.NET 1.x就有的功能,只是似乎没人用这个... PS. 对于只允许使用primitive类型,我认为是没道理的,最起码应该允许所有值类型的栈内数组。设计者们为啥这么考虑呢?怕堆栈溢出?据我测试.NET的堆栈空间也是1M左右,大部分情况下这么大的栈空间都被浪费了。 2、Function pointer as a return value 在.NET 1.x做P/Invoke时,对于那些回调函数,可以使用Delegate类型的参数作为函数指针传入。但有些非托管函数的返回值也是个函数指针,此时.NET 1.1变得无能为力,要调用这个函数,你得用native代码再写个包装,总之很麻烦。 .NET 2.0的System.Runtime.InteropServices.Marshal类为此需求新增了两个方法:public static Delegate GetDelegateForFunctionPointer ( IntPtr ptr, Type t ); public static IntPtr GetFunctionPointerForDelegate ( Delegate d ); 3、Marshal过程支持更多的类型 这是一个很细的问题,比如这样一个非托管struct:typedef struct { Point2D XYZ[3]; } Point2DX3; 对应到C#,你也许会这样写:struct......[阅读全文]

posted @ | Feedback (10) | Filed Under [ P/Invoke ]