From aaaa3208d0113421c5b5dd86a8d4045adbd4f420 Mon Sep 17 00:00:00 2001 From: Da Shen Date: Fri, 7 Aug 2026 23:47:21 +0800 Subject: [PATCH 1/2] =?UTF-8?q?[1182]=20=E4=BB=BB=E5=8A=A1=E6=96=87?= =?UTF-8?q?=E6=A1=A3=EF=BC=9ALLM=20Chat=20=E6=A0=87=E7=AD=BE=E9=A1=B5?= =?UTF-8?q?=E6=89=93=E5=BC=80=E8=80=97=E6=97=B6=E6=89=93=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude --- devel/1182.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 devel/1182.md diff --git a/devel/1182.md b/devel/1182.md new file mode 100644 index 0000000000..a5030408b3 --- /dev/null +++ b/devel/1182.md @@ -0,0 +1,34 @@ +# [1182] LLM Chat 标签页打开耗时打点 + +## 1 任务相关的代码文件 +- `src/Plugins/Qt/qt_chat_controller.cpp` — ChatController::createView 及各 scheme 调用点 +- `src/Plugins/Qt/qt_chat_tab_widget.cpp/.hpp` — QTChatTabWidget,首次 paintEvent 打点 +- `src/Plugins/Qt/qt_tm_widget.cpp` — sync_chat_tab_mode,show+focus 打点 +- `TeXmacs/progs/kernel/gui/kbd-define.scm` — lazy-keyboard-force 逐模块加载计时 + +## 2 改动记录 + +### 2.1 点击 LLM 标签页全链路耗时打点(2026-08-07) + +**What**:在点击 Chat 标签页 → 界面首次绘制的全链路加 `debug_std` 计时日志 +(`-d std` 开启)。 + +**Why**:实测点击 LLM 标签页到界面显示约 400ms,需要定位耗时分布。 + +**How**: +- `ChatController::createView`:分 7 段计时(use-modules / load-all / + build-infos / new-widget / connect / activate / misc + total) +- `ChatController::ensureNewConversation`:create-panel / sync-dark-style / + load-input-styles / activate-panel 分段 +- `ChatController::getOrCreatePanel`、`loadSessionContent`:scheme 调用计时 +- `QTChatTabWidget`:新增 `paintEvent` override + `armFirstPaintLog`, + 首次绘制时输出 show→paint 耗时(界面真正可见时刻) +- `sync_chat_tab_mode`:show_widget_in_layout + setFocus 计时 +- `kbd-define.scm`:`lazy-keyboard-force-do` 每个 `module-provide` 计时, + 覆盖 update_menus 之后 lazy kbd 模块加载的空档 + +**实测分布**(点击到显示 ~400ms): +- `use-modules (llm chat-loader)` ~57ms +- scheme init(sync-dark-style + load-input-styles)~81ms +- `update_menus` ~97ms +- 其余为 lazy kbd 模块加载与首次 paint From 70c05c6d9ed0f00adee55491b21f6e77830d47be Mon Sep 17 00:00:00 2001 From: Da Shen Date: Fri, 7 Aug 2026 23:47:21 +0800 Subject: [PATCH 2/2] [1182] bench on llm tab Co-Authored-By: Claude --- TeXmacs/progs/kernel/gui/kbd-define.scm | 12 ++++++- src/Plugins/Qt/qt_chat_controller.cpp | 43 +++++++++++++++++++++++++ src/Plugins/Qt/qt_chat_tab_widget.cpp | 13 ++++++++ src/Plugins/Qt/qt_chat_tab_widget.hpp | 12 +++++++ src/Plugins/Qt/qt_tm_widget.cpp | 6 ++++ 5 files changed, 85 insertions(+), 1 deletion(-) diff --git a/TeXmacs/progs/kernel/gui/kbd-define.scm b/TeXmacs/progs/kernel/gui/kbd-define.scm index 417d1a5a1a..49b1dd77c8 100644 --- a/TeXmacs/progs/kernel/gui/kbd-define.scm +++ b/TeXmacs/progs/kernel/gui/kbd-define.scm @@ -55,7 +55,17 @@ (cond ((null? l) l) ((ahash-ref lazy-keyboard-done (cdar l)) (lazy-keyboard-force-do (cdr l))) ((or lazy-force-all? (texmacs-in-mode? (caar l))) - (module-provide (cdar l)) + (let ((start (texmacs-time))) + (module-provide (cdar l)) + (debug-message "debug-std" + (string-append "lazy-keyboard-force: loaded " + (object->string (cdar l)) + " in " + (number->string (- (texmacs-time) start)) + " ms\n" + ) ;string-append + ) ;debug-message + ) ;let (ahash-set! lazy-keyboard-done (cdar l) #t) (lazy-keyboard-force-do (cdr l)) ) ; diff --git a/src/Plugins/Qt/qt_chat_controller.cpp b/src/Plugins/Qt/qt_chat_controller.cpp index 377dcbf1a8..42019cb3a3 100644 --- a/src/Plugins/Qt/qt_chat_controller.cpp +++ b/src/Plugins/Qt/qt_chat_controller.cpp @@ -18,6 +18,8 @@ #include "new_buffer.hpp" #include "s7_tm.hpp" #include "scheme.hpp" +#include "tm_debug.hpp" +#include "tm_timer.hpp" #include #include @@ -54,10 +56,13 @@ ChatController::destroyView () { QWidget* ChatController::createView (QWidget* parent, qt_tm_widget_rep* tm) { + time_t t0= texmacs_time (); // 1. Load session metadata // llm 插件按 idle 延迟初始化,新建 Chat 标签页时其 scheme 模块可能尚未加载 eval ("(use-modules (llm chat-loader))"); + time_t t1= texmacs_time (); call ("chat-persist-load-all"); + time_t t2= texmacs_time (); cout << "[chat-persist] ChatController: restored " << sessionManager_.sessionCount () << " session metadatas" << LF; @@ -72,10 +77,12 @@ ChatController::createView (QWidget* parent, qt_tm_widget_rep* tm) { else { initialId= sessionManager_.firstActiveSessionId (); } + time_t t3= texmacs_time (); // 3. 创建 View,Sidebar 构造时就有数据 view_= new QTChatTabWidget (infos, initialId, parent); view_->setParentTmWidget (tm); + time_t t4= texmacs_time (); // 连接 Sidebar 信号 ChatSidebar* sb= view_->sidebar (); @@ -128,12 +135,14 @@ ChatController::createView (QWidget* parent, qt_tm_widget_rep* tm) { } // 4. 激活初始会话(按需创建 Panel) + time_t t5= texmacs_time (); if (!is_empty (initialId)) { activateSession (initialId); } else { ensureNewConversation (); } + time_t t6= texmacs_time (); // 5. 恢复当前模型(使用激活的会话) if (!is_empty (initialId)) { @@ -149,6 +158,16 @@ ChatController::createView (QWidget* parent, qt_tm_widget_rep* tm) { return view_->contentWidget (); }); + if (DEBUG_STD) { + time_t t7= texmacs_time (); + debug_std << "[chat-tab] createView: use-modules=" << (t1 - t0) + << "ms, load-all=" << (t2 - t1) << "ms, build-infos=" << (t3 - t2) + << "ms, new-widget=" << (t4 - t3) << "ms, connect=" << (t5 - t4) + << "ms, activate=" << (t6 - t5) << "ms, misc=" << (t7 - t6) + << "ms, total=" << (t7 - t0) << "ms\n"; + } + view_->armFirstPaintLog (texmacs_time ()); + return view_; } @@ -486,8 +505,13 @@ ChatController::loadSessionContent (ChatConversationPanel* panel) { // 只在非归档会话且内容未加载时才加载 if (s->archived) return; + time_t t0= texmacs_time (); call ("chat-persist-load-session-content", panel->sessionId (), object (s->defaultExpandCount)); + if (DEBUG_STD) { + debug_std << "[chat-tab] loadSessionContent: scheme-load=" + << (texmacs_time () - t0) << "ms\n"; + } // 检查消息 buffer 是否非空,若非空则进入会话模式并滚动到底部 tree msgBody= get_buffer_body ( @@ -595,15 +619,19 @@ ChatController::ensureNewConversation () { } // 创建新会话 + time_t t0 = texmacs_time (); string sid = sessionManager_.createSession (); ChatConversationPanel* panel= view_->createPanel (sid); if (!panel) return; + time_t t1= texmacs_time (); sessionManager_.setPanel (sid, panel); sessionManager_.setModel (sid, currentModel_); call ("chat-tab-sync-dark-style!", sid); + time_t t1b= texmacs_time (); call ("chat-tab-load-input-styles!", sid); + time_t t2= texmacs_time (); if (panel->sessionTitle ()) panel->sessionTitle ()->hide (); @@ -612,6 +640,15 @@ ChatController::ensureNewConversation () { view_->activatePanel (panel); view_->sidebar ()->setActiveItem (""); + + if (DEBUG_STD) { + time_t t3= texmacs_time (); + debug_std << "[chat-tab] ensureNewConversation: create-panel=" << (t1 - t0) + << "ms, sync-dark-style=" << (t1b - t1) + << "ms, load-input-styles=" << (t2 - t1b) + << "ms, activate-panel=" << (t3 - t2) << "ms, total=" << (t3 - t0) + << "ms\n"; + } } /** @@ -629,13 +666,19 @@ ChatController::getOrCreatePanel (const string& sessionId) { if (s->panel) return static_cast (s->panel); // 按需创建面板 + time_t t0 = texmacs_time (); ChatConversationPanel* panel= view_->createPanel (sessionId); if (!panel) return nullptr; + time_t t1= texmacs_time (); sessionManager_.setPanel (sessionId, panel); call ("chat-tab-sync-dark-style!", sessionId); call ("chat-tab-init-session!", sessionId, s->model); + if (DEBUG_STD) { + debug_std << "[chat-tab] getOrCreatePanel: create-panel=" << (t1 - t0) + << "ms, scheme-init=" << (texmacs_time () - t1) << "ms\n"; + } // 连接 Panel 的信号 connectPanelSignals (panel); diff --git a/src/Plugins/Qt/qt_chat_tab_widget.cpp b/src/Plugins/Qt/qt_chat_tab_widget.cpp index c4384f8ff6..53bd4318a4 100644 --- a/src/Plugins/Qt/qt_chat_tab_widget.cpp +++ b/src/Plugins/Qt/qt_chat_tab_widget.cpp @@ -21,6 +21,8 @@ #include "qt_utilities.hpp" #include "qt_widget.hpp" #include "s7_tm.hpp" +#include "tm_debug.hpp" +#include "tm_timer.hpp" #include "tm_window.hpp" #include @@ -1672,6 +1674,17 @@ QTChatTabWidget::setGlobalSidebarCollapsed (bool collapsed) { * QTChatTabWidget 事件处理 ******************************************************************************/ +void +QTChatTabWidget::paintEvent (QPaintEvent* event) { + if (firstPaintPending_) { + firstPaintPending_= false; + if (DEBUG_STD) + debug_std << "[chat-tab] first paint: show->paint=" + << (texmacs_time () - createFinishTime_) << "ms\n"; + } + QWidget::paintEvent (event); +} + void QTChatTabWidget::keyPressEvent (QKeyEvent* event) { // Ctrl/Cmd+J:关闭 AI 聊天侧边栏(仅在 dock 模式下触发) diff --git a/src/Plugins/Qt/qt_chat_tab_widget.hpp b/src/Plugins/Qt/qt_chat_tab_widget.hpp index 7a0e3dfed0..847fe25acc 100644 --- a/src/Plugins/Qt/qt_chat_tab_widget.hpp +++ b/src/Plugins/Qt/qt_chat_tab_widget.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #include "widget.hpp" @@ -398,6 +399,12 @@ class QTChatTabWidget : public QWidget { // ---- 供外部组件访问 ---- QWidget* contentWidget () const { return contentWidget_; } + /// 标记 createView 完成,等待首次 paint 打点(性能定位用) + void armFirstPaintLog (time_t createFinishTime) { + firstPaintPending_= true; + createFinishTime_ = createFinishTime; + } + signals: void cancelRequested (const string& sessionId); void newChatRequested (); @@ -409,6 +416,8 @@ class QTChatTabWidget : public QWidget { void keyReleaseEvent (QKeyEvent* event) override; /// 事件过滤器 bool eventFilter (QObject* watched, QEvent* event) override; + /// 首次绘制时打点(用于测量点击标签页到界面可见的耗时) + void paintEvent (QPaintEvent* event) override; private: /// 构建左侧侧边栏布局 @@ -441,6 +450,9 @@ class QTChatTabWidget : public QWidget { qt_tm_widget_rep* parentTmWidget_= nullptr; ///< 关联的 TeXmacs widget static bool globalSidebarCollapsed_; ///< 全局记忆的侧边栏折叠状态 + + bool firstPaintPending_= false; ///< 是否等待首次绘制打点 + time_t createFinishTime_ = 0; ///< createView 完成时刻(ms) }; #endif // QT_CHAT_TAB_WIDGET_HPP diff --git a/src/Plugins/Qt/qt_tm_widget.cpp b/src/Plugins/Qt/qt_tm_widget.cpp index dfb189b02b..035340181e 100644 --- a/src/Plugins/Qt/qt_tm_widget.cpp +++ b/src/Plugins/Qt/qt_tm_widget.cpp @@ -52,6 +52,8 @@ #include "qt_renderer.hpp" #include "qt_tm_widget.hpp" #include "qt_utilities.hpp" +#include "tm_debug.hpp" +#include "tm_timer.hpp" bool in_presentation_mode (); @@ -1259,8 +1261,12 @@ qt_tm_widget_rep::sync_chat_tab_mode () { chatContentWidget= get_chat_controller ()->createView (centralwidget (), this); } + time_t t_show0= texmacs_time (); show_widget_in_layout (chatContentWidget, layout); chatContentWidget->setFocus (Qt::OtherFocusReason); + if (DEBUG_STD) + debug_std << "[chat-tab] sync_chat_tab_mode: show+focus=" + << (texmacs_time () - t_show0) << "ms\n"; } else { // Show normal editor view only when no special tab mode is active