前段時間(2016年秋天),在開發下一版的1C:Enterprise技術平台時,開發團隊內部出現了支援新標準的問題
對於不知道的人來說,1C:Enterprise 是一個用於快速開發跨平台業務應用程式及其在不同作業系統和 DBMS 上執行的執行時間的環境。 一般來說,該產品包含:
應用伺服器叢集 ,在 Windows 和 Linux 上運行顧客 ,透過 http(s) 或其自己的二進位協定與伺服器一起工作,適用於 Windows、Linux、macOS網頁客戶端 ,在 Chrome、Internet Explorer、Microsoft Edge、Firefox、Safari 瀏覽器中運行(以 JavaScript 編寫)- 開發環境(
配置器 ),適用於 Windows、Linux、macOS 管理工具 應用程式伺服器,在 Windows、Linux、macOS 上運行手機客戶端 ,透過http(s)連接到伺服器,適用於運行Android、iOS、Windows的行動設備行動平台 — 一個用於建立離線行動應用程式的框架,具有同步能力,在 Android、iOS、Windows 上運行- 開發環境
1C:企業開發工具 ,用Java編寫 - 服務器
互動系統
我們盡可能嘗試為不同的作業系統編寫相同的程式碼——伺服器程式碼庫是 99% 通用的,客戶端程式碼庫是大約 95% 的。 1C:Enterprise技術平台主要用C++編寫,大致的程式碼特徵如下:
- 10萬行C++程式碼,
- 14個文件,
- 60萬個班級,
- 五十萬種方法。
所有這些東西都必須翻譯成 C++14。 今天我們將告訴您我們是如何做到這一點以及我們在過程中遇到了什麼。
免責聲明
下面寫的所有關於慢/快工作、(而不是)各種庫中標準類別的實作所消耗的大量記憶體都意味著一件事:這對我們來說是正確的。 標準實現很可能最適合您的任務。 我們從自己的任務開始:我們取得客戶的典型數據,對其運行典型場景,查看效能、消耗的記憶體量等,並分析我們和客戶對這些結果是否滿意。 他們的行動取決於。
我們擁有什麼
最初,我們使用 Microsoft Visual Studio 編寫 1C:Enterprise 8 平台的程式碼。 這個專案始於 2000 年代初,我們有一個僅限 Windows 的版本。 自然,此後程式碼一直在積極開發,許多機制已被完全重寫。 但程式碼是按照1998年的標準寫的,例如我們的右尖括號之間用空格分隔,這樣編譯就能成功,如下圖:
vector<vector<int> > IntV;
2006年,隨著平台版本8.1的發布,我們開始支援Linux並切換到第三方標準庫
我們的字串基於 2000 年代初期表達的字串優化思想
我們的生產線使用了兩種主要的最佳化技術:
- 對於短值,使用字串物件本身的內部緩衝區(不需要額外的記憶體分配)。
- 對於所有其他的,都使用力學
寫時複製 。 字串值儲存在一處,並且在賦值/修改期間使用引用計數器。
為了加快平台編譯速度,我們從 STLPort 變體(我們沒有使用)中排除了流實現,這使我們的編譯速度提高了約 20%。 隨後我們不得不有限地使用
第三條路
當轉向 C++14 標準時,我們考慮了以下選項:
- 將我們修改的STLPort升級到C++14標準。 這個選擇非常困難,因為... 對 STLPort 的支援已於 2010 年停止,我們必須自行建置所有程式碼。
- 轉換到與 C++14 相容的另一個 STL 實作。 非常希望此實作適用於 Windows 和 Linux。
- 針對每個作業系統進行編譯時,請使用對應編譯器內建的函式庫。
由於工作量太大,第一個選項被徹底拒絕。
我們考慮了第二種選擇一段時間; 被視為候選人
而我們選擇了第三條路。
過渡
因此,我們必須將 STLPort 的使用替換為對應編譯器的函式庫(適用於 Windows 的 Visual Studio 2015、適用於 Linux 的 gcc 7、適用於 macOS 的 clang 8)。
幸運的是,我們的程式碼主要是根據指南編寫的,沒有使用各種巧妙的技巧,因此在腳本的幫助下,遷移到新庫的過程相對順利,這些腳本替換了源代碼中的類型、類別、命名空間和包含的名稱檔案。 遷移影響了 10 個來源檔案(總共 000 個)。 wchar_t 被替換為 char14_t; 我們決定放棄使用 wchar_t,因為char000_t 在所有作業系統上都佔用 16 個字節,並且不會破壞 Windows 和 Linux 之間的程式碼相容性。
有一些小冒險。 例如,在 STLPort 中,迭代器可以隱式轉換為指向元素的指針,並且在我們的程式碼中的某些地方使用了這一點。 在新的圖書館中,不再可能這樣做,必須手動分析和重寫這些段落。
這樣,程式碼遷移就完成了,程式碼是針對所有作業系統編譯的。 是時候進行測試了。
轉換後的測試顯示,與舊版的程式碼相比,效能下降(在某些地方高達 20-30%),記憶體消耗增加(高達 10-15%)。 這尤其是由於標準字串的效能不佳所致。 因此,我們再次不得不使用我們自己的、稍作修改的生產線。
也揭示了嵌入式庫中容器實現的一個有趣功能:內建庫中的空(無元素) std::map 和 std::set 分配記憶體。 並且由於實現特性,在程式碼中的某些地方創建了相當多的這種類型的空容器。 標準記憶體容器為一個根元素分配了一點,但對我們來說這至關重要 - 在許多場景中,我們的效能顯著下降,記憶體消耗增加(與 STLPort 相比)。 因此,在我們的程式碼中,我們將內建程式庫中的這兩類容器替換為 Boost 中的實現,而這些容器沒有此功能,這解決了速度變慢和記憶體消耗增加的問題。
正如在大型專案中進行大規模更改後經常發生的那樣,原始程式碼的第一次迭代並不是沒有問題的,特別是在這裡,Windows 實作中對偵錯迭代器的支援派上了用場。 我們一步步向前推進,到2017年春天(版本8.3.11 1C:Enterprise)遷移完成。
結果
過渡到 C++14 標準花了大約 6 個月的時間。 大多數時候,一名(但非常合格的)開發人員負責該項目,並在最後階段負責特定領域的團隊代表加入 - UI、伺服器叢集、開發和管理工具等。
這項轉變極大地簡化了我們遷移到最新版本標準的工作。 因此,版本1C:Enterprise 8.3.14(正在開發中,計劃於明年初發布)已轉移到標準
遷移後,開發人員有更多選擇。 如果早些時候我們有自己的STL 修改版本和一個std 命名空間,那麼現在我們在std 命名空間、stdx 命名空間中擁有來自內置編譯器庫的標準類- 我們的線路和容器針對我們的任務進行了優化,在boost 中 -最新版本的提升。 開發人員使用那些最適合解決他的問題的類別。
移動建構函數的「本機」實作也有助於開發(
美中不足
也許遷移最令人不快(但不是關鍵)的後果是我們面臨數量的增加
來源: www.habr.com