My Blog! blog.gkong.com   

Tip of my blog

新年快乐!


阅读(7321) 评论(1)
标签(TAG):

注:请常常上MSDN 搜一下,里边有很多大师级程序员写的关于如何优化托和提高“托管代码”性能的好文。

用 C# 编写更快的托管上位机程序代码 (原创:zj_qzm)

为什么我会用C#来写工控程序,而不是C++呢?原因有两个,第一,我不想用MFC来写程序,第二,由于有了C#的出现。当然了,用C++写程序有一个很大的优点就是“快”,而且部署容易;而C#呢,由于它是机于 Framework 的,要安装运行时库,而且比C++要慢。

但是C#也有它天生的优点,我最看重的就是开发的效率极高(这是VC++的缺点所在),而且容易上手,现在也有很多基于 .NET Framework 的工控相关的资源可用,比如NI的Measurement Studio.Net 组件就是专门为 Visual Studio.NET 而开发的,里面有丰富的UI资源和各种工业控制所用到的算法、波形产生函数、软件滤波、PID等等都已集成成 类方法的形式提供给调用者。

由于现在的电脑已经非常快,内存也很大,所以,代码的速度和效率比起来效率占了上风,但并不是就不用考虑代码的速度了。因此,我在此总结出用C#如何写出接近C++代码速度的方法。这对于上位机这种实时性要求比较高的程序是很有用的。

首先要清除一个误区,.NET Framework 的代码速度和 java 根本不是一个层次的,尽管它们都是托管代码,但是JAVA只能写蜗牛应用程序,而 .NET Framework 托管代码能写出很快的本地应用程序。

为什么呢?原因就在于它的“实时编译器”, CLR 使用 JIT(实时)编译器将 CIL 中的各种方法编译成本机 x86 代码,然后运行本机代码。尽管 JIT 在编译首次调用的方法时会稍有延迟,但所调用的各种方法在运行纯本机代码时都不需要解释性的系统开销。

与传统的脱机 C++ 编译过程不同,JIT 编译器花费的时间对用户来说都是“时钟时间”延迟,因此 JIT 编译器不具备占用大量时间的彻底优化过程。尽管如此,JIT 编译器所执行的一系列优化仍给人以深刻印象:

常量重叠

常量和复制的传播

通用子表达式消除

循环不变量的代码活动

死存储 (Dead Store) 和死代码 (Dead Code) 消除

寄存器分配

内联方法

循环展开(带有小循环体的小循环)

结果可以与传统的本机代码相媲美,至少是相近。

至于数据,可以混合使用值类型和引用类型。值类型(包括整型、浮点类型、枚举和结构)通常存储在栈中。这些数据类型就像 C/C++ 中的本地和结构一样又小又快。使用 C/C++ 时,应该避免将大的结构作为方法参数或返回值进行传送,因为复制的系统开销可能会大的惊人。

引用类型和装箱后的值类型存储在堆中。它们通过对象引用来寻址,这些对象引用只是计算机的指针,就像 C/C++ 中的对象指针一样。 因此实时编译的托管代码可以很快。

以下就是编写快速本机代码的原则:

1、不要创建不必要的对象

托管代码与本机代码的最大不同之处在于自动内存管理。您可以分配新的对象,但 CLR 垃圾回收器 (GC) 会在这些对象无法访问时自动释放它们。GC 不时地运行,通常不为人觉察,但一般会使应用程序停止一两毫秒,偶尔也会更长一些。 不过GC是一个很优秀的内存管理专家。但是我们也要记住,不要创建不必要的对象。由于自动内存管理消除了许多对象分配和释放方面的复杂情况、问题和错误,并且用起来又快又方便,因此我们会很自然地想要创建越来越多的对象,最终形成错综复杂的对象群。如果您想编写真正的快速托管代码,创建对象时就需要深思熟虑,确保对象的数量合适。

将组件和应用程序作为 CIL 的程序集的最大好处之一是,您的程序可以做到每秒都变快、每年都变快。“每秒都变快”是因为运行时(理论上)可以在程序运行时重新调整 JIT 编译的代码;“每年都变快”是因为新发布的运行时总能提供更好、更先进、更快的算法以将代码迅速优化。因此,如果 .NET 1.1 中的这几个计时不是最佳结果,请相信在以后发布的产品中它们会得到改善。 而且在今后发布的 .NET Framework 中,本文中所列代码的本机代码序列可能会更改。

2、尽量不用委托来调用方法,尽可能用函数。

委托调用比方法调用大约慢八倍。希望以后会有所改进。

3、尽可能不用线程,而用延时函数。比如framework 内置的timer、和Windows 系统提供的高精度记时器,等等。

4、记住,发布代码时一定要以Release的形式发布,因为它是被优化了。

5、尽可能不用反射 (Reflection)

有这样一种说法:“如果您要知道‘反射'的开销是多少,您可能根本负担不起。”

反射很有用而且功能强大,但与实时编译的本机代码相比,它既不显得快,也不够精炼。

6、 许多 C/C++ 代码优化方法也同样适用于托管代码。

如果您想成为一名高性能托管代码的开发人员,您必须获得“托管代码知识”。您必须了解每项低级操作的开销,必须了解像委托 (Delegate) 和代码访问安全等这类功能的开销,还必须了解正在使用以及正在编写的类型和方法的开销。能够发现哪些方法的开销太大,对您的应用程序不会有什么损害,反倒因此可以避免使用这些方法。

7、要正确理解托管数据和垃圾回收器

在以性能为中心的应用程序中使用托管代码时,开发人员最关心的问题之一就是 CLR 内存管理的开销 - 这种管理由垃圾回收器 (GC) 执行。内存管理的开销由与类型实例关联的内存的分配开销、在实例的整个生命周期中内存的管理开销以及当不再需要时内存的释放开销计算得出。

托管分配的开销通常都非常小,在大多数情况下,比 C/C++ malloc 或 new 所需的时间还要少。这是因为 CLR 不需要通过扫描可用列表来查找下一个足以容纳新对象的可用连续内存块,而是一直都将指针保持指向内存中的下一个可用位置。我们可以将对托管堆的分配看作“类似于堆栈”。如果 GC 需要释放内存才能分配新对象,那么分配可能会引发回收操作。在这种情况下,分配的开销就会比 mallocnew 大。固定的对象也会对分配的开销造成影响。固定的对象是指那些 GC 接到指令,在回收操作期间不能移动其位置的对象,通常是由于这些对象的地址已传递到本机 API 中。

与 malloc 或 new 不同的是,在对象的整个生命周期中管理内存都会带来开销。CLR GC 区分不同的代,这就意味着它不是每次都回收整个堆。但是,GC 仍然需要了解剩余堆中的活动对象的根是否是堆中正在回收的那部分对象。如果内存中包含的对象具有对其下一代对象的引用,那么在这些对象的生命周期中,管理内存的开销将会非常大。

8、关于终结 (Finalization)

CLR 提供一种在释放与类型实例关联的内存之前自动进行清理的机制。这一机制称为终结 (Finalization)。通常,终结用于释放本机资源,在这种情况下,释放由对象使用的数据库连接或操作系统句柄。

终结是一个开销很大的功能,而且它还会加大 GC 的压力。GC 会跟踪 Finalizable 队列中需要执行终结操作的对象。如果在回收期间,GC 发现了一个不再存活且需要终结的对象,它就会将该对象在 Finalizable 队列中的条目移至 FReachable 队列中。终结操作在一个称为终结器线程 (Finalizer Thread) 的独立线程中执行。因为在终结器的执行过程中,可能需要用到该对象的所有状态,因此该对象或其指向的所有对象都会升级至下一代。与对象或对象图相关的内存仅在执行后续的 GC 时才会释放。

需要释放的资源应该包装在一个尽可能小的可终结对象中,例如,如果您的类需要引用托管资源和非托管资源,那么您应该在新的可终结类中包装非托管资源,并使该类成为您的类的成员。父类不能是可终结类。这意味着只有包含非托管资源的类会被升级(假如您没有在包含非托管资源的类中引用父类)。另外还要记住只有一个终结线程。如果有终结器导致此线程被阻塞,就不能调用后续的终结器,也不能释放资源,而且您的应用程序会导致资源泄漏。

提示应该尽可能使终结器保持简单,且永远不会阻塞。

提示仅将需要清理的非托管对象的包装类设为可终结

可以将终结认为是引用计数的一种替代形式。执行引用计数的对象将跟踪有多少其他对象对其进行引用(这会导致一些非常众所周知的问题),以便在引用计数为零时释放其资源。CLR 没有实现引用计数,因此它需要提供一种机制,以便在不存在对象的引用时自动释放资源。终结就是这种机制。通常,终结仅需要在要清理的对象的生命周期不明确的情况下执行。

9、处置模式 (Dispose Pattern)

当对象的生命周期不明确时,应该尽快释放与该对象关联的非托管资源。这一过程称为“处置”对象。处置模式通过 IDisposable 接口实现(尽管您自己实现也很容易)。如果您希望对类应用终结,例如,要使类实例可处置,就需要让对象实现 IDisposable 接口并执行 Dispose 方法。使用 Dispose 方法您可以调用终结器中的同一段清理代码,并通知 GC 不需要通过调用 GC.SuppressFinalization 方法来终结该对象。最好同时使用 Dispose 方法和终结器来调用通用终结函数,这样就只需要维护一份清理代码。而且,如果对象的语义是这样的:Close 方法比 Dispose 方法更符合逻辑,那么还应实现 Close 方法,在这种情况下,数据库连接或套接字逻辑上都被“关闭”。Close 可以只是调用 Dispose 方法。

使用终结器为类提供 Dispose 方法始终是一种很好的做法,因为永远没有人能确切地知道使用类的方法,例如,是否可以明确知道它的生命周期。如果您使用的类实现了处置模式,而且您也确切地知道何时处置好对象,则最好明确地调用 Dispose

提示请为所有可终结的类提供 Dispose 方法。

提示请在 Dispose 方法中取消终结操作。

提示请调用通用清理函数。

提示如果您使用的对象实现了 IDisposable,并且您知道已不再需要该对象,请调用 Dispose 方法。

C# 提供了一种非常方便的自动处置对象的方法。使用关键字 using 来标记代码块,之后,将对大量可处置对象调用 Dispose

C# using 关键字

using(DisposableType T){ //Do some work with T}//T.Dispose() is called automatically

10、托管代码和 CLR JIT

托管程序集是托管代码的分发单位,它由 Microsoft 中间语言(MSIL 或 IL)构成,适用于所有的处理器。CLR 的实时 (JIT) 功能可将 IL 编译成优化的本机 X86 指令。JIT 是一种执行优化操作的编译器,但是由于编译是在软件运行时进行的,并且仅当第一次调用方法时才会进行,因此进行优化的次数需要与执行编译所花费的时间保持平衡。通常,这对于服务器应用程序并不重要,因为启动时间和响应对于它们来说通常都不构成问题;但对于客户端应用程序来说,却十分重要。请注意,安装时可以通过使用 NGEN.exe 执行编译来加快启动时间。

许多由 JIT 执行的优化操作都没有与其关联的编程模式,例如,您无法对它们进行显式编码,但是也有一些优化操作具有关联的编程模式。下一节将讨论后者中的部分操作。

提示使用 NGEN.exe 实用工具在安装时编译应用程序,可以加快客户端应用程序的启动时间。

11、方法内联

所有的方法调用都会带来开销。例如,需要将参数推入堆栈中或存储在寄存器中,需要执行的方法起头 (prolog) 和结尾 (epilog) 等。只需要将被调用方法的方法主体移入调用方的主体,就可以避免某些方法的调用开销。这一操作称为方法内联。JIT 使用大量的探测方法来确定是否应内联某个方法。下面是其中一些比较重要的探测方法的列表(请注意这并不是一个详尽的列表):

IL 超过 32 字节的方法不会内联。

虚函数不会内联。

包含复杂流程控制的函数不会内联。复杂流程控制是除 if/then/else 以外的任意流程控制,在这种情况下,为 switch 或 while。

包含异常处理块的方法不会内联,但是引发异常的方法可以内联。

如果某个方法的所有定参都为结构,则该方法不会内联。

我会认真考虑一下对这些探测方法进行显式编码的问题,因为在以后的 JIT 版本中它们可能会有所变化。请不要为了确保方法可以内联而放弃方法的正确性。您也许已经注意到了一个有趣的现象,C++ 中的关键字 inline __inline不能保证编译器将一种方法内联(尽管 __forceinline可以)。

一般情况下,属性的 Get 和 Set 方法都非常适合内联,因为它们主要用于初始化私有数据成员。

提示请不要为了试图保证内联而放弃方法的正确性。

去除范围检查

托管代码有许多优点,其中一项是可以自动进行范围检查。每次使用 array[index] 语义访问数组时,JIT 都会进行检查以确保索引在数组范围中。在具有大量迭代和少量由每个迭代执行的指令的循环环境中,范围检查的开销可能会很大。在某些情况下,JIT 也可能会在检测到这些范围检查不必要时将其从循环体中删除,即仅在循环开始执行之前对其进行一次检查。在 C# 中有一种编程模式,用来确保这些范围检查会被删除:对“for”语句中数组的长度进行的显式测试。请注意,只要此模式中存在细微的偏差都会导致检查无法去除,在这种情况下,需向索引中添加一个值。

C# 中去除范围检查

//Range check will be eliminated
for(int i = 0; i < myArray.Length; i++) 
{
   Console.WriteLine(myArray[i].ToString());
}
//Range check will NOT be eliminated
for(int i = 0; i < myArray.Length + y; i++) 
{ 
   Console.WriteLine(myArray[i+x].ToString());
}

搜索大型不规则数组时,优化操作特别明显,因为此时将同时删除内循环和外循环范围检查。

要求进行变量使用情况跟踪的优化操作

大量 JIT 编译器优化操作都要求 JIT 跟踪定参和局部变量的使用情况,例如,它们在方法主体中的最早以及最近一次使用的时间。在 CLR 1.0 和 1.1 版中,JIT 可以跟踪使用情况的变量总数限制在 64 个之内。例如“寄存操作”就是一个需要进行使用情况跟踪的优化操作。寄存操作是指将变量存储在处理器寄存器中,而不是堆栈框架中(例如,在内存中)。与对堆栈框架中的变量进行访问的时间相比,对寄存变量的访问要快得多,即便框架中的变量位于处理器的缓存中也一样。只能对 64 个变量进行寄存,所有其他变量都将推至堆栈中。除寄存操作以外,另外也有一些优化操作需要进行使用情况跟踪。应该将方法中的定参和局部参数的数量保持在 64 个以下,以确保实现最大数目的 JIT 优化操作。请记住在以后的 CLR 版本中此数目可能会有所变化。

提示使方法保持简短。要这样做的原因有很多,包括方法内联、寄存操作和 JIT 持续时间的需要。

其他 JIT 优化操作

JIT 编译器还可以执行大量其他的优化操作:常量和副本传播、循环不变式提升以及若干其他操作。需要用来实现优化的编程模式都是免费的,无需花钱购买。

为什么我在 Visual Studio 中没有看到这些优化功能?

当您在 Visual Studio 中使用 Debug(调试)菜单或按下 F5 键启动应用程序时,无论您生成的是发行版还是调试版,所有的 JIT 优化功能都将被禁用。当托管应用程序通过调试器启动时,即使它不是该应用程序的调试版本,JIT 也会发出非优化的 x86 指令。如果您希望 JIT 发出优化代码,那么请从 Windows 资源管理器中启动该应用程序,或者在 Visual Studio 中使用 CTRL+F5 组合键。如果希望查看优化的反汇编程序,并将其与非优化代码进行对比,则可以使用 cordbg.exe。

提示使用 cordbg.exe 可以查看 JIT 发出的优化和非优化代码的反汇编程序。使用 cordbg.exe启动应用程序之后,可以通过键入下列代码设置 JIT 模式:

(cordbg) mode JitOptimizations 1
JIT's will produce optimized code
(cordbg) mode JitOptimizations 0

JIT 将生成可调试(非优化)代码。

12、值类型

CLR 可提供两组不同的类型:引用类型和值类型。引用类型总是分配到托管堆中,并按引用传递(正如它的名称所暗示的)。值类型分配到堆栈中或在堆中内联为对象的一部分,默认情况下按值传递,不过您也可以按引用来传递它们。分配值类型时,所需的开销非常小,假设它们总是又小又简单,当它们作为参数进行传递时开销也会很小。正确使用值类型的一个很好的示例就是包含 xy 坐标的 Point 值类型。

Point 值类型

struct Point
{
   public int x;
   public int y;
   //
}

值类型也可以视为对象,例如,可以在值类型上调用对象方法,它们可以转换为对象,或传递到需要使用对象的位置。无论使用什么方法,只要将值类型转换为引用类型,都需要经过装箱 (Boxing) 处理。对值类型进行装箱处理时,会在托管堆中分配一个新对象,而值则复制到此新对象中。这项操作将占用很大的系统开销,还可能会降低或完全消除通过使用值类型而获得的性能。将装箱的类型隐式或显式转换回值类型的过程称为取消装箱 (Unboxed)。

装箱/取消装箱值类型

C#

int BoxUnboxValueType(){ int i = 10; object o = (object)i; //i is Boxed return (int)o + 3; //i is Unboxed}

MSIL

.method private hidebysig instance int32 BoxUnboxValueType() cil managed{ // Code size 20 (0x14) .maxstack 2 .locals init (int32 V_0, object V_1) IL_0000: ldc.i4.s 10 IL_0002: stloc.0 IL_0003: ldloc.0 IL_0004: box [mscorlib]System.Int32 IL_0009: stloc.1 IL_000a: ldloc.1 IL_000b: unbox [mscorlib]System.Int32 IL_0010: ldind.i4 IL_0011: ldc.i4.3 IL_0012: add IL_0013: ret} // end of method Class1::BoxUnboxValueType

如果实现自定义值类型(C# 中的结构),则应该考虑覆盖 ToString 方法。如果不覆盖此方法,那么对值类型上的 ToString 的调用将导致该类型被装箱。对于从 System.Object 继承的其他方法也是如此。在这种情况下,请使用 Equals 来进行覆盖,尽管 ToString 很可能是最常用的调用方法。如果您希望了解值类型是否被装箱以及何时装箱,可以使用 ildasm.exe 实用工具在 MSIL 中查找 box 指令(如上所述)。

覆盖 C# 中的 ToString() 方法以防止装箱

struct Point{ public int x; public int y; //This will prevent type being boxed when ToString is called public override string ToString() { return x.ToString() + "," + y.ToString(); }}

请注意,在创建集合(例如,浮点数组列表)时,添加到集合中的每一项都将进行装箱。您应该考虑使用数组或为值类型创建自定义集合类。

使用 C# 中的集合类时进行隐式装箱

ArrayList al = new ArrayList();al.Add(42.0F); //Implicitly Boxed becuase Add() takes objectfloat f = (float)al[0]; //Unboxed

13、异常处理

通常,错误条件都将作为常规流程控制使用。在此情况下,如果试图通过编程将用户添加到 Active Directory 实例中,则只能试着添加该用户,如果系统返回 E_ADS_OBJECT_EXISTS HRESULT,则说明它们已经存在于该目录中。此外,您也可以通过搜索目录查找该用户,如果搜索失败则只需添加该用户。

按照常规流程控制使用错误,在 CLR 环境中会降低性能。CLR 中的错误处理是借助结构化异常处理实现的。引发异常之前,托管异常的开销非常小。在 CLR 中,引发异常时,需要使用堆栈遍历为已引发的异常找到相应的异常处理程序。堆栈遍历是一种开销较大的操作。正如它的名称所表示的,异常应该用于异常或意外的情况。

提示对于以性能为中心的方法,请考虑返回预期结果的枚举结果,而不是引发异常。(这在写工控程序时是要注意的,在工控程序代码中并不提倡写try{} catch{} finally{} 而是每个函数执行的效果用返回一个枚举值来表示,有时也用 bool数据类型。)

提示有多种 .NET CLR 异常性能计数器都可以通知您在应用程序中引发了多少异常。

提示如果您使用 VB.NET 的 use exceptions 而不是 On Error Goto,错误对象就是不必要的开销。

14、关于线程

在单处理器计算机上,线程并不能提高程序的性能;请记住,添加线程实际上会降低吞吐量,而不会增加吞吐量,但它肯定会增加内存的利用率。

而线程在工控程序中最多的应用是提高反应性能,如提高显示给用户看的界面的反应性能(但这是以增加CPU开销为代价的,也就是降低了吞吐量)

16、反射 (Reflection)

反射是由 CLR 提供的一种机制,用于在运行时通过编程方式获得类型信息。反射在很大程度上取决于嵌入在托管程序集中的元数据。许多反射 API 都要求搜索并分析元数据,这些操作的开销都很大。

这些反射 API 可以分为三个性能区间:类型比较、成员枚举和成员调用。这些区间的系统开销一直在变大。类型比较操作,在本例中,有 typeof (C#)、以及 GetTypeisIsInstanceOfType 等,都是开销最小的反射 API,尽管它们的实际开销一点也不小。成员枚举操作可以通过编程方式对类的方法、属性、字段、事件、构造函数等进行检查。例如,可能会在设计时的方案中使用这一类的成员枚举操作,在这种情况下,此操作将枚举 Visual Studio 中的 Property Browser(属性浏览器)的 Customs Web Controls(自定义 Web 控件)的属性。那些用于动态调用类成员或动态发出 JIT 并执行某个方法的反射 API 是开销最大的反射 API。当然,如果需要动态加载程序集、类型实例化以及方法调用,还存在一种晚期绑定方案,但是这种松散的耦合关系需要进行明确的性能权衡。一般情况下,应该在对性能影响很大的代码路径中避免使用反射 API。请注意,尽管您没有直接使用反射,但是您使用的 API 可能会使用它。因此,也要注意是否间接使用了反射 API。

17、晚期绑定

晚期绑定调用是一种在内部使用反射的功能。Visual Basic.NET 和 JScript.NET 都支持晚期绑定调用。例如,使用变量之前您不必进行声明。晚期绑定对象实际上是类型对象,可以在运行时使用反射将该对象转换为正确的类型。晚期绑定调用比直接调用要慢几个数量级。除非您确实需要晚期绑定行为,否则应该避免在性能关键代码路径中使用它。

提示如果您正在使用 VB.NET,且并不一定需要晚期绑定,您可以在源文件的顶部包含 Option Explicit On 和 Option Strict On 以便通知编译器不允许晚期绑定。这些选项将强制您进行声明,并要求您设置变量类型并关闭隐式转换。

18、安全性

安全性是必要的而且也是主要的 CLR 的组成部分,使用它时会降低性能。当代码为 Fully Trusted(完全信任)且安全策略为默认设置时,安全性对应用程序的吞吐量和启动时间的影响会很小。对代码持不完全信任态度(例如,来自 Internet 或 Intranet 区域的代码)或缩小 MyComputer Grant Set 都将增加安全性的性能开销。

19、COM 互操作和平台调用

COM 互操作和平台调用会以几乎透明的方式为托管代码提供本机 API,通常调用大多数本机 API 时都不需要任何特殊代码,但是可能需要使用鼠标进行多次单击。正如您所预计的,从托管代码中调用本机代码会带来开销,反之亦然。这笔开销由两部分组成:一部分是固定开销,此开销与在本机代码和托管代码之间进行的转换有关;另一部分是可变开销,此开销与那些可能要用到的参数封送和返回值有关。COM 互操作和平台调用的固定开销在开销中占的比例较小:通常不超过 50 条指令。在各托管类型之间进行封送处理的开销取决于它们在边界两侧的表示形式的相似程度。需要进行大量转换的类型开销相对较大。例如,CLR 中的所有字符串都为 Unicode 字符串。如果要通过平台调用需要 ANSI 字符数组的 Win32 API,则必须缩小该字符串中的每个字符。但是,如果是将托管的整数数组传递到需要本机整数数组的类型中时,就不需要进行封送处理。

由于存在与调用本机代码相关的性能开销,因此您应该确保该开销是合理的开销。如果您打算进行本机调用,请确保本机调用所做的工作使得因执行此调用而产生的性能开销划算,即尽量使方法“小而精”而非“大而全”。测量本机调用开销的一种好方法是测量不接受任何参数也不具备任何返回值的本机方法的性能,然后再测量您希望调用的本机方法的性能。它们之间的差异即封送处理的开销。

提示应创建“小而精”的 COM 互操作和平台调用,而不是“大而全”的调用,并确保调用的开销对于调用的工作量是划算的。

请注意,不存在与托管线程相关的线程模式。当您打算进行 COM 互操作调用时,需要确保已将执行调用的线程初始化为正确的 COM 线程模式。此操作通常是使用 MTAThreadAttribute 和 STAThreadAttribute 来实现的(尽管也可以通过编程来实现)。

20、要善于利用性能计数器

有大量的 Windows 性能计数器可供 .NET CLR 使用。当开发人员首次诊断性能问题,或试图找出托管应用程序的性能特点时,这些性能计数器就是他们可以选择的武器。我已经简要介绍了几个与内存管理和异常有关的性能计数器。在 CLR 和 .NET 框架中,性能计数器几乎无处不在。通常,这些性能计数器都可以使用且对系统无害,它们的开销较低,而且不会改变应用程序的性能特征。

请不要对您的应用程序的目标平台以及您正在使用的 API 的性能做任何的假设。请测量它们!

21、提高 .NET Framework 应用程序的字符串处理性能

在一个方法中多次出现字符串的连接或替换时,除非是最无关紧要的字符串连接(或替换)操作才用string,否则都应该使用 StringBuilder 类。与在性能和可缩放性方面可能获得的好处相比,使用 StringBuilder 类所需的额外投入完全可以忽略不计。

22、请看我收集的“垃圾回收器基础与性能提示”

by 进化中的兔子 发表于:2005/11/12 16:31:33
回复:用 C# 编写更快的托管上位机程序代码 (原创:zj_qzm)
引用 | 举报回复

感谢博主,分析的很好,现在没有更新了吗?

by craigtao 发表于:2015/7/28 9:00:30
2005/11/18更新
引用 | 举报回复

by zjcsharp 发表于:2005/11/18 11:54:47
补充说明
引用 | 举报回复

这篇文章将不定时更新,因为优化托管代码的技术远不只于此.

by zjcsharp 发表于:2005/11/12 16:39:38
My subject | 我的主题

New entries | 新发表

Messages Board | 留言板


Guest Comments | 新评注


Blogger Login | 登陆栏
Blog Infomation | 信息栏
博客名称:嵌入式系统&虚拟仪器 日志总数:450 评论数量:277 访问次数:1781231 建立时间::2005年10月25日
Blog Infomation | 搜索栏
Blog Infomation | 收藏栏

XML RSS 2.0

嵌入式系统&虚拟仪器
© COPYRIGHT 2004 ALL RIGHTS RESERVED http://www.gkong.com

中华工控网 | 联系我们 | 工控论坛首页 | 工控博客首页 | 博客注册 | 博客登陆

工控博客管理联系邮箱:工控博客服务邮箱

中华工控网 © Copyright 2013. All rights reserved.