2014年3月12日 星期三

【Java】位移運算...消失的位元...請不要強制讀取它!否則.....活不過七天

坦白說,在Java之下用位元運算的方式來處理「乘以二」這件事情,感覺不太出有明顯的效能差異。

(我用百億次的方式累積差異,但還是沒有明顯的差。)

但就算不討論效能上的差異,位元運算是很多工業用或商業用程式設計技巧的基礎,只是一直被JAVA(的標準教程)給忽略了。

原因或許不難理解,因為除非為了特殊理由,──例如加密──位元運算的意義在JAVA中很雞肋。



【以下開始為無厘頭的內容,有點像是我個人的自言自語。不建議點進來的人認真看待。反正知道Java中用位移方式做「乘以二」並沒有塊到哪裡去就好,願意自己做實驗更好。】

所以「講」一些很基本的東西好了...(這其實是筆記,並不是教材,路過看到的人...見笑了。)

byte值的上下限是-128~127。

問題是如果將127在加一,會如何呢?



位元運算的基礎原理就是在二進位的表示法中,將所有的1和0左右移動。

例如在二進位中的110其實是6,如果乘以二變成12,二進位表示法是1100。

它只是往左移動一位,並補上一個零。

如果是基數,例如7,二進位表示法111,除以二則是往右移位,變成011,但零可以省去,變成11。

結果...如果是乘法,或是往左位移,除了數字增加以外,還會增大這個數值得「單位」

例如本來是Integer長整數,往左移動幾次後就變成了long...



但有個問題是...如果將一個負數做往左或往右位移呢?

正數位移最後會變成0,但是負數卻會變成....-1。(1再往右位一,一樣會變成零。)



這就是上面要問將127在加一會如何的道理。

假設127是01111111,加一後會變成10000000,也就是-128。

不要懷疑!

10000000是負128。

也就是說00000001+11111111會得到00000000....(1消失了)......也就是零。

1加多少會是零?...當然是-1啊!

所以......-128不停的往右位移...不是補0,因為它從10000000到最後變成11111111。



(驚恐!!!!!!!)

除非編譯器的程式有特別判斷,這是否可以表示...往右位移其實是將「第一個位元繁殖」。

所以第一個位元若為0(同時表示這是個正數),所以往右位移後補上的是0,若為1(同時表示這是個負數),所以往右位移後補上的是1。

那往左位移呢?

除非程式又設定為「一律補零」,否則這是否表示其實在最右邊的位元之外,還有一個位元,只是設計者/使用者/人類永遠無法讀取修改它?



就好像七夜怪談中的錄影帶一樣......強制讀取的後果,蒼蠅會跑出來。

2014年3月10日 星期一

【Android】Activity中的Thread

一堆範例寫到Android中的Thread控制,都會寫一堆「怎麼把Thread關閉」的程式碼。

但其實...因為在Android中,有「Activity的生命週期」這件事,還有所謂的「APP之間的界線」,所以大家變得不太敢用一些理所當然的方法去操作Thread。



其實Thread並不會介意是哪個Activity在呼叫它、操縱它、管理它,就好像Thread根本不會介意設計者把操縱的程式寫在Activity之外。

把Thread的屬性設定為「public static」,然後大膽的關掉啟動它的Activity、並切換到另一個Activity。



程式意外的繼續順暢運作!

如果程式運作不順暢,絕對不是因為這樣切換Activity,更不是因為忘了把Thread關掉。



但...當你/妳確定這次按下「返回」後,APP的每個Activity都會退出執行狀態,那還是確保每個ˊThread都關掉吧!



講得很籠統?...連個程式碼都沒有,只有Thread/Activity/publi/static.....就別要求太多了。

(思考一下Thread設計的方式,思考一下Thread對各個Activity的意義。

如果是我,我會在Thread中留個Handler做為常數,讓各Activity不定時丟個Handler進去讓Thread來觸發/send......

如果是我,我會寫個統一的資料物件,讓各Activity去指揮Thread上網路接收資料,把資料轉入物件中,再讓Activity取用......

Google的Android API中,有些東西是寶,有些東西只有參考價值,但也有很多根本是渣。)

2014年2月18日 星期二

【Java】Make your object work better/fast...???

 That is my first English Blog post. And ,if you like to know, I never spend a day in school of Computer Science. So..... everything in this blog are amateur.
What is the different between those class???

If I use a loop to run a "new()" ... like new Test1(), or new Test2()... 100000 times, there are how much time they took to finish the loop.

Test1 :100 millisecond.
Test2:12000 millisecond.
Test3:34000 millisecond.

Obviously, more field the object has, more time it take to "new."

Is there more? Am I satisfied my own curiosity?

No! There is one more.


....... Interesting. They take the same time to finish the loop.

So the field make different, but method do nothing.



Wait!!!  There are more story about what happen when I use a infinity loop to test how many Test1/Test2/Test3 can a ArralyList<Test0> carry.......

Good night... it is 12 pm in Taiwan.

【Java】這樣設計物件有差喔!

差在哪裡?

我同樣用迴圈的方式放大其中的差異...在迴圈中執行「宣告動作」,──簡單來說,就是用類別 new 一個物件出來啦!

Test1執行的結果為100毫秒(大概的平均值)。

Test2執行的結果卻要12000毫秒。

Test3執行的結果更是增加到36000毫秒。

所以......愛護你/妳的程式、增加你/妳程式的效能,請從減少不必要的參數開始。

但萬一真的有必要,要使用「很多判斷式」.......把Test1/Test2/Test3改造成下列型式,然後參數規格都修改成一樣........


結果執行速度就差異不大...頂多1~2%,而且浮動性很高,極可能是「記憶體回收」或是其他干擾造成的。

重點在於:宣告階段影響程式效能的關鍵在「參數多寡」。



對!我聽到馬上有人質疑:反正一個物件宣告完後就擺在哪,何必計較宣告動作的效能呢?

今天假設是要做遊戲,比如某個關卡會有無限的「飛彈」、「小怪物」產生,「飛彈」不停被擊落、又不停射出,「小怪物」不停被打死、又不停被召喚........

當然可以把飛彈/怪物本身「被擊落與否」、「死亡與否」用純粹參數代替,(飛彈被擊落?重回原點等待發射,怪物死亡?重回原點等待召喚。)

如果採用這個做法,那確實沒有考量這點差異的必要。



但思考一下這個效能差異的由來......

雖然上面的附圖沒有截載,但關鍵在於記憶體。

記憶體配置?記憶體寫入?.......不管是哪一點,如果換個迴圈跑法,只是單純的做個ArrayList,然後一直塞物件進去、看記憶體何時爆掉........

Test3最快,Test2大約要Test3的三倍,Test1則是Test2的兩倍多一些......

還沒有機會測試使用了介面後的結果,但想必是相同的,因為使用介面根本不會有太多影響,介面(似乎)是類別的一部份,只會存在類別中,而不在物件存於記憶體的「標記/區段」上。

2014年2月11日 星期二

【JAVA】if...else if...else...判斷式,怎樣寫效能才會好?(3) 拿switch做比較

承續上一篇......我的「無聊神經」一旦開始運轉好像停不下來。

但是這次改用switch做比較。


我把這個迴圈用個函數包起來,a就是函數的傳入引數。

另外有一個函數在同樣的迴圈中寫入了「if...else...」,如下圖:




這兩個迴圈/函數執行的方式是......

run為switch,run1則是「if...else...」。為何要這樣做?run(1)和run(2)執行的結果不列入比較,原因看上一篇。剩下的.....做實驗當然要有充分的數據。

以下是結果...a是run函數輸出的,b則是run1......


先聲明:要使用switch或if來進行條件判斷,效能並不是優先考量......很多時候根本沒得選,因為switch只能判斷數值。(這算是「判斷」嗎?感覺跟分類沒兩樣。)

所以雖然效能明顯快了兩成,但其實只是作為一種比較而已。

2014年2月10日 星期一

【JAVA】if...else if...else...判斷式,怎樣寫效能才會好?(2)

上一篇,寫完後發現應該有個更客觀、更有準確的方法可以來測試,所以我先重新設計了作為基準/比對的「if...else...」,如下圖:




但跟上一篇不同的地方是我將它做成了一個函數...並且用以下的方法執行:


並且得到結果:

注意到了嗎...第1/2次的測試結果跟接下來的3/4/5/6差異過大。不管程式重複執行再多次也一樣。(這挺可怕的...雖然差異只是1~2%左右。)

不過這是插曲,接下來是我修改run()這個函數的內容的方法......


先聲明,用註解、或測底刪除程式,結果沒有明顯差異。測試結果如下圖.......




「if...else...」確實會明顯造成執行效能的差異。

這個測試法還有得搞.........(下一篇)

2014年2月7日 星期五

【JAVA】if...else if...else...判斷式,怎樣寫效能才會好?

「if」怎麼使用?

寫這個未免也太娘太小家子氣太學生口味了!(沒有瞧不起人的意思,感謝網路上無數的教學網站和教學文,但...你們還有需要加強的地方。)

參考一下我以前測試「效能」的方法,現在用來測下面這兩種「if」寫法在執行上會有多少效能差異......

IF1


IF2

上圖稱為IF1,下圖稱為IF2。因為我用個三層式的for迴圈來進行測試,所以會有i/j/k三個數值出現。

c1/c2的型態為long。看看他們被遞增的if條件式......是完全一樣的!也就是說如果c1被遞增、c2也會被遞增。唯一的差異就在於if...else的執行順序而已。

就理論上來說,「if」會被逐一執行,也就是說如果第一行if的條件吻合,則剩下的if都不會被執行,即使有條件更嚴謹、更精確、更穩和的。也就是說,理論上IF1執行起來會比較慢。

但另一個真正的問題是......要做if判斷,會需要耗掉多少資源。(沒辦法精準測量,只能取個概念。)

測試結果的彈性變化差異很大。



i/j/k的遞增極限都是1200,──k遞增到1200時會歸零,然後把j遞增1,j遞增到1200時也會歸零,然後把i遞增1......直到i也是1200,這個迴圈宣告結束。

limit1的值越小,IF1被執行到最後一行的機率就越高,理論上來說消耗的資源也越高。

當我把limit1定在400時,IF1執行完花了1769millisecond,IF2只花了836。

但當我把limit1定在800時,IF1執行完花了372millisecond,IF2只花了252。

當我再把limit1定在1000時,IF1執行完花了101millisecond,IF2只花了79。

最後但當我把limit1定在1200時,IF1執行完花了27millisecond,IF2只花了16。



這結果挺詭異的...我沒預想到,其實應該只要專注在小於800的數字就好。我擔心IF的條件太少會失去意義,但顯然我的IF條件太多...但又不夠多!(所以當limit1設在1200時,兩者的執行效率沒辦法達到一樣,因為我沒有精確的把所有i/j/k的狀況列入。)

可是結果肯定有差.......

如果寫了一長串的「if...else...」判斷式,然後又把常用的都擺在最後頭,程式的效能差異可以到達兩倍之多。(還沒有嘗試增加判斷式的難度會有何差別,那雖然不難.....但細節很多。)



這不單單只是效能測試而已!

程式碼光是簡潔還不夠,判斷式光是正確還不夠.......因為我們沒辦法預料這自己寫的判斷式會碰到什麼樣的資料/數據。我們沒辦法精準的保證「自己最前頭的判斷條件一定會被優先執行」。



所以會有使用函數並搭配return來控制函數執行的需要。不過這是改天再寫的了。

(但如果是我,我會用物件的多型性來取代這一長串判斷式。這有機會再寫的了。



點這是看續篇.......(我怎麼這麼無聊,連這種東西都寫續篇。)