C# 程序性能提升篇-2、类型(字段类型、class和struct)的错误定义所影响性能浅析

前景提要:

  编写程序时,也许你不经意间,就不知不觉的定义了错误的类型,从而发生了额外的性能消耗,从而降低了效率,不要说就发生那么一次两次,如果说是程序中发生了循环、网络程序(不断请求处理的)等这些时候,减少了不必要额外的消耗,使优化程序提高效率的一种途径。不仅跬步,无以至千里,不积小流,无以至江河。优化从点点滴滴做起。

一、问题抛出:

  大家先看这么一段定义

  class ReserveData 
  {
    public string ReserveId;  
    public string patient_id;
    public string patient_name; 
    public string queue_type_id;
    public string source_code;
    public string IsCall;  
    public string date;
    public string start_time;
    public string end_time;
    public string reserve_timespan_id;
}

  这是真实的摘自同事代码,先不说命名规则,Pascal、骆驼命名,(其中我们自己定义来自数据库使用骆驼,自定义变量使用Pascal)

  这段代码主要用途是向前台页面序列化ReserveData的List的json。

  以前我从没感觉类型定义会是什么问题,这个谁会出什么问题呢,可是有人真的就这么干,最后还不以为然。

二、问题解析一——字段类型定义

  在您这么定义的时候,您觉得可能这样定义并没有什么,甚至会说:“我都工作,两三年了,别对我指手画脚的”。

  一共工作过两家公司,均有一些工作两三年的同事,这样使用。偶的神啊!

  2.1 不同层面分析

    2.1.1 从程序员角度理解

      从程序员角度,我真的理解不了,你要表达的意思,明明是bool,一个true,false的意思,你变成string,datetime,也定义成string。  我去问他们,来了一句,“那怎么了,又不影响使用,你别纠结这些问题了,想想流程”。我不知该说什么。

    2.1.2 从机器CLR去理解

      确实,我够智能,你怎么着,我都给你正常编译、执行,但是正常干活已经够累的了,怎么还给我找事,告诉你,消耗cpu,内存过多,我就给你宕机去。

    2.1.3 实际使用中的我

       苦逼的我在前台js接收json时,针对bool怎么也是解析不出来,各种怪现象,看了源代码,有种想抽她的冲动,bool类型返回的是string类型的“True”,int32返回的也变成string类型数值,datetime类型也不是预想中的毫秒值,变成了string(2014/05/30 09:00:00),你说我能不抽她吗??

    2.1.4 性能分析

      现在咱们,就分析一下这段类型定义。

        在这其中 ReserveId 其实是 int32
        IsCall 其实是 bool
        start_time、end_time 其实是 Datetime
        patient_name 是 String 其他的也可能有别的类型,

      这里主要指出的不按真实情况,定义应有的类型,接收应有的数据,而自以为事的胡乱定义。第一、原本是值类型的,如果您定义成了string引用类型,可能造成不必要的装箱,性能损失。(补充一句,谢谢网友“ 冰麟轻武”提醒,这里使用类接收或者承载数据库数据之后,需要对其进行操作的,string 类型的Datetime,需要使用类型装换,同时还有一些其他的操作,“可能”会造成装箱,拆箱。可能使用Convert、(Int)强转、int.Parse()等类似 值类型→引用类型,引用类型→值类型) 第二、原本是引用类型的,如果您定义成了Int32、Boolean等值类型,可能直接就会有语法错误。第三,增加程序员之间的配合难度。以及会是程序中出现各种怪问题。

      装箱是很消耗性能的一种操作,这里不再详述:可参看:C# 程序性能提升篇-1、装箱和拆箱,枚举的ToString浅析

      实际事例:像这种List<ReserveData>,可能会有多条,每次都会进行装箱,就按本例使用示例分析一下性能问题(以实际现状分析,本人从事医疗辅助软件开发)
        (说明一下,一般医院给这种医疗辅助软件的服务器,就那么一台(四核八线程,4G内存),更有可能者,只给你一个虚拟机。web和DB在一台服务器,这还不止一个医疗辅助软件系统,还有其他的多媒体展示,或者其他医院系统)
        示例:某医院有一百个终端屏幕(小医院的点数,大医院会上千),其实就是请求客户端。每个终端屏幕每隔60s请求一次(这一般是最小的),甚至有的医院要求5s刷新,每次列表有10个患者。ok,这是实际情景
          咱们数学计算一下,预定义量,假设每次装箱耗时1ms、内存消耗1k,cpu使用0.001%
                (以上使用10个字段,8个可能发生装箱,每次list按10个计算)  
          粗略计算一下100个同时请求的即时消耗
            时间 100个屏幕*10个患者*8个装箱字段*1s时间*1ms额外消耗==8000ms =8s
            内存 100个屏幕*10个患者*8个装箱字段*1s时间*1K 额外消耗==8000K  =8M
            CPU  100个屏幕*10个患者*8个装箱字段*1s时间*0.001%额外消耗==8%

          这还仅仅是系统中的一角,如果有更多的这种额外消耗,那么程序就死定了

      综上所述,额外消耗(可能所有的基数并不准确,但是消耗是一定的,你懒了,机器多干活了,必然会有多的消耗(能量守恒定律码)),只不过是微乎其微的消耗;
        桌面程序,请求数少,可以忽略不计;
        局域网程序,像我们这种,频繁多请求,在服务器cpu、内存控制下,就得考虑额外消耗;
        互联网则更需要考虑,轻则上千,重则千万级别,再深了更别说了。这种额外的消耗(完全可以避免的消耗),并不在正常消耗之内的消耗,可能就是造成您的cpu,内存,居高不小的主要原因。

三、问题解析二——class和struct

  3.1概念简述

      3.1.1 不用多说,class 是引用类型,struct是值类型

      3.1.2 补充一句,引用类型开辟内存栈指针,内存堆存放数据,具体内存等资源释放由CLR的GC处理,偏重型;   值类型,存放于内存栈中,在作用域结束释放,轻型

      3.1.3 class与struct差不多一样,struct 是C、C++旧时代的产物,class才是王道,才是面向对象中使用的。C#中之所以有struct 是为了兼容C、C++程序员才过度的产物。估计很多您search过,都是这个答案吧。回忆,看过的教程、百度、google,博客园,甚至曾经的自己,都是这种想法。

    3.2什么时候使用类、什么时候使用struct  

      google一下就很多了,不在深入抄袭,但是看到以上类似3.1.3的话您就别看了,我给您补充一下:

        1.对于轻量级的数据组,类似上面的就是一些字段、属性的序列化,没有进一步抽象,继承的需求可以考虑使用Struct;
        2.struct  类型是一种值类型,通常用来封装小型相关变量组(MSDN:http://msdn.microsoft.com/zh-cn/library/ah19swz4.aspx)  
        3..结构用于封装由相关字段组成的组。因为结构是值类型,所以它们的分配效率要比类略高些(来自MSDN:http://msdn.microsoft.com/zh-cn/library/ms228600(v=vs.90).aspx)  
        4.结构还可以包含构造函数、常量、字段、方法、属性、索引器、运算符、事件和嵌套类型,但如果同时需要上述几种成员,则应当考虑改为使用类作为类型(MSDN:http://msdn.microsoft.com/zh-cn/library/ah19swz4.aspx)  
        5.类是反映现实事物的一种抽象,而结构体的作用只是一种包含了具体不同类别数据的一种包装,结构体不具备类的继承多态特性  综上所述,您也知道了吧,还是那句话,什么时候什么场景该用什么类型就要用什么类型。

小结:  
    1.字段类型的定义,一定要符合实际,不要随便胡乱定义。要不然可能造成的后果,别人不理解您的程序,机器损失巨大性能。  
    2.class,struct,合适场景用合适的定义,不经意间提升你程序的性能。  
    3.程序进步、优雅,技能同时也会再进步,难道money还会少吗??

上一篇:【转】【C#】C#性能优化总结


下一篇:UVALive 4992 Jungle Outpost(半平面交)