C# 性能优化最佳实践

1、显式注册的EvenHandler要显式注销以避免内存泄漏

将一个成员方法注册到某个对象的事件会造成后者持有前者的引用。在事件注销之前,前者不会被垃圾回收。

private void Form1_Load()
{
  ……
  //注册事件
  CommandRemotingContext.CmdChanged += new ReciverCmdStateChangedEventHandler(this.CommandRemotingContext_CmdChanged);
  ……
}
private void Form1_FromClosed()
{
  ……
  //关闭窗体时及时释放事件
  CommandRemotingContext.CmdChanged -= new ReciverCmdStateChangedEventHandler(this.CommandRemotingContext_CmdChanged);
  ……
}

由事件引起的内存泄漏问题:

  • 对象A订阅了对象B中的事件
  • 对象A的生命周期远远大于对象B
  • 对象A没有取消订阅对象B的时间
  • 最终导致对象B无法释放

2、控件绑定的数据源批量操作应避免自动刷新

  • 客户端批量操作数据时,控件自带的刷新操作,会造成不必要的时间消耗
  • 当数据源(如DataTable、Array、List、ObservableCollection或其他IListSource等)被绑定到控件时,批量操作数据时应该断开绑定或挂起控件的刷新。
    this.gcBillList.DataSource = null;
    DataRowCollection rows = this.ds.Tables[0].Rows;
    foreach (DataRow row in rows)
    {
        // DataRow数据操作
    }
    this.gcBillList.DataSource = this.ds.Tables[0].DefaultView;

3、减少客户端与服务端的通信次数

  • WebService调用并非越少越好,传输数据量较大的情况可考虑拆分为多次调用
  • 对于短WebService的调用,应尽量合并以减少交互次数
    //多次调用了相同的WS
    txtCompanyName.Text=SCPubFunctionClient.PublicWSCal<string>(“ForTest”, “GetCompanyNameByID”,“0001”);  txtCompanyInnerName.Text=SCPubFunctionClient.PublicWSCal<string>(“ForTest”, “GetCompanyInnerNameByID”,“0001”);
    //合并相邻的WS
    string[] result=SCPubFunctionClient.PublicWSCal<string>(“ForTest”, “GetCompanyNameAndInnerNameByID”,“0001”);
    txtCompanyName.Text=result[0];
    txtCompanyInnerName.Text= result[1];  

4、减少客户端与服务端的通信次数

如非必要,应尽量避免在循环体内重复调用WebService

//循环调用了相同的WS
List<Person> persons;
……
foreach(string personID in personIDs)
{
	person=HRPubWsClient.getPerson(personID);
	persons.Add(person);
}
//合并WS
List<Person> persons;
……
persons =HRPubWsClient.getPersonList(personIDs);

5、使用泛型来避免装箱、拆箱操作(减少垃圾回收压力)

  • 装箱操作会造成GC压力;如果发生在集合中,应该使用泛型集合避免。
  • 对于值类型的集合,使用List<T>来代替ArrayList,使用Dictionary<TKey, TValue> 来代替Hashtable。
    ArrayList h=new ArrayList();  //不建议
    h.Add(1);
     
    List<object> h = new List<object>();  //不建议
    h.Add(1);
     
    List<int> h = new List<int>();    //建议
    h.Add(1);

6、字符串操作:

C# 字符串操作--减少垃圾回收压力
7、使用常量避免创建对象

  • 如下例,程序中存在大量 new decimal(0)的代码,这会导致小对象频繁创建及回收;正确的做法是使用 Decimal.Zero 常量。

    private string CurrencyCalc()
    {
        if (firstValue == new decimal(0))  ……
        if (secondValue == new decimal(0)) ……
        if (thirdValue == new decimal(0)) ……
        if (fourthValue == new decimal(0)) ……
        ……
    } 

8、避免不必要的抛出异常

C# 异常处理(Catch Throw)IL分析

9、使用RemoveAll而非RemoveAt进行删除多个元素

  • 使用RemoveAll方法对集合(如List)中的多个元素进行一次性删除时,只会对List的内部数组做一次resize 操作,效率明显高于循环调用RemoveAt。

    List<string>  lst = new List<string> {"1", "2", "3", "1", "2", "4"};
    //不建议:
    for (int i = lst.Count - 1; i >= 0; i--)
    {
        if (lst[i] == "1" || lst[i] == "2")
        {
            lst.RemoveAt(i);
         }
    }
     
    //建议:
    lst.RemoveAll(s => s == "1" || s == "2");

10、C# DataSet性能最佳实践

11、反射与动态绑定--减少CPU占用

  • 反射技术是将编译期间的静态绑定转换为延迟到运行期间的动态绑定。
  • C#主要支持 5 种动态创建对象的方式(时间消耗来自网络,与我实测的差距挺大,具体测试见下方):
动态创建对象的方式 与Direct Create
1.Type.InvokeMember 慢40倍以上
2.ContructorInfo.Invoke 慢40倍以上
3.Activator.CreateInstance(Type) 慢7倍
4.Activator.CreateInstance(assemblyName, typeName) 慢1000倍以上
5.Assembly.CreateInstance(typeName) 慢40倍以上
  • 应尽量避免使用反射和动态绑定;如必须使用,要遵循以下原则:

1. 使用接口调用方式将动态绑定改造为早期绑定(Direct Call)
2. 使用 Activator.CreateInstance(Type)方式动态创建对象

3. 使用typeof操作符代替GetType调用

小注:

通过循环创建实例记录时间如下:

加载程序集、获取类型在循环外部时间如下(这时不同创建方式消耗时间差距挺大):

代码如下:

 public void TestCreateInstance()
        {
            Stopwatch watch1 = new Stopwatch();
            var asmb = Assembly.LoadFrom("ReflectiveClassLibrary.dll");
            Type type = asmb.GetType("ReflectiveClassLibrary.TestClass");
            int num = 100000;
            watch1.Start();

            for (int i = 0; i < num; i++)
            {
                //var asmb = Assembly.LoadFrom("ReflectiveClassLibrary.dll");
                //Type type = asmb.GetType("ReflectiveClassLibrary.TestClass");
                Activator.CreateInstance(type);
            }
            watch1.Stop();
            label1.Text = "Activator.CreateInstance(Type type)时间:" + watch1.ElapsedMilliseconds + "毫秒";

            watch1.Reset();
            watch1.Start();
            for (int i = 0; i < num; i++)
            {
                Activator.CreateInstance("ReflectiveClassLibrary", "ReflectiveClassLibrary.TestClass");
            }
            watch1.Stop();
            label2.Text = "Activator.CreateInstance(string assemblyName,string typeName)时间:" + watch1.ElapsedMilliseconds + "毫秒";

            watch1.Reset();
            watch1.Start();
            for (int i = 0; i < num; i++)
            {
                //var asmb = Assembly.LoadFrom("ReflectiveClassLibrary.dll");
                //加载程序集
                asmb.CreateInstance("TestClass");
            }
            watch1.Stop();
            label3.Text = "assembly.CreateInstance(string typeName)时间:" + watch1.ElapsedMilliseconds + "毫秒";

            watch1.Reset();
            watch1.Start();
            for (int i = 0; i < num; i++)
            {
                //var asmb = Assembly.LoadFrom("ReflectiveClassLibrary.dll");
                //Type type = asmb.GetType("ReflectiveClassLibrary.TestClass");
                object obj = type.InvokeMember(null, BindingFlags.Public |
               BindingFlags.Instance | BindingFlags.CreateInstance, null, null, null);
            }
            watch1.Stop();
            label4.Text = "Type.InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args)时间:" + watch1.ElapsedMilliseconds + "毫秒";

            watch1.Reset();
            watch1.Start();
            for (int i = 0; i < num; i++)
            {
                //var asmb = Assembly.LoadFrom("ReflectiveClassLibrary.dll");
                //Type type = asmb.GetType("ReflectiveClassLibrary.TestClass");
                ConstructorInfo constructorInfo = type.GetConstructors()[0];
                constructorInfo.Invoke(null);
            }
            watch1.Stop();
            label5.Text = "ContructorInfo.Invoke(object[] parameters)时间:" + watch1.ElapsedMilliseconds + "毫秒";

        }

加载程序集、获取类型在循环内部时间如下(这时不同创建方式消耗时间差距比较小):

代码如下:

 public void TestCreateInstance()
        {
            Stopwatch watch1 = new Stopwatch();
            //var asmb = Assembly.LoadFrom("ReflectiveClassLibrary.dll");
            //Type type = asmb.GetType("ReflectiveClassLibrary.TestClass");
            int num = 100000;
            watch1.Start();

            for (int i = 0; i < num; i++)
            {
                var asmb = Assembly.LoadFrom("ReflectiveClassLibrary.dll");
                Type type = asmb.GetType("ReflectiveClassLibrary.TestClass");
                Activator.CreateInstance(type);
            }
            watch1.Stop();
            label1.Text = "Activator.CreateInstance(Type type)时间:" + watch1.ElapsedMilliseconds + "毫秒";

            watch1.Reset();
            watch1.Start();
            for (int i = 0; i < num; i++)
            {
                Activator.CreateInstance("ReflectiveClassLibrary", "ReflectiveClassLibrary.TestClass");
            }
            watch1.Stop();
            label2.Text = "Activator.CreateInstance(string assemblyName,string typeName)时间:" + watch1.ElapsedMilliseconds + "毫秒";

            watch1.Reset();
            watch1.Start();
            for (int i = 0; i < num; i++)
            {
                var asmb = Assembly.LoadFrom("ReflectiveClassLibrary.dll");
                //加载程序集
                asmb.CreateInstance("TestClass");
            }
            watch1.Stop();
            label3.Text = "assembly.CreateInstance(string typeName)时间:" + watch1.ElapsedMilliseconds + "毫秒";

            watch1.Reset();
            watch1.Start();
            for (int i = 0; i < num; i++)
            {
                var asmb = Assembly.LoadFrom("ReflectiveClassLibrary.dll");
                Type type = asmb.GetType("ReflectiveClassLibrary.TestClass");
                object obj = type.InvokeMember(null, BindingFlags.Public |
               BindingFlags.Instance | BindingFlags.CreateInstance, null, null, null);
            }
            watch1.Stop();
            label4.Text = "Type.InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args)时间:" + watch1.ElapsedMilliseconds + "毫秒";

            watch1.Reset();
            watch1.Start();
            for (int i = 0; i < num; i++)
            {
                var asmb = Assembly.LoadFrom("ReflectiveClassLibrary.dll");
                Type type = asmb.GetType("ReflectiveClassLibrary.TestClass");
                ConstructorInfo constructorInfo = type.GetConstructors()[0];
                constructorInfo.Invoke(null);
            }
            watch1.Stop();
            label5.Text = "ContructorInfo.Invoke(object[] parameters)时间:" + watch1.ElapsedMilliseconds + "毫秒";

        }

测试代码如下:c# 反射测试demo

12、序列化与反序列化

  • 相对于XML、二进制序列化方式,Protobuf效率较高,支持数据量较大
  • protobuf序列化后的大小是json的1/10,xml格式的1/20,是二进制序列化的1/10

C#  序列化

C# Protobuf-Net 序列化

13、数据压缩

在数据从客户端传输到服务端过程,为减少数据传输量,建议对数据进行压缩处理。

常用的数据压缩: C# 文件流压缩解压

14、

时间: 2024-08-03 11:31:16

C# 性能优化最佳实践的相关文章

【读书笔记】《Android应用性能优化最佳实践》

<第一行代码>读书笔记 一.引言 二.读书内容 书名:<Android应用性能优化最佳实践> 作者:罗彧成 (腾讯音乐Android开发总监) 出版社:机械工业出版社 封面: 三.书籍评价 四.个人心得 五.参考文档

高性能网站建设进阶指南:Web开发者性能优化最佳实践 pdf扫描版

高性能网站建设进阶指南:Web开发者性能优化最佳实践是<高性能网站建设指南>姊妹篇.作者Steve Souders是Google Web性能布道者和Yahoo!前首席性能工程师.在本书中,Souders与8位专家分享了提升网站性能的最佳实践和实用建议,主要包括:理解Ajax性能,编写高效的JavaScript,创建快速响应的应用程序.无阻塞加载脚本, 跨域共享资源,无损压缩图片大小,使用块编码加快网页渲染:避免或取代iframe的方法,简化CSS选择符,以及其他技术.性能是任何一个网站成功的关

前端性能优化最佳实践

最佳实践1:使用DocumentFragments或innerHTML取代复杂的元素注入 DOM操作在浏览器上是要付税的.尽管性能提升是在浏览器,DOM很慢,如果你没有注意到,你可能会察觉浏览器运行非常的慢.这就是为什么减少创建集中的DOM节点以及快速注入是那么的重要了. 现在假设我们页面中有一个<ul>元素,调用AJAX获取JSON列表,然后使用JavaScript更新元素内容.通常,程序员会这么写: Javascript代码 var list = document.querySelecto

何俊谈阿里巴巴前端性能优化最佳实践-笔记

网站页面前端优化对网站核心页面基于Wise load的原则做定点性能优化,减少HTTP请求,减少DNS请求时间,减少页面DOM的数量,做一些图片.JS压缩等.减少HTTP请求方案:阿里开发了自动合并CSS和JS静态文件的框架,对于减少页面DNS数方面采用前端延迟加载框架,主要负责页面加载时只加载首屏,用户滚动页面时才加载二屏或三屏,这样对网站的性能包括流量都是很大的提升和节约. Web I/O(高并发)方面的优化,使用高性能Web服务器,另外在冬天页面处理上,尽可能地减少冬天页面所占比例,采用一

Web前端优化最佳实践及工具集锦

前端的性能对于一个Web应用来说非常重要,如果一个Web应用的页面加载速度非常快.对于用户的操作可以及时响应,那么产品的用户体验将会极大地提升.下图显示了页面加载速度对于用户体验的影响. 你的Web页面的速度是否已经足够快了?其实可能还有很多可以提升的地方.Google和雅虎也提出了一些Web应用的前端优化建议,并发布了一些工具,你可以逐一检验你的Web应用,以便达到更高的性能. 这些优化不仅仅可以给用户提供更好的体验,从开发者角度来说,进行优化还可以减少页面的请求数.降低请求所占的带宽.减少资

一触即发——App启动优化最佳实践

一触即发 App启动优化最佳实践 文中的很多图都是Google性能优化指南第六季中的一些截图 Google给出的优化指南来镇楼 https://developer.android.com/topic/performance/launch-time.html 闪屏定义 Android官方的性能优化典范,从第六季开始,发起了一系列针对App启动的优化实践,地址如下: https://www.youtube.com/watch?v=Vw1G1s73DsY&index=74&list=PLWz5r

提高 Web 站点性能的最佳实践

本文内容 提高 Web 站点性能的最佳实践 最大限度减少 HTTP 请求 使用内容分发网络(CDN) 添加 Expires 或 Cache – Control 头 Gzip 组件 CSS 放在页面顶部 JavaScript 放在页面底部 避免 CSS 表达式 使用外部 JavaScript 和 CSS 减少 DNS 查询 精简 JavaScript 和 CSS 避免重定向 删除重复的脚本 配置 ETags 使得 Ajax 可缓存 尽早强制地发送缓冲给客户端 用 GET 发送 Ajax 请求 延迟

Web 前端优化最佳实践

面向内容的优化规则目前有 10 条. 1. 尽量减少HTTP请求 (Make FewerHTTPRequests) 作为第一条,可能也是最重要的一条.根据 Yahoo! 研究团队的数据分析,有很大一部分用户访问会因为这一条而取得最大受益.有几种常见的方法能切实减少HTTP请求: 1) 合并文件,比如把多个CSS文件合成一个: 2) CSSSprites 利用CSSbackground 相关元素进行背景图绝对定位:参见:CSS Sprites: Image Slicing’s Kiss of De

一触即发 App启动优化最佳实践

一触即发 App启动优化最佳实践 本文在 DiyCode 和 CSDN个人博客 同时首发,关注作者的 DiyCode帐号 或者 作者微博 可第一时间收到新文章推送. 文中的很多图都是Google性能优化指南第六季中的一些截图 Google给出的优化指南来镇楼 https://developer.android.com/topic/performance/launch-time.html 闪屏定义 Android官方的性能优化典范,从第六季开始,发起了一系列针对App启动的优化实践,地址如下: h