如何加快 LibreOffice Calc 宏處理大型資料集的速度

LibreOffice Calc 巨集在處理大型工作表時經常會變慢,原因很簡單:巨集花在跨越 UNO 物件邊界上的時間比實際計算的時間要多。讀取一個儲存格、修改它、然後再寫回數千次,其開銷遠大於一次性移動更大區塊的相同資料。

目標並非抽像地追求每個宏的「速度」都「很快」。有效的最佳化應​​該在相同的資料集上產生相同的輸出,同時縮短運行時間,保持 Calc 的響應速度足以滿足工作流程的需求,並且避免引入新的正確性或記憶體問題。以下技巧著重於這些可衡量的結果。

截至 2026 年 10 月,LibreOffice SDK 文件已在 LibreOffice 26.8 API 參考中明確列出了此處使用的電子表格介面。這些核心方法是長期沿用的 UNO 接口,而非新引入的性能最佳化特性。

1. 在更改巨集之前先對其進行測量

首先要建立一個可重複的基準。 LibreOffice Basic 提供了GetSystemTicks()一個功能,官方幫助文件將其描述為測量系統運行時間並優化進程的方法。將你真正想要改進的巨集部分封裝起來,而不是測量工作簿開啟時間、使用者提示時間或無關的設定時間。

Dim t0 As Long, elapsed As Long

t0 = GetSystemTicks()

' Run the work you want to measure here.

elapsed = GetSystemTicks() - t0
MsgBox "Elapsed ticks: " & elapsed

對同一文件副本多次執行相同的操作。第一次運行的結果可能會因檔案系統快取、公式狀態或其他一次性操作而有所不同。請記錄資料集大小、處理範圍以及是否啟用了自動計算,以確保比較結果的有效性。

好的結果不僅僅是運行時間更短。也要確認行數、總計、公式、文字值、日期以及所有輸出範圍是否仍與基準值一致。

官方參考:LibreOffice 幫助:GetSystemTicks 函數。

2. 先移除逐個單元格的讀寫操作。

如果巨集循環遍歷一大片區域並反覆呼叫諸如 `move()` 之類的方法getCellByPosition(),那麼這種模式通常是首先要檢查的地方。 UNO API 公開了 `move()`XCellRangeData.getDataArray()和`move_range()` 方法,setDataArray()專門用於將整個單元格區域作為陣列移動。

官方 API 說明中指出,函數getDataArray()傳回一個行序列,每行包含若干列。傳回值可以是字串或雙精度浮點數。此函數setDataArray()會將一個陣列寫回指定範圍,且陣列的維度必須與該範圍相符。

例如,假設 A 列到 E 列包含巨集要轉換的輸入資料。與其逐一讀取每個單元格,不如一次選擇整個範圍:

Dim doc As Object
Dim sheet As Object
Dim sourceRange As Object
Dim data As Variant

doc = ThisComponent
sheet = doc.Sheets.getByName("Data")

' Example only: A2:E50001.
sourceRange = sheet.getCellRangeByPosition(0, 1, 4, 50000)
data = sourceRange.getDataArray()

在基本記憶體中進行處理data,然後將結果陣列一次寫入匹配的目標區域。與數千次單獨的讀寫操作相比,這大大減少了 UNO 呼叫次數。

官方參考資料:LibreOffice SDK:XCellRangeData和LibreOffice SDK:XCellRange。

3. 只處理你實際需要的範圍

批次存取數組固然有用,但將過大的工作表轉換為陣列只會將瓶頸轉移到記憶體。如果任務僅涉及特定表格,請避免硬編碼整列或整個 Calc 網格。

利用工作簿中已知的資訊(例如匯入行數、資料庫結果大小、指定的鍵列或應用程式建立的範圍)來確定最後一個有效行。然後僅請求巨集所需的行和列。

當工作表中包含大量格式信息,而實際數據卻被格式信息淹沒,或者舊內容佔用了大量內存空間時,這一點尤其重要。大型記憶體數組會消耗大量 RAM,一次過大的記憶體分配可能比處理適中的記憶體區塊更糟。

何時切換到分塊方法

如果單一陣列導致記憶體使用量急遽上升、處理開始前出現長時間停頓,或接近機器的實際處理極限,則應分塊處理資料。例如,先處理幾千行數據,寫入結果,然後再處理下一個資料區塊。沒有通用的理想資料區塊大小;請使用實際的工作簿和硬體進行測試。

4. 在繁重工作期間暫停螢幕更新通知

Calc 文件實作了 LibreOffice 的XModel介面。它的lockControllers()方法會暫停一些用於顯示更新的控制器通知,並在unlockControllers()巨集執行大量變更時恢復這些通知。這可以減少不必要的視覺操作。

Dim doc As Object
doc = ThisComponent

doc.lockControllers()

' Perform bulk changes here.

doc.unlockControllers()

官方 API 明確指出,控制器鎖定和解鎖操作必須成對進行。這意味著生產環境中的巨集應該包含錯誤處理機制,unlockControllers()即使處理失敗也能確保執行到位。文件保持鎖定狀態並非效能最佳化,而是清理路徑有缺陷。

官方參考:LibreOffice SDK:XModel。

5. 當公式在運行時占主導地位時,控制會自動重新計算。

如果巨集重複更改多個公式所依賴的儲存格,Calc 可能會花費大量時間重新計算,而巨集仍在更新中間值。電子表格文件公開了接口,XCalculatable包括isAutomaticCalculationEnabled()、、和。enableAutomaticCalculation()calculate()calculateAll()

更安全的做法是記住使用者目前的計算模式,僅在更新期間停用自動計算,更新後恢復原始狀態:

Dim doc As Object
Dim wasAutoCalc As Boolean

doc = ThisComponent
wasAutoCalc = doc.isAutomaticCalculationEnabled()

doc.enableAutomaticCalculation(False)

' Perform bulk updates.

doc.enableAutomaticCalculation(wasAutoCalc)

If wasAutoCalc Then
    doc.calculate()
End If

calculate()重新計算髒單元格。calculateAll()它會重新計算每個單元格,這本身可能很耗費資源,因此不要僅僅因為巨集運行完畢就自動調用它。僅當工作簿邏輯確實需要時才使用完全重新計算。

與控制器鎖定類似,應在錯誤清理路徑中恢復計算狀態。巨集不應在靜默的情況下將使用者的工作簿保持在手動計算模式。

官方參考:LibreOffice SDK:XCalculatable。

6. 安全地整合優化措施

最有效的結構通常是:確定一個精確的範圍,讀取一次,在記憶體中處理值,批量寫入結果,並且僅在該操作期間抑制不必要的 UI 或重新計算工作。

LibreOffice Basic 中一個實用的清理模式如下所示:

Sub ProcessLargeData
    Dim doc As Object
    Dim sheet As Object
    Dim dataRange As Object
    Dim data As Variant
    Dim wasAutoCalc As Boolean

    On Error GoTo CleanUp

    doc = ThisComponent
    sheet = doc.Sheets.getByName("Data")
    dataRange = sheet.getCellRangeByPosition(0, 1, 4, 50000)

    wasAutoCalc = doc.isAutomaticCalculationEnabled()

    doc.lockControllers()
    doc.enableAutomaticCalculation(False)

    data = dataRange.getDataArray()

    ' Transform data in memory here.
    ' Build an output array with dimensions matching its target range.

CleanUp:
    doc.enableAutomaticCalculation(wasAutoCalc)

    If doc.hasControllersLocked() Then
        doc.unlockControllers()
    End If

    If wasAutoCalc Then
        doc.calculate()
    End If
End Sub

範例中故意省略了轉換邏輯,因為這部分取決於您的工作簿。重要的最佳化在於工作流程的結構,而不是人為設定的業務計算。

7. 在重新實作函數之前,請考慮使用 Calc 的內建函數。

LibreOffice Basic 可以透過此服務呼叫 Calc 函數com.sun.star.sheet.FunctionAccess。如果您的巨集手動實現了 Calc 已提供的查找、聚合或其他操作,使用內建函數可以簡化程式碼並減少自訂循環。

這並不能保證每個內建函數都優於每個自訂演算法。請對實際工作負載進行基準測試。官方說明文件展示如何從 Basic 呼叫 Calc 函數,並指出函數名稱以英文提供。

官方參考:LibreOffice 幫助:在巨集中使用 Calc 函數。

如何判斷優化是否夠好

查看好兆頭何時改變策略
運行時重複跑動距離始終短於基線距離。大部分時間仍然花費在一個部分;請單獨分析該部分。
正確性總計、行數、公式和代表性記錄與原始巨集相符。數組轉換會改變資料類型或輸出維度。
記憶大批量採購可以輕鬆滿足需求,無需換貨或長時間的分配暫停。當全範圍數組過大時,請使用較小的資料塊。
重新計算公式計算在主更新之後執行一次,而不是每次寫入之後都執行。公式依賴關係需要中間計算值。
使用者介面行為文件在計算完成或失敗後恢復正常顯示和計算狀態。如果狀態可能被改變,則需要增加更強大的清理/錯誤處理機制。

常見的錯誤會抵銷收益

  • 明明只需要幾列數據,卻將整個工作表讀入記憶體。
  • getDataArray()仍然逐個單元格寫回每個結果。
  • 停用自動計算功能後忘記恢復先前的設定。
  • 鎖定控制器沒有可靠的清理路徑。
  • calculateAll()即使只有髒單元格需要重新計算,也在每個小批次處理後呼叫。
  • 僅憑單次運行結果判斷速度,而不是對相同數據進行重複測量。
  • 優化效能時沒有檢查輸出是否保持不變。

這些技巧何時失效了?

陣列傳輸主要降低電子表格物件的存取開銷,但並不能解決演算法本身固有的開銷問題。例如,即使所有值都已載入到記憶體中,對每一行進行嵌套比較仍然可能很慢。在這種情況下,需要改進演算法:使用字典或索引,只排序一次而不是重複排序,減少重複操作,或者在電子表格不再適合執行的情況下,將工作負載遷移到更合適的資料處理工具。

這同樣適用於以網路呼叫、資料庫查詢、檔案 I/O 或外部服務為主的巨集。首先要進行測量,以便優化實際的瓶頸,而不是只優化可見的電子表格循環。

推薦最佳化順序

  1. 使用可重複的資料集測量當前巨集。
  2. 將重複的儲存格讀取和寫入操作替換為範圍數組。
  3. 僅處理所需的行和列。
  4. 如果批次數組太大,記憶體使用不便,請使用分塊儲存。
  5. 在大批量生產過程中鎖定控制器,並確保清理工作。
  6. 當重複公式重新計算被證實為瓶頸時,暫時中止自動計算。
  7. 僅重新計算必要的量,恢復應用程式狀態,並驗證輸出。
  8. 重新評估。如果運行時間仍然主要由計算而非 UNO 存取造成,則應改進演算法。

對於大型 Calc 資料集,減少 Basic 物件和電子表格物件之間的呼叫次數通常是最重要的結構性改進。每一次額外的最佳化應​​視為需要衡量的指標,而不是例行公事。只有當巨集運行速度更快且仍然正確、可預測且易於維護時,它才真正有用。

留下評論

如何在 Microsoft Word 中移動圖片

如何在 Microsoft Word 中移動圖片

學習如何在 Word 中拖曳圖片、選擇文字環繞方式、將圖片與段落保持在一起或將其固定在頁面上,以及如何解決圖片無法移動的問題。

如何在 Excel 中插入複選框:兩種簡易方法

如何在 Excel 中插入複選框:兩種簡易方法

學習如何使用儲存格內工具在 Excel 中插入複選框,使用公式計算選取項目的數量,或在舊版桌面 Excel 中新增連結的表單控制項。

如何在 Excel 中自動填充字母:從 A 到 Z 及其他

如何在 Excel 中自動填充字母:從 A 到 Z 及其他

使用填充柄公式、SEQUENCE 或可重複使用的自訂清單在 Excel 中產生 A 到 Z、小寫字母或 Z 以上的標籤。

如何在 Excel 中新增刪除線:快捷鍵和逐步指南

如何在 Excel 中新增刪除線:快捷鍵和逐步指南

學習如何在 Windows、Mac 和網頁版 Excel 中新增刪除線、設定儲存格部分格式以及刪除線條而不刪除其值。

什麼是 Microsoft Access?它的資料庫工具是如何運作的?

什麼是 Microsoft Access?它的資料庫工具是如何運作的?

Microsoft Access 是一款 Windows 資料庫應用程序,用於儲存相關記錄、建立查詢、建立資料輸入表單和產生報表。了解它的工作原理以及適用場景。

如何在 Word 2010 中變更網格線高度和寬度

如何在 Word 2010 中變更網格線高度和寬度

了解如何在 Word 2010 中調整網格線高度和寬度、變更可見線頻率以及設定底層水平和垂直繪圖網格間距。

Office Outlook 2010 匯入和匯出:使用 PST 檔案移動數據

Office Outlook 2010 匯入和匯出:使用 PST 檔案移動數據

了解如何將 PST 檔案匯入 Outlook 2010 或匯出郵件、聯絡人、行事曆和任務。按照正確的選單操作,避免遺漏資料夾或重複文件。

使用 Nucleus Kernel 還原 Outlook PST 密碼

使用 Nucleus Kernel 還原 Outlook PST 密碼

使用 Kernel 的目前步驟還原忘記的 Outlook PST 密碼,包括如何識別正確的檔案、進行備份以及檢查工具的限制。

如何在 PowerPoint 幻燈片中插入網頁鏈接

如何在 PowerPoint 幻燈片中插入網頁鏈接

使用 LiveWeb 外掛程式將網站放置在 PowerPoint 投影片上,設定其大小和刷新行為,並在簡報前檢查相容性。

如何在 Word 2010 中變更預設字體和正文樣式

如何在 Word 2010 中變更預設字體和正文樣式

在 Word 2010 中設定預設字體、字號和文字效果,將選擇儲存到 Normal.dotm,然後修改 Normal 樣式以用於以後的文件。