原文網址:http://blog.oio.de/2014/03/28/gwt-rpc-deserialization-vs-ie8/
幾乎每個 web 開發人員都知道,當 JS 執行時間過久 browser 就會跳出討厭的對話框問你要不要取消。在 IE 上頭會顯示「這個網頁的指令碼造成 Internet Explorer 執行速度緩慢」。
這篇文章將說明 IE8 的特殊行為、它如何影響 GWT RPC 的 deserialize 機制、以及如何解決這些問題。
IE8 上的緩慢 JS
大多數 browser 在等待一段特定時間後(例如 5 秒),會顯示前面提到的訊息。這在 IE8 上有些不同,它判斷的依據是「執行 script 敘述的數量」,預設是 500 萬個敘述。
這爛爆了,因為 2014 年典型的桌機執行起來不到 1 秒,於是這就變成一個困擾。所以,比起其他 browser,IE8 更難避免跳出那個訊息。
GWT RPC 與緩慢的執行速度
在 GWT 中,我們有工具來避免這類問題。關鍵是 Scheduler class,這能夠讓我們在一個新的 JS 區段(scheduleDeferred
)執行程式碼,所以計數器會重置。即使 incremental execution 也是可以用 Scheduler
。
有某些情況下是沒辦法單純把一些程式碼延遲執行來避免這些問題。其中一種狀況是 GWT RPC 的 deserialize 行為。下列的特性會增加 deserialize 行為的程式數量:
- RPC 所要掌控的 class 數量
- data model 的複雜度
- 在一次 RPC 呼叫中傳輸的 data / object 數量
這裡有一些通用的建議可以協助優化 GWT RPC serialize 機制的效能:
- 盡可能使用最具體的 interface / class
- 不要在 GWT RPC 的參數、回傳值、或是內含的 field,用
Serializable
。 - 另一個例子是:要用
ArrayList
而不是用List
- 不要在 GWT RPC 的參數、回傳值、或是內含的 field,用
- 只傳輸你真正需要的資料(包含仔入其他資料所需的 ID)
有趣的組合:Java Collection + GWT RPC
有一類問題是 Java Collection API 所導致的。
我最近遇到 JS 在 deserialize 時執行緩慢的問題,傳輸的資料量小於 100KB。這是重度使用 HashSet
跟 HashMap
所造成的。在 deserialize 時會造成幾百萬行的 JS 敘述。
讓我們看看一個 Map 在 deserialize 時會發生什麼事:
public static void deserialize(SerializationStreamReader streamReader, Map instance) throws SerializationException {
int size = streamReader.readInt();
for (int i = 0; i < size; ++i) {
Object key = streamReader.readObject();
Object value = streamReader.readObject();
instance.put(key, value);
}
}
這段程式碼用了正規的 put()
來增加所有的轉換值。對 HashMap
而言會造成額外的 JS 敘述:
- 作為 key 值的 object 會呼叫很多次
hashCode()
/equals()
- 找到正確的地方來插入 entry 的代價是很昂貴的
你把越多 entry 加到 Map
中,要付出的代價就越高。這件事對 HashSet
也適用,因為它們使用相同的機制。
實際上,這些 Map
跟 Set
是用來優化存取多個 object 的狀況。解決方法是用 List
(就 deserialize 而言比較「便宜」)來傳輸資料,然後到了 client 再對應到最佳化的資料結構。
用 transient field 是一個好用的技巧,它會在傳輸 object 的時候隱藏起來不被處理:
private List<MyType> data;
private transient Map<MyKey, MyValue> optimizedData;
public Map<MyKey, MyValue> getOptimizedData() {
if(optimizedData == null) {
optimizedData = optimizeIt();
}
return optimizedData;
}
如此會讓 optimizedData
這個 field 不會被 serialize。在第一次存取資料時會作初始化的動作,然後 cache 起來留給後頭使用。
沒有留言:
張貼留言