2019年4月5日 星期五

HMI 回憶錄 (10)

沒有意外的話,這應該是這系列最後一篇。既然是完結篇,不拿點壓箱底的東西怎行?


奧義: 讀取最佳化

事先說明:本文提到的演算法,並未在本人服務過的公司使用過,相關的演算法實作也未參與,本文純粹是最近 1 年思索研究的心得總結。

什麼是讀取最佳化?一樣拿台達 DOPSoft 當範例,通訊參數設定如下:



上圖左下角有個「讀取最佳化」,它是指什麼呢?我們在畫面上擺 3 個數值顯示元件實驗看看:


再執行 Modbus RTU Slave 模擬軟體,觀察通訊行為:

 

實際的通訊行為呢?


從上圖可以看到 W40010, W40011, W40016 被合併成一個 command 送給 Modbus RTU Slave 模擬器:


這樣做有什麼好處?我們來看 Modbus Read Holding Registers protocol:

從上圖可以看到,讀取 1 word 的通訊成本是 19 bytes,19 bytes 除了上面提到的資訊外,包含 2 bytes CRC,Request/Response 的 3.5bytes delay(算 4bytes)。

如果 W40010, W40011, W40016 分 3 次讀取,就需要花費 19 + 4 + 19 + 4 + 19 = 65bytes,以 9600 none parity, 8 data bits, 1 stop bits 為例,則通訊時間

1/9600*10*65 = 68ms

而合併後只需要 32ms (1/9600*10*(27+4)),足足快了 50%!! 更不用說如果要讀取的位址一多會差到幾倍了!

總結讀取最佳化的目的:
  • 減少 round trip time(往返時間)
  • 盡可能一次讀取多筆資料,改善系統反應

本人的讀取最佳化演算法是一個 two pass 演算法:


Pass1

Pass1 很明顯可以看出來是合併鄰接位址,這部份在另外兩篇已經提過,相對 Pass2 還算簡單。

Pass2

Pass2 難點有兩個。第一,未使用區域上限為何?


未使用區域的讀取成本 + 下一個位址的讀取成本,不能超過單獨讀取一個位址的讀取成本(成本應包含末尾的 checksum、CRC,當然也不能超過 protocol spec),用圖形表示如下:


上面提過,讀取 1 個 Holding Register 需要 19bytes,因為 Holding Register 大小皆為 2bytes,所以最接近的未使用區域大小上限為 14 bytes (14 + Holding Register + 2bytes CRC =  18 bytes)。經由實測發現,DOPSoft 的作法比較保守,未使用區域只允許 8 bytes,也就是 4 Holding Registers。

筆者試算了一下,大概可以理解台達為何如此設計,假設欲讀取 Holding Register 位址如下
  • W40010
  • W40011
  • W40019
一次讀取花費時間:39ms
分兩次讀取花費時間:42ms

只得到 7% 的增益,但如果以 8 bytes 計算, W40019 換成 W40016,一次讀取花費時間為 32ms,得到 24% 的增益。以筆者的經驗,小於 10% 的改善很難有什麼感覺,這大概是為何台達取 8 bytes 的原因,8 bytes 剛好也是 14/2 最接近的值。

第二點就頭大了,如果你有一堆已經通過 Pass1 合併的位址:


如果這些未使用區域都沒有超過第一點的上限,但因為 protocol spec 的限制,是不可能一次全部讀回的,以 Modbus Holding Register 為例,位址範圍 64K words,protocol spec 限制一次最多只能讀回 125 words,所以問題是你要如何以 125 words 為上限對這些位址進行分組?

也許有人還不知道這個問題有多難,下面是一個簡化的例子,以 5 為區塊大小,3 個區塊就可以產生很多組合:


大家都知道排列組合的結果是階乘(factorial),要透過所有的排列組合找出最佳解,在空間與時間上會耗費巨大成本,更別提在嵌入式系統上幾乎不可行。這種涉及所有組合的問題常常是 NP complete。

拋開完美主義的心態,其實這時候只要能找出近似解就可以了(如同前面台達對未使用區域範圍的處理),使用貪心演算法(Greedy Method)就可以簡單處理這個問題:
  1. 找出最大的使用區塊
  2. 由這個最大區塊找出左邊/右邊最大能合併的位址
  3. 重複 1 直到處理完所有區塊

其他要注意的地方

TCP/IP based protocol

如果用  TCP/IP 通訊,就不用太在意未使用區域,以 Ethernet 來說 MTU(最大傳輸單元)是 1500bytes,即使未使用區域很大一塊也不用太在意,即使超出 MTU,以 Ethernet 至少 10Mbps - 100Mbps 的頻寬也沒什麼影響。

不可分割的一部分

有些區塊不能被分成兩次讀取,例如以 2 個 Holding Register 代表一個 IEEE754 32bits 浮點數,在優化時就不能加以切割。

 

尾聲

不只是 HMI,Modbus Gateway(還有一堆其他 xxx Gateway)也可以使用本文的演算法在 run-time 時優化通訊,某些廠牌還起了個什麼 Agent mode、intellegnt optimization 之類高大上的廣告詞,今天就來個大揭秘。

為何要寫下這一系列呢?

1993 年寫出毀滅戰士(DOOM)的首席工程師 John Carmack,在 1997 年就釋出 source code。當然筆者的成就與遠遠無法與 John Carmack 相比(他光靠 DOOM 就賺到法拉利還多到可以送人,筆者只有機車一台),但 John Carmack 認為 1993 年他開發出的技術遲早會有人學會跟上。

相比之下,筆者這點三腳貓功夫也沒什麼好弊帚自珍,還不如寫出來公諸於世,放下過去...

沒有留言:

張貼留言