2020中國 .NET開發者大會精彩回顧:葡萄城高效能表格技術解讀

powertoolsteam發表於2020-12-23

12月19日,2020中國 .NET開發者大會在蘇州召開。本次會議以“開源、共享、創新”為主題,結合線下、線上實時同步直播的方式,徵集了來自微軟、龍芯等知名企業的40餘位技術大咖,為50 餘萬名開發者帶來了近50場技術講座和 .NET應用實踐。

葡萄城的表格技術負責人王鴻先生,有幸作為本次大會的演講嘉賓,向在場的 .NET 開發者分享了葡萄城高效能表格技術調優方面的經驗積累。

image.png

王鴻,作為葡萄城表格技術的負責人,自 2014年起,便一直聚焦於企業高效能表格技術領域的研究,為葡萄城設計了全新的表格元件架構,並帶領研發團隊推出了一款效能在業界領先的電子表格元件GcExcel, 積累了大量高併發、高可用性表格元件的架構設計經驗。

image.png

在本次分享中,王鴻從葡萄城研發電子表格元件的背景與初衷出發,詳細對比了 Excel 與原生 C# 程式碼的讀取效能差異,並總結了若干針對prototype 原型進行效能調優的手段,如減少垃圾回收的影響、共享物件提升效能、壓縮資料降低記憶體、充分利用快取記憶體等方式。

以下是王鴻老師的主要分享內容:

1. 葡萄城研發電子表格元件的背景與初衷

早在30 多年前,電子表格就已經作為辦公軟體中的一個基礎功能套件,首次出現在個人電腦中。近些年,隨著網路資訊化的進一步加強,電子表格的應用越來越廣泛和深入。

如今“表格”也已經成為資料的一種重要表現形式,廣泛應用於各類桌面軟體、應用系統和 SaaS 平臺的儲存結構、系統構成中。人們已經習慣使用表格工具來處理財稅、金融、證券、保險、工業製造、物流倉儲等行業的大規模資料,其中典型代表包括微軟的 Excel、谷歌的 Spreadsheet,以及 WPS 等。

image.png

葡萄城,作為全球領先的開發技術提供商,很早便投入了研發精力,開拓並探索如何將電子表格以元件的方式嵌入到各類系統中。經過近 30 年的研究,葡萄城的表格技術已經實現了在保留使用者 Excel 使用習慣的同時,也能基於使用者的經驗和積累在業務系統中提供高效的資料處理和視覺化能力。

2. 通過 C# 程式碼,測試 Excel 檔案讀取的極限效能

電子表格的應用場景一般都較為複雜,開發實現它們會碰到很多技術難點,其中最為典型的便是效能問題。

葡萄城為實現高效能的表格元件,克服了很多效能挑戰:如怎樣快速開啟和儲存一個電子表格檔案、如何計算海量的公式函式、如何讓使用者快速完成大量單元格的值和樣式設定等。

為了測試 C# 程式碼對 Excel 檔案的讀取效能,王鴻老師選取了一個日常生活中很容易碰到的場景:當一個電子表格檔案很大的時候(包含30列、1,000,000行、30,000,000 個單元格資料),用Excel開啟它需要等待 34 秒。

image.png

如果使用者想要用更短的時間開啟這樣的大檔案時,有沒有辦法實現呢?答案是有,經過測試,用葡萄城的表格元件 GcExcel 開啟這樣一份檔案,僅需 12 秒。

image.png

3. 葡萄城表格技術優化:減少垃圾回收的影響

從Office 2007 開始,Excel 檔案就是一個標準的 Zip 檔案,對其解壓後,找到一個名為“Worksheets”的資料夾,在其中的“sheet1.xml”檔案中,存放了每個 Excel 檔案單元格對應的位置和值。

image.png

如何通過更高效的演算法取出這些位置和值,便是葡萄城表格技術的優化方向。對於一段未經優化的 C# 程式碼而言,把每個單元格的值讀出來,存放在一個List中,需要27秒。在這裡,我們僅僅將程式碼中的 object 改為 double,就可以讓其在20秒完成。

image.png

image.png

這 7秒差距,便是由於垃圾回收帶來的影響。在 List中有太多的object物件,這耗費了大量的垃圾回收時間,儘管它沒有被回收掉,但因為它們是object,所以在垃圾回收的過程中,需要不斷的檢測它們是否可以回收,將 object 改為 double,垃圾回收的時間便可以忽略不計。

葡萄城表格技術如何克服垃圾回收的影響?

  1. 消除單元格概念。因為單元格的數量太多了,保留這個概念就很難把object的數量降得很低。葡萄城把原來在單元格型別裡得資料分開處理,把樣式剔除出去另外處理,這裡只考慮單元格值。電子表格中單元格的值可能有四種型別:數字、文字、布林和錯誤,數字在內部都是用double型別來表示,布林和錯誤也可以用double來儲存。對於文字,不能用常規方法儲存,但我們可以想一個辦法,讓double也可以存文字,這個後面詳細講。總之,用double可以儲存所有的Excel資料,這樣我們就可以設計一個簡單單元格的資料物件,它是一個結構體,不是object。

  2. 行儲存改成列儲存。電子表格行的數量最大為2的20次方(約1048576),而列的數量最大為2的14次方(約16384),所以把行儲存改成列儲存,可以減少物件的數量。利用C# 的泛型,讓字典中儲存值型別資料。經過這樣一改造,object的數量會從九千多萬下降到一萬多,垃圾回收的影響基本忽略不計。

4. 葡萄城表格技術優化:共享儲存及樣式壓縮

所謂共享儲存,就是把整個軟體中共同的物件只生成一份,放在一個全域性的地方,每個物件用一個數字做ID,其它地方地方只存這個ID。以下面的文字為例,所有的文字集中儲存在一個表中,每個文字有一個唯一的ID,單元格中只儲存這個ID。

image.png

共享儲存有很多優點,對降低記憶體和提升效能有非常明顯的作用:

  1. 降低記憶體消耗。對一個資料量很大的軟體,會有很多資料都是重複的,比如電子表格中,如果文字的數量很大,那麼重複率就非常高,把這些文字集中儲存,相同的只存一份,你會發現,不同的文字數量其實很少,這會極大降低記憶體消耗。

  2. 提升效能。以對字串做查詢、替換為例,如果沒有共享集中儲存,必須要掃描所有的單元,對每一個單元格文字都做一次比較操作。但有了共享儲存,就只需要查詢“字典”就可以了;在字串比較時,只需要比較數字是否相等就可以了;在排序等操作中,只需先把表中的字串建立一個索引,排好序,後面需要時利用這個索引就可以了。

image.png

通過共享儲存,葡萄城表格技術已經把記憶體的消耗降低了很多,但我們還可以進一步優化。大家觀察這個圖,我們在每一個單元格儲存了一個數字,指向全域性樣式表的一格樣式物件。但我們發現,這些數字的重複率很高,因為電子表格中,一片區域的樣式往往是一樣的,針對這個特點,我們很容易就想到一個優化的方法。

我們不需要在每個單元格中儲存一個數字,而是建立圖上這樣一個表,表的一欄記錄一個區域(一個矩形),另一欄記錄這個區域對應的樣式的ID,通過這樣的方式,我們又可以把樣式的儲存空間降低很多。極端一點講,如果整個工作表都是同一種樣式,那麼我們這裡只需要儲存一個矩形和一個數字。

image.png

5. 葡萄城表格技術優化:充分利用快取記憶體

image.png

現在的CPU都有快取記憶體,它能提升CPU處理資料的效能。這個圖展示了不同儲存介質和CPU的關係:越高速的儲存介質離CPU更近,速度越快、空間更小、價格越貴。

CPU在讀取資料的時候,先從最近的快取中讀,只有沒命中,才從下一級快取中讀。因此,提升快取命中率對改善效能意義重大。

同時,我們還要清楚,當資料從較慢的儲存介質往較快的儲存介質複製時,不是依次進行,而是一塊一塊的複製,也就是說,當我們訪問記憶體中某一個資料時,它周圍相鄰的資料也一起讀入了離CPU最近的快取記憶體。因此,如果下一個時刻就讀相鄰的資料,便可以直接在快取中找到,讀取速度就會非常快。

知道了快取記憶體的原理,如何編寫快取友好的程式碼?

· 在元件設計的時候,選擇什麼樣的資料結構得注意,比如陣列和字典,受快取的影響可能不一樣。

· 在訪問資料的時候,要注意資料的儲存方式。

6. 葡萄城表格技術優化:其他實踐

除了上述優化方法,葡萄城的表格技術還有更多優化實踐。如建立Cache、使用基於集合的操作運算、利用SIMD計算大量資料等。

image.png

以上就是王鴻老師分享的主要內容,通過一個個鮮活的示例程式碼+時間對比,也讓大家對葡萄城高效能的表格技術形成了非常深刻的認識。

於此同時,王鴻老師在大會上分享的高效能表格技術,均已經實現落地。在前端,純前端表格控制元件 SpreadJS可針對Excel、Grid資料進行線上編輯、計算和展示;在後端,服務端表格元件 GrapeCity Documents for Excel (簡稱:GcExcel)可批量處理 Excel 文件,執行更高效的匯出與列印。

image.png

相關文章