MNN LLM Chat iOS 流式輸出優化實踐

圖片

本文介紹了在 iOS 平臺上使用 MNN 框架部署大語言模型(LLM)時,針對聊天應用中文字流式輸出卡頓問題的優化實踐。通過分析模型輸出與 UI 更新不匹配、頻繁刷新導致性能瓶頸以及缺乏視覺動畫等問題,作者提出了一套包含智能流緩沖、UI 更新節流與批處理、以及打字機動畫渲染的三層協同優化方案。最終實現了從技術底層到用戶體驗的全面提升,讓本地 LLM 應用的文字輸出更加絲滑流暢,接近主流在線服務的交互體驗。

圖片

背景

在iOS端部署大語言模型(LLM) 聊天應用時,用戶體驗的流暢性是一個關鍵要素。MNN LLM iOS應用基于MNN推理框架,為用戶提供本地化的AI對話體驗。如果直接將模型的輸出更新到回答的頁面UI中,會有一個嚴重影響用戶體驗的問題:模型輸出文字時存在明顯的卡頓現象,文字顯示生硬,缺乏自然的流動感。

因為用戶已經習慣了ChatGPT、Qwen等在線服務提供的流暢回復和絲滑打字機效果。本地模型推理輸出沒有網絡延遲,如果直接將模型結果輸出,在用戶體驗上會大打折扣。所以我針對這個問題,進行了優化。本文將分析具體的問題,針對這些問題提出解決方法,并且詳細的講解具體的原理和實現。

我們先看看優化前的直接輸出:

再看看優化之后的效果:

流暢度有明顯的提升。

完整的項目地址如下:https://github.com/alibaba/MNN/blob/master/apps/iOS/MNNLLMChat/README.md


問題分析

通過輸出現象分析,可以識別出導致卡頓和生硬輸出的三個核心問題:

1. 模型輸出速度與UI更新頻率不匹配

  • 現象:模型推理速度較快,但輸出內容會積累后批量更新UI。

  • 原因:缺乏合適的緩沖機制,導致"要么不更新,要么大量更新"的極端情況。

2. UI刷新頻率過高造成性能瓶頸

  • 現象:本地模型快速推理輸出,會引起頻繁的UI更新導致主線程壓力過大,出現卡頓和掉幀。

  • 原因:每個字符都觸發獨立的UI更新,沒有合理的批處理機制。

3. 缺乏流式輸出的視覺動畫效果

  • 現象:文字瞬間出現,缺乏漸進式的視覺反饋。

  • 原因:沒有展示類似打字機的逐字符顯示動畫。


優化策略

在Chat應用回答的過程中, 數據流向如下:

原始輸出流 → 智能緩沖 → 批量更新 → 動畫渲染 → 用戶界面。

基于上面的數據流和優化需求,我們在可以進行后面三層協同優化策略:


???1.?底層流緩沖優化 (OptimizedLlmStreamBuffer)

職責:解決模型輸出與UI更新的頻率不匹配問題。

  • 智能觸發機制:基于內容特征(標點符號)和緩沖閾值的雙重觸發;

  • 標點符號觸發:中英文支持,完整的UTF-8 Unicode標點符號識別;

  • 性能優化:預分配內存,減少重分配開銷。

???2. 中間層更新優化 (UIUpdateOptimizer)

職責:統一管理UI更新請求,實現批處理和節流。

  • 雙重策略:批量觸發(5個更新)+ 時間觸發(30ms超時);

  • 線程安全:基于Swift Actor模型的并發處理;

  • 智能調度:自動取消重復任務,避免資源浪費。

???3. UI層動畫增強 (LLMMessageTextView)

職責:提供自然流暢的用戶視覺體驗。

  • 條件化動畫:判斷是否需要啟用打字機效果;

  • 流式適配:完美適配流式輸出的文本變化;

  • 資源管理:自動清理動畫資源,防止內存泄漏。

最終,我們通過底層增加緩沖輸出,中層合并更新請求,UI層提供視覺緩沖——這三層配合實現了從技術優化到體驗優化的完整覆蓋,提升整體性能和體驗效果。


詳細技術實現


???1. OptimizedLlmStreamBuffer:智能流緩沖優化


  • 1.1 原理

OptimizedLlmStreamBuffer?是對標準?std::streambuf?的增強實現,通過智能緩沖策略解決模型輸出與UI更新的頻率不匹配問題。它的工作原理是在模型輸出和UI更新之間建立一個緩沖層,根據內容特征和緩沖大小決定何時將累積的內容推送給UI。


  • 1.2 類設計

class?OptimizedLlmStreamBuffer?:?public?std::streambuf {private:? ??static?const?size_t?BUFFER_THRESHOLD =?64; ? ? ? ? ?// 緩沖區閾值(字節)? ? std::string buffer_; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?// 內容緩沖區public:? ??using?CallBack = std::function<void(const?char* str,?size_t?len)>;?// 更新回調? ??OptimizedLlmStreamBuffer(CallBack callback);protected:? ??virtual?std::streamsize?xsputn(const?char* s, std::streamsize n)?override;
private:? ??void?flushBuffer(); ? ?// 刷新緩沖區? ??bool?checkForFlushTriggers(const?char* s, std::streamsize n);?// 檢查觸發條件? ??bool?checkUnicodePunctuation(); ?// Unicode標點檢測};

  • 1.3 方法詳解

1.?xsputn方法 - 數據流入口

下面是整體方法流程,每當模型生成新內容時都會調用此方法:

virtual?std::streamsize?xsputn(const?char* s, std::streamsize n)?override?{? ??if?(!callback_ || n <=?0) {? ? ? ??return?n;?// 參數校驗,確保安全性? ? }try?{? ? ? ??// 步驟1: 將新數據追加到緩沖區? ? ? ? buffer_.append(s, n);// 步驟2: 判斷是否需要立即刷新? ? ? ??const?size_t?BUFFER_THRESHOLD =?64;? ? ? ??bool?shouldFlush = buffer_.size() >= BUFFER_THRESHOLD;// 步驟3: 如果大小未達標,檢查內容特征? ? ? ??if?(!shouldFlush && n >?0) {? ? ? ? ? ? shouldFlush =?checkForFlushTriggers(s, n);? ? ? ? }// 步驟4: 符合條件則刷新緩沖區? ? ? ??if?(shouldFlush) {? ? ? ? ? ??flushBuffer();? ? ? ? }return?n;? ? }?catch?(const?std::exception& e) {? ? ? ??NSLog(@"Error in stream buffer: %s", e.what());? ? ? ??return?-1;?// 異常處理,確保程序穩定性? ? }}

工作流程說明:

  • 數據接收:模型每次輸出的文本片段進入緩沖區;

  • 閾值判斷:當累積

  • 內容達到64字節時立即輸出;

  • 自動觸發:即使未達到閾值,遇到標點符號

  • 也會觸發輸出;

  • 異常處理:完善的錯誤處理機制保證系統穩定性。

2. 觸發機制

  • 閾值觸發策略

const?size_t?BUFFER_THRESHOLD =?64;?// 積累 64 byte 內容才輸出
  • ASCII標點符號觸發

bool?checkForFlushTriggers(const?char* s, std::streamsize n)?{? ??char?lastChar = s[n-1];?// 獲取最后一個字符// 檢查常見的英文標點符號? ??if?(lastChar ==?'\n'?|| ?// 換行符 - 句子結束? ? ? ? lastChar ==?'\r'?|| ?// 回車符 - 兼容不同系統? ? ? ? lastChar ==?' '?|| ??// 空格 - 詞語分隔? ? ? ? lastChar ==?'\t'?|| ?// 制表符 - 格式化字符? ? ? ? lastChar ==?'.'?|| ??// 句號 - 句子結束? ? ? ? lastChar ==?','?|| ??// 逗號 - 語句停頓? ? ? ? lastChar ==?';'?|| ??// 分號 - 語句分隔? ? ? ? lastChar ==?':'?|| ??// 冒號 - 說明引導? ? ? ? lastChar ==?'!'?|| ??// 感嘆號 - 情感表達? ? ? ? lastChar ==?'?') { ??// 問號 - 疑問句結束? ? ? ??return?true;? ? }return?checkUnicodePunctuation();?// 繼續檢查Unicode標點}

觸發邏輯說明:

  • 語義完整性:在語義完整的點進行輸出,提升閱讀體驗

  • 視覺節奏:模擬人類閱讀時的自然停頓

  • 跨語言支持:同時支持英文和中文的標點符號

  • Unicode標點符號檢測

中文標點符號采用UTF-8編碼,需要特殊處理:

bool?checkUnicodePunctuation()?{? ??if?(buffer_.size() >=?3) {?// UTF-8中文標點通常占3字節? ? ? ??const?char* bufferEnd = buffer_.c_str() + buffer_.size() -?3;// 定義中文標點符號的UTF-8編碼? ? ? ??static?const?std::vector<std::string> chinesePunctuation = {? ? ? ? ? ??"\xE3\x80\x82", ? ??// 。(句號) - 句子結束? ? ? ? ? ??"\xEF\xBC\x8C", ? ??// ,(逗號) - 語句停頓 ?? ? ? ? ? ??"\xEF\xBC\x9B", ? ??// ;(分號) - 語句分隔? ? ? ? ? ??"\xEF\xBC\x9A", ? ??// :(冒號) - 說明引導? ? ? ? ? ??"\xEF\xBC\x81", ? ??// !(感嘆號) - 情感表達? ? ? ? ? ??"\xEF\xBC\x9F", ? ??// ?(問號) - 疑問句結束? ? ? ? ? ??"\xE2\x80\xA6", ? ??// …(省略號) - 語意延續? ? ? ? };// 逐一比較字節序列? ? ? ??for?(const?auto& punct : chinesePunctuation) {? ? ? ? ? ??if?(memcmp(bufferEnd, punct.c_str(),?3) ==?0) {? ? ? ? ? ? ? ??return?true;?// 找到匹配的中文標點? ? ? ? ? ? }? ? ? ? }? ? }// 檢查2字節的Unicode標點(如破折號)? ??if?(buffer_.size() >=?2) {? ? ? ??const?char* bufferEnd = buffer_.c_str() + buffer_.size() -?2;? ? ? ??if?(memcmp(bufferEnd,?"\xE2\x80\x93",?2) ==?0?|| ?// – (短破折號)? ? ? ? ? ??memcmp(bufferEnd,?"\xE2\x80\x94",?2) ==?0) { ?// — (長破折號)? ? ? ? ? ??return?true;? ? ? ? }? ? }return?false;}

UTF-8編碼處理細節:

  • 字節序列識別:通過比較字節序列精確識別中文標點

  • 長度適配:中文標點占2-3字節,需要相應的緩沖區長度檢查

  • 性能優化:使用靜態數組和memcmp進行高效比較

3. 內存預分配
OptimizedLlmStreamBuffer(CallBack?callback) :?callback_(callback) {? ? buffer_.reserve(1024);?// 預分配1KB內存}
  • 減少重分配:避免頻繁的內存分配和拷貝操作

  • 提升性能:預分配內存可以減少約30%的內存操作開銷

1)std::string 在動態增長時,每次容量不足都會:

  • 分配新的更大內存空間(通常是當前容量的1.5-2倍)

  • 復制現有數據到新內存

  • 釋放舊內存

// 沒有預分配的情況下,字符串增長模式:// 容量: 0 -> 1 -> 2 -> 4 -> 8 -> 16 -> 32 -> 64 -> 128 -> 256 -> 512 -> 1024// 重分配次數: 約10次// 預分配1024字節后:// 容量: 1024 (一次分配)// 重分配次數: 0次 (在1024字節內)

2)與緩沖策略的協同

C++const?size_t?BUFFER_THRESHOLD =?64;bool?shouldFlush = buffer_.size() >= BUFFER_THRESHOLD;
  • 緩沖閾值:64字節觸發刷新

  • 預分配容量:1024字節

  • 協同效果:支持16次緩沖操作而無需重分配

因此我們預分配1024字節避免了前期的多次重分配操作。

4. 異常安全設計

~OptimizedLlmStreamBuffer() {? ??flushBuffer();?// 析構時確保緩沖區內容全部輸出}void?flushBuffer() {? ??if?(callback_ && !buffer_.empty()) {? ? ? ??callback_(buffer_.c_str(), buffer_.size());? ? ? ? buffer_.clear();?// 清空緩沖區,釋放內存? ? }}

???2. UIUpdateOptimizer:基于Actor的更新優化器


  • 2.1 原理

UIUpdateOptimizer?采用Swift 5.5引入的Actor并發模型,解決UI更新的線程安全和性能問題。它的核心思想是將頻繁的UI更新請求按緩存大小或間隔時間進行批處理和節流,減少主線程壓力。

Actor?隊列(批處理 + 節流) -> 主線程UI(低頻率UI更新 )

  • 2.2 類設計

actor?UIUpdateOptimizer?{? ??static?let?shared =?UIUpdateOptimizer()?// 全局單例// 狀態管理? ??private?var?pendingUpdates: [String] = [] ? ?// 待處理更新隊列? ??private?var?lastFlushTime:?Date?=?Date() ? ??// 上次刷新時間? ??private?var?flushTask:?Task<Void,?Never>? ? ?// 延遲刷新任務// 配置參數? ??private?let?batchSize:?Int?=?5? ? ? ? ? ? ? ?// 批處理大小? ??private?let?flushInterval:?TimeInterval?=?0.03?// 節流間隔(30ms)}

簡單介紹一下 Actor。在多線程或異步程序中,多個任務訪問共享變量時容易造成數據競爭(data race)。Actor 是一種引用類型,用來保護其內部狀態免受數據競爭影響。它是并發安全的,當你調用時,會自動對外部訪問進行同步(串行隊列),所以不需要手動加鎖。


  • 2.3 方法詳解

1. 雙重觸發策略

func?addUpdate(_ content:?String, completion:?@escaping?(String) -> Void) {? ??// 步驟1: 添加到待處理隊列? ? pendingUpdates.append(content)// 步驟2: 判斷觸發條件? ??let?shouldFlushImmediately = pendingUpdates.count?>= batchSize ||? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?Date().timeIntervalSince(lastFlushTime) >= flushInterval// 步驟3: 選擇處理策略? ??if?shouldFlushImmediately {? ? ? ??flushUpdates(completion: completion)?// 立即處理? ? }?else?{? ? ? ??scheduleFlush(completion: completion)?// 延遲處理? ? }}

策略選擇邏輯:

2. 延遲刷新調度

private?func?scheduleFlush(completion:?@escaping?(String) -> Void) {? ??// 取消之前的調度,避免重復執行? ? flushTask?.cancel()// 創建新的延遲任務? ? flushTask =?Task?{? ? ? ??// 等待指定時間間隔? ? ? ??try??await?Task.sleep(nanoseconds:?UInt64(flushInterval *?1_000_000_000))// 檢查任務是否被取消,以及是否有待處理內容? ? ? ??if?!Task.isCancelled?&& !pendingUpdates.isEmpty?{? ? ? ? ? ??flushUpdates(completion: completion)? ? ? ? }? ? }}

上面的方式,可以:

  • 節流控制:為UI更新提供30毫秒的緩沖時間;

  • 批處理優化:在這30毫秒內如果有新的更新到來,會取消當前延遲任務并重新開始計時;

  • 性能平衡:既避免過于頻繁的UI更新,又保證內容能及時顯示;

  • 響應性保證:即使在低頻更新場景下,也確保內容在30毫秒內顯示給用戶。

3. 批處理執行

private?func?flushUpdates(completion:?@escaping?(String) -> Void) {? ? guard !pendingUpdates.isEmpty?else?{?return?}// 合并所有待處理的更新? ??let?batchedContent = pendingUpdates.joined()// 清空隊列,準備下一輪? ? pendingUpdates.removeAll()? ? lastFlushTime =?Date()// 切換到主線程執行UI更新? ??Task?{?@MainActor?in? ? ? ??completion(batchedContent)? ? }}

批處理優勢分析:

  • 減少調用次數:將多次UI更新合并為一次,減少開銷;

  • 提升響應性:主線程壓力減少,UI更加流暢;

  • 內存效率:及時清理已處理內容,避免內存累積。

???3. LLMMessageTextView:沉浸式打字機動畫


  • 3.1 背景

LLMMessageTextView?的設計目標是創造接近人類打字速度的自然動畫效果。通過設置的時間參數和智能的動畫控制,讓AI的文字輸出更加自然和富有節奏感。


  • 3.2 類設計

struct?LLMMessageTextView:?View?{? ??// 數據模型? ??let?text:?String? ? ? ? ? ? ? ? ? ? ?// 完整文本內容? ??let?messageUseMarkdown:?Bool? ? ? ? ?// 是否使用Markdown渲染? ??let?messageId:?String? ? ? ? ? ? ? ??// 消息唯一標識? ??let?isAssistantMessage:?Bool? ? ? ? ?// 是否為AI消息? ??let?isStreamingMessage:?Bool? ? ? ? ?// 是否正在流式傳輸// 動畫狀態? ??@State?private?var?displayedText:?String?=?""??// 當前顯示的文本? ??@State?private?var?animationTimer:?Timer? ? ? ?// 動畫定時器// 動畫配置參數? ??private?let?typingSpeed:?TimeInterval?=?0.015??// 15ms每字符? ??private?let?chunkSize:?Int?=?1? ? ? ? ? ? ? ? ?// 每次顯示1個字符}

  • 3.3 動畫控制策略

1. 條件化動畫觸發

private?var?shouldUseTypewriter: Bool {? ??// 只有同時滿足以下條件才啟用動畫:? ??// 1. 是AI助手的消息(用戶消息不需要動畫)? ??// 2. 文本長度超過5個字符(避免短消息的不必要動畫)? ??return?isAssistantMessage && (text?.count ???0) >?5}

觸發邏輯分析:

  • 用戶體驗導向:只對AI消息使用動畫,用戶消息直接顯示;

  • 性能考慮:短消息(≤5字符)直接顯示,避免動畫開銷;

  • 場景適配:流式傳輸時啟用動畫,靜態顯示時關閉動畫。

2.?流式文本變化處理

private?func?handleTextChange(_?newText:?String?) {? ??guard?let?newText?=?newText?else?{? ? ? ? displayedText?=?""? ? ? ? stopAnimation()? ? ? ??return? ? }if?isAssistantMessage?&&?isStreamingMessage?&&?shouldUseTypewriter {? ? ? ??// 智能判斷文本變化類型? ? ? ??if?newText.hasPrefix(displayedText)?&&?newText?!=?displayedText {? ? ? ? ? ??// 場景1: 文本內容追加(流式輸出的常見情況)? ? ? ? ? ? continueTypewriterAnimation(with: newText)? ? ? ? }?else?if?newText?!=?displayedText {? ? ? ? ? ??// 場景2: 文本內容完全變化(消息重新生成)? ? ? ? ? ? restartTypewriterAnimation(with: newText)? ? ? ? }? ? ? ??// 場景3: 文本內容無變化,不做處理? ? }?else?{? ? ? ??// 非動畫場景:直接顯示完整文本? ? ? ? displayedText?=?newText? ? ? ? stopAnimation()? ? }}

處理策略詳解:


  • 3.4 動畫執行機制

1. 動畫啟動流程

private?func?startTypewriterAnimation(for?text:?String) {? ??// 步驟1: 重置顯示狀態? ? displayedText?=?""// 步驟2: 開始動畫循環? ? continueTypewriterAnimation(with: text)}private?func?continueTypewriterAnimation(with?text:?String) {? ??// 前置檢查:避免無效動畫? ??guard?displayedText.count?<?text.count?else?{?return?}// 清理舊定時器,避免沖突? ? stopAnimation()// 創建新的動畫定時器? ? animationTimer?=?Timer.scheduledTimer(withTimeInterval: typingSpeed, repeats:?true) { timer?in? ? ? ??DispatchQueue.main.async {? ? ? ? ? ??self.appendNextCharacters(from: text)? ? ? ? }? ? }}

定時器機制特點:

  • 主線程執行:確保UI更新在主線程進行

  • 重復執行:設置repeats: true實現連續動畫

  • 沖突避免:啟動前先停止舊定時器

2. 字符追加邏輯

private?func?appendNextCharacters(from?text:?String) {? ??let?currentLength?=?displayedText.count// 邊界檢查:防止越界訪問? ??guard?currentLength?<?text.count?else?{? ? ? ? stopAnimation()?// 動畫完成,清理資源? ? ? ??return? ? }// 計算下一次顯示的字符范圍? ??let?endIndex?=?min(currentLength?+?chunkSize, text.count)? ??let?startIndex?=?text.index(text.startIndex, offsetBy: currentLength)? ??let?targetIndex?=?text.index(text.startIndex, offsetBy: endIndex)// 提取新字符并追加到顯示文本? ??let?newChars?=?text[startIndex..<targetIndex]? ? displayedText.append(String(newChars))// 檢查動畫是否完成? ??if?displayedText.count?>=?text.count {? ? ? ? stopAnimation()? ? }}

字符處理細節:

  • Unicode安全:使用String.Index正確處理多字節字符

  • 邊界保護:使用min()函數防止數組越界

  • 增量更新:每次只追加新字符,避免重復渲染

  • 3.5 視圖渲染策略

1. 條件化渲染

var?body: some?View?{? ??Group?{? ? ? ??if?let?text = text, !text.isEmpty?{? ? ? ? ? ??if?isAssistantMessage && isStreamingMessage && shouldUseTypewriter {? ? ? ? ? ? ? ??typewriterView(text) ?// 動畫視圖? ? ? ? ? ? }?else?{? ? ? ? ? ? ? ??staticView(text) ? ? ?// 靜態視圖? ? ? ? ? ? }? ? ? ? }? ? }? ??// 生命周期綁定? ? .onAppear?{?/* 啟動動畫 */?}? ? .onDisappear?{?/* 清理資源 */?}? ? .onChange(of: text) {?/* 處理文本變化 */?}? ? .onChange(of: isStreamingMessage) {?/* 處理流式狀態變化 */?}}

2. Markdown支持

@ViewBuilderprivate?func?typewriterView(_?text:?String) ->?some?View?{? ??if?messageUseMarkdown {? ? ? ??Markdown(displayedText)? ? ? ? ? ? .markdownBlockStyle(\.blockquote) { configuration?in? ? ? ? ? ? ? ? configuration.label? ? ? ? ? ? ? ? ? ? .padding()? ? ? ? ? ? ? ? ? ? .markdownTextStyle {? ? ? ? ? ? ? ? ? ? ? ??FontSize(13)? ? ? ? ? ? ? ? ? ? ? ??FontWeight(.light)? ? ? ? ? ? ? ? ? ? ? ??BackgroundColor(nil)? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? .overlay(alignment: .leading) {? ? ? ? ? ? ? ? ? ? ? ??Rectangle()? ? ? ? ? ? ? ? ? ? ? ? ? ? .fill(Color.gray)? ? ? ? ? ? ? ? ? ? ? ? ? ? .frame(width:?4)? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? .background(Color.gray.opacity(0.2))? ? ? ? ? ? }? ? }?else?{? ? ? ??Text(displayedText)? ? }}

  • 3.6 生命周期管理

1. 資源自動管理

.onAppear?{? ??if?let?text = text, isAssistantMessage && isStreamingMessage && shouldUseTypewriter {? ? ? ??startTypewriterAnimation(for: text)? ? }?else?if?let?text = text {? ? ? ? displayedText = text? ? }}.onDisappear?{? ??stopAnimation()?// 防止內存泄漏}

2. 狀態變化響應

.onChange(of: isStreamingMessage) { oldIsStreaming, newIsStreaming?in? ??if?!newIsStreaming {? ? ? ??// 流式傳輸結束,立即顯示完整內容? ? ? ??if?let?text = text {? ? ? ? ? ? displayedText = text? ? ? ? }? ? ? ??stopAnimation()? ? }}

3. 內存泄漏防護

private?func?stopAnimation() {? ? animationTimer?.invalidate() ?// 停止定時器? ? animationTimer?=?nil? ? ? ? ??// 釋放引用}

總結

綜上,結合三層的優化,通過以上多層協同優化方案,我們成功地將一個卡頓、生硬的文字輸出體驗轉變為流暢、自然的現代化AI交互界面。

PS:如果覺得文章對你有幫助或者啟發,歡迎 Star MNN :https://github.com/alibaba/MNN

團隊介紹

本文作者攬清,來自淘天集團-Meta技術團隊。本團隊目前負責面向消費場景的3D/XR基礎技術建設和創新應用探索,創造以手機及XR 新設備為載體的消費購物新體驗。團隊在端智能、端云協同、商品三維重建、真人三維重建、3D引擎、XR引擎等方面有著深厚的技術積累,先后發布深度學習引擎MNN、商品三維重建工具Object Drawer、3D真人數字人TaoAvatar、端云協同系統Walle等。團隊在OSDI、MLSys、CVPR、ICCV、NeurIPS、TPAMI等頂級學術會議和期刊上發表多篇論文。歡迎視覺算法、3D/XR引擎、深度學習引擎研發、終端研發等領域的優秀人才加入,共同走進3D數字新時代。

¤?拓展閱讀?¤

3DXR技術?|?終端技術?|?音視頻技術

服務端技術?|?技術質量?|?數據算法

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/95870.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/95870.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/95870.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【開發技巧】VS2022+QT5+OpenCV4.10開發環境搭建QT Creator

VS2022編譯器支持配置 QT5默認安裝以后支持的是VS2015與VS2017&#xff0c;不支持VS2022&#xff0c;所以必須首先在Qt Creator中配置支持VS2022。配置順序如下&#xff1a; 首先打開【工具】->【選項】 然點擊Kits里面的【編譯器】選項。點擊Manual下面的【C】然后點擊【…

【Linux系統】動靜態庫的制作

前言&#xff1a; 上文我們講到了文件系統【Linux系統】詳解Ext2&#xff0c;文件系統-CSDN博客 本文我們來講講動靜態庫的制作 庫 【Linux】編譯器gcc/g及其庫的詳細介紹_linux gcc 有哪些庫-CSDN博客 這篇文章的第4大點&#xff0c;簡單是介紹了一下庫的基本概念。 靜態庫 靜…

鏈式二叉樹的基本操作——遍歷

本文筆者將帶領讀者一起學習鏈式二叉樹的一些基本語法&#xff0c;至于更難一些的插入刪除等&#xff0c;筆者將在后續C更新后再次詳細帶領大家學習。 首先&#xff0c;在進行二叉樹之前&#xff0c;我們需要一顆二叉樹&#xff0c;而二叉樹的初始化現階段實現不太現實&#x…

Windows運維之以一種訪問權限不允許的方式做了一個訪問套接字的嘗試

一、問題場景 在Windows 上運維服務過程中&#xff0c;經常會遇到運行服務&#xff0c;部署安裝時候無任何問題&#xff0c;后續再某個特殊時間點&#xff0c;突然服務無法啟動了。再次啟動時&#xff0c;提示端口占用與以一種訪問權限不允許的方式做了一個訪問套接字的嘗試。 …

2020/12 JLPT聽力原文 問題二 3番

3番&#xff1a;レストランで、女の人と店長が話しています。店長はサラダについて、どんなアドバイスをしていますか。女&#xff1a;店長、この前話してた新しいランチメニューのサラダを作ってみたんですが、どうでしょうか。 男&#xff1a;ああ、サラダだけで満足できるっ…

芯片行業主要廠商

作為一個小白&#xff0c;每次淘寶買芯片時看到相似的命名規則&#xff1a;“OPA、AD、LT、MAX”等等時&#xff0c;我不禁好奇這些芯片行業大廠有哪些&#xff0c;所以查了些資料&#xff1a; 1. 德州儀器&#xff08;Texas Instruments, TI&#xff09; 公司概況&#xff1…

【BLE系列-第四篇】從零剖析L2CAP:信道、Credit流控、指令詳解

目錄 引言 一、L2CAP主要功能 二、L2CAP幀格式及信道概念 2.1 邏輯鏈路是什么&#xff1f; 2.2 邏輯信道的作用 2.3 L2CAP幀格式介紹 三、L2CAP信令信道 3.1 信令信道幀格式說明 3.2 信令信道指令介紹 3.2.1 信令信道指令一覽表 3.2.2 Credit流控規則 引言 在BLE協…

CSS保持元素寬高比,固定元素寬高比

方法一&#xff1a; <div class"hcp-fixed-aspect-ratio-box">這里是正文內容 </div>.hcp-fixed-aspect-ratio-box {width: 50%;color: #FFFFFF;margin: 100px auto;background: #FF0000;/* 寬高比2:1&#xff0c;兼容性可能不太好 */aspect-ratio: 2 / …

數據分析小白訓練營:基于python編程語言的Numpy庫介紹(第三方庫)(上篇)

&#xff08;一&#xff09;Numpy庫的安裝安裝指定版本的Numpy庫&#xff0c;打開命令提示符&#xff0c;輸入下圖內容&#xff0c;只需要將1.25.5的版本修改成個人需要的版本&#xff0c;然后按下回車鍵&#xff0c;numpy庫就安裝在python中&#xff1a;指定版本numpy庫安裝可…

從 Windows 到 Linux 服務器的全自動部署教程(免密登錄 + 壓縮 + 上傳 + 啟動)

一、準備工作 1. 環境說明 本地開發環境&#xff1a;Windows 服務器&#xff08;需執行部署腳本&#xff09;目標服務器&#xff1a;Linux 服務器&#xff08;需安裝 node.js、pm2、unzip&#xff09;核心工具&#xff1a;7-Zip&#xff08;壓縮&#xff09;、OpenSSH&#x…

智能汽車領域研發,復用云原始開發范式?

汽車電子電氣架構演進趨勢&#xff1a;分散的功能ECU -> 域控制器 -> 中央計算服務器汽車電子方案與架構在發展與迭代時會使用虛擬化方法幾種可行的軟硬一體化方案&#xff1a;多ECU&#xff0c;硬件隔離&#xff0c;硬件分區&#xff0c;車規級多核硬件架構 Hypervisor…

數據電臺詢價的詢價要求

技術規格及主要參數 1.電臺基本要求&#xff1a; 1.1 電臺中的信號處理基于FPGA設計&#xff0c;采用FPGAARM高速AD/DA設計架構&#xff1b; 1.2 具備頻譜感知、自主選頻、跳頻、擴頻等功能&#xff1b; 1.3 具備鏈路質量信息、自組網路由信息、電池電壓監測信息、北斗定位信息…

IoT/HCIP實驗-5/基于WIFI的智慧農業實驗(LwM2M/CoAP+PSK+ESP8266 連接到 IoTDA)

文章目錄概述WIFI8266 通信模組WIFI模組也用AT指令&#xff1f;ESP8266 內置協議棧?支持的無線網絡模式MCU通過串口與模組交互Wifi模組做客戶端PC-AT接入路由器向本地TCP服務發數據用代碼接入你家路由器已接入AP&#xff08;你家Wifi&#xff09;平臺側開發工程配置和編譯工程…

定時器輸出PWM波配置(呼吸燈)

使用定時器 4 通道 3 生成 PWM 波控制 LED1 &#xff0c;實現呼吸燈效果。 頻率&#xff1a;2kHz&#xff0c;PSC71&#xff0c;ARR499pwm.c:#include "pwm.h" // 本模塊頭文件&#xff1a;應聲明 pwm_init/pwm_compare_set 等原型、并包含 HAL 頭//&#xff08;示…

[ai-agent]環境簡介之沙盒e2b vs daytona

所謂的環境的就是agent運行在哪里&#xff0c;或者是agent和那里進行交互。 最常見的環境就是本地開發環境&#xff0c;也就是個人主機&#xff0c;但是存在問題就是沒有辦法出網和橫向擴展。 在沙盒之前也是有其他選擇的&#xff1a; 云服務器&#xff0c; 虛擬機&#xff0c;…

【前端面試題】前端面試知識點(第三十一題到第六十一題)

三十一. CSS實現垂直水平居中 實現元素的垂直水平居中是前端開發中的常見需求,主要有以下幾種思路: text-align + line-height實現單行文本水平垂直居中 適用于單行文本元素,通過text-align: center實現水平居中,line-height等于容器高度實現垂直居中 text-align + vertic…

嵌入式練習項目——————抓包獲取天氣信息

一、內容 嘗試通過實時天氣接口 - 數據接口 - NowAPI此網站獲取天氣信息&#xff0c;實現可以發送城市查詢當前天氣和未來天氣 二、獲取請求報文 可以根據測試示例看到獲取內容&#xff0c;此時數據是cJSON格式&#xff0c;我們首先要通過合適的網址抓包獲取到請求報文&#x…

Python爬蟲實戰:研究NewsCrawl ,構建新浪和網易新聞數據采集系統

1. 引言 1.1 研究背景與意義 在信息時代,新聞作為社會動態、公眾觀點的重要載體,其傳播速度與影響力持續擴大。傳統的人工篩選與采集方式已無法滿足對海量新聞數據的高效處理需求,亟需自動化工具實現大規模、結構化的新聞數據采集。網絡爬蟲技術作為一種按照預設規則自動抓…

PyTorch神經網絡工具箱全解析:nn.Module vs nn.functional

&#x1f50d; 為何需要神經網絡工具箱&#xff1f; 在僅用 Autograd 和 Tensor 實現模型時&#xff0c;開發者需手動設置參數梯度&#xff08;requires_gradTrue&#xff09;、反向傳播&#xff08;backward()&#xff09;及梯度提取&#xff0c;過程繁瑣且易出錯。nn 工具箱應…

Java注解學習記錄

目錄 一、為什么要學注解&#xff1f; 二、注解是什么&#xff1f; 三、為什么要使用注解&#xff1f; 四、注解的作用 五、注解的分類 5.1 元注解 Retention&#xff08;/ r??ten?(?)n /&#xff09; ★★★★★ Target ★★★★★ Inherited(/ ?n?her?t?d /…