前景提要:
編寫程序時,也許你不經意間,就不知不覺的定義了錯誤的類型,從而發生了額外的性能消耗,從而降低了效率,不要說就發生那么一次兩次,如果說是程序中發生了循環、網絡程序(不斷請求處理的)等這些時候,減少了不必要額外的消耗,使優化程序提高效率的一種途徑。不僅跬步,無以至千里,不積小流,無以至江河。優化從點點滴滴做起。
一、問題拋出:
大家先看這么一段定義
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還會少嗎??