網誌

令大量圖片嘅代理回合保持喺記憶體範圍之內

正式環境入面有一個代理回合,喺裁剪六張目錄掃描圖入面嘅產品,並檢查嗰啲裁剪圖嘅時候,耗盡咗 952 MB 記憶體。問題唔係一個洩漏,而係三個。每次呼叫模型,都會以全尺寸重新傳送該回合嘅圖片;我哋用嚟呼叫模型嘅函式庫,會一直保留每個請求,直到回合結束;而代理框架嘅追蹤功能,亦會保留每次呼叫嘅輸入,包括圖片,直到整個回合完結。我哋三個都修正咗:圖片喺傳送之前會縮細,而每次呼叫嘅圖片,亦會喺呼叫完成後即時釋放。

代理點樣睇圖片

喺 Mythex 建構嘅時候,代理會喺你項目嘅雲端沙盒入面工作。佢可以睇圖片,例如你附加嘅截圖、設計稿、項目入面嘅一個檔案,或者佢自己剛生成嘅圖片,方法係將圖片連同對話一齊傳送俾模型。

回合係指代理因為你嘅一則訊息而做嘅所有工作。喺一個回合入面,代理會多次呼叫模型:思考、執行工具、睇結果、再思考。每次呼叫都會傳送到目前為止嘅對話,而當中包括該回合嘅圖片,圖片會以文字形式編碼(base64,令佢大咗約三分之一)。

令我哋當機嗰個回合,做嘅嘢係合理嘅。佢取咗六張目錄掃描圖,每張約 2.4 MB,裁剪出單一產品,然後不斷讀返啲裁剪圖嚟檢查。咁樣就係好多次模型呼叫,每次都帶住幾張大圖。

原因一:每次呼叫都重新傳送全尺寸圖片

每次模型呼叫都會用全尺寸圖片重新組裝請求內容,圖片於回合入面每次都係全尺寸。每次呼叫大約係 20 MB 嘅 base64。請求內容係喺代理記憶體入面組成一個字串,所以每次呼叫都會再配置多約 20 MB。

而且全尺寸傳送根本冇帶嚟任何好處。模型自己會將圖片縮到長邊約 1,300 像素,所以額外嘅像素只係增加記憶體同上傳時間。

修正:一次縮細,之後都傳送細版

  • 超過 400 KB 嘅圖片,傳送前會轉換成 JPEG,長邊最多 1,568 像素。細過咁嘅圖片則維持原樣。
  • 轉換使用 ImageMagick,每個 Mythex 沙盒都有,而且每個檔案只會執行一次。一個喺每次呼叫都顯示同一批掃描圖嘅回合,會重用縮細後嘅副本。快取以檔案嘅路徑、大小同修改時間作為鍵,所以被編輯過嘅檔案會重新轉換;快取亦有自己嘅大小上限,唔會變成另一個吃光記憶體嘅來源。
  • 單次模型呼叫中圖片嘅預算,由 16 MB 降到 6 MB。

同一六張掃描圖而家每次呼叫只需 4.4 MB,而唔係 19.7 MB。

原因二:模型客戶端從來唔釋放請求

每個請求本身嘅大小只係其中一部分。記憶體亦會隨著一次又一次嘅呼叫而增長。喺我哋嘅預備環境度,同樣類型嘅回合,即使做完完整嘅垃圾回收(即係所有無法到達嘅物件都已釋放嘅時間點),仍然佔住 369 MB 嘅活動記憶體。堆積取樣顯示,所有增長都發生喺模型客戶端函式庫編碼請求內容嘅部分。

原因係一個 abort 監聽器。當你撳 Stop,該回合嘅 abort signal 會被觸發,所有監聽緊佢嘅請求都會取消。我哋將該回合唯一嘅 signal 傳俾每個請求。模型客戶端函式庫會往俾佢嘅 signal 加一個「abort」監聽器,然後永遠唔移除。

所以,一個回合嘅每個請求,都會一直連繫住該回合嘅 signal,直至回合完結:監聽器持有請求嘅 controller,controller 持有請求,請求再持有整個請求內容,連同嗰啲圖片。

修正:每個請求一個 signal

而家每個請求都有自己嘅 signal。佢只會喺該請求運行期間連結到回合嘅 signal;如果係串流回應,就會一直到最後一個區塊被讀取為止,之後即時解除連結。Stop 仍然會觸達正在進行嘅請求,但回合嘅 signal 每次只會持有一個監聽器。

測試涵蓋咗上述三部分:

  • 12 次呼叫之後,回合嘅 signal 上冇任何監聽器殘留。修正之前,有 12 個。
  • 連續 15 個各 8 MB 嘅請求,記憶體保持平穩。修正之前,會增長 128 MB。
  • 按 Stop 仍然可以喺串流中途中止請求。

原因三:追蹤保留咗每次呼叫嘅輸入

堆積快照顯示第三個持有者:每次模型呼叫大約保留 6 MB。呢個來自代理框架嘅事件串流。佢會將回合入面每次模型呼叫記錄為回合嘅子項,連輸入都包括,直到回合結束為止。每次呼叫附加嘅每張圖片,都跟住留喺記憶體入面。

修正:呼叫返回後清空圖片

圖片只會附加喺為某次特定模型呼叫而建立嘅訊息上面。佢哋從來唔會儲存喺對話嘅已儲存狀態入面。所以呼叫返回之後,清空嗰啲訊息入面嘅圖片數據係安全嘅。追蹤會保留該呼叫嘅文字記錄,像素就會被丟棄。

有一個細節好關鍵:圖片必須就地清空。追蹤器可以透過一則訊息上面嘅多個參照,接觸到同一份圖片數據,所以如果只係換成一個新嘅空清單,原本嗰份(連同像素)仍然可以被到達。已儲存嘅對話狀態則從來唔會被修改。

有一個測試會喺同一個事件串流下運行代理嘅圖,每次呼叫都附帶一張 4 MB 圖片。冇修正之前,16 次呼叫之後記憶體增長咗 77 MB。有修正之後,記憶體保持平穩。

我哋學到嘅嘢

  • 記憶體問題可以分層出現。 三個獨立原因,每個都令圖片留喺記憶體太耐,三個都要修好,回合先可以保持喺記憶體範圍之內。
  • 交俾函式庫嘅任何東西都可能被保留。 Signal 同追蹤都比佢哋被交俾嘅呼叫存活得更久。當記憶體隨每次呼叫而增長,就要留意呼叫觸及咗邊啲長壽嘅物件。
  • 唔好傳送對方本來就會丟棄嘅嘢。 模型本身都會丟棄嘅像素,喺我哋每次呼叫時都佔用記憶體。
  • 直接測試記憶體。 每個修正都附有一個測試,量度堆積增長或監聽器數目,所以出現退步時,會以數字顯示,而唔係變成崩潰。

呢個係近期第二個有關代理記憶體嘅故事。第一個係一個進度標籤被重新排隊咗 41,000 次。如果你想了解代理係乜嘢,以及佢點樣運作,可以睇吓我哋嘅 AI 代理指南,同對話文件。

Mythex · Pricing · 用 AI 建造任何嘢 · 網站及應用程式範本 · Mythex 比較 · AI 應用程式開發詞彙表 · 免費 AI 應用程式開發工具 · English

English አማርኛ العربية Български বাংলা Bosanski Català Čeština Dansk Deutsch Ελληνικά Español Español (España) Eesti فارسی Suomi Français Français (Canada) ગુજરાતી हिन्दी Hrvatski Magyar Հայերեն Bahasa Indonesia Íslenska Italiano 日本語 ქართული Қазақ тілі ಕನ್ನಡ 한국어 Lietuvių Latviešu Македонски മലയാളം Монгол मराठी Bahasa Melayu မြန်မာ Norsk bokmål Nederlands ਪੰਜਾਬੀ Polski Português (Brasil) Português (Portugal) Română Русский Slovenčina Slovenščina Soomaali Shqip Srpski Svenska Kiswahili தமிழ் తెలుగు ไทย Tagalog Türkçe Українська اردو Tiếng Việt 简体中文 繁體中文(台灣)