Skip to content
Open
56 changes: 53 additions & 3 deletions TeXmacs/progs/kernel/gui/menu-widget.scm
Original file line number Diff line number Diff line change
Expand Up @@ -1244,14 +1244,57 @@
;; Menu expansion
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(define menu-expand-link-cache (make-ahash-table))

(define (static-menu-link? name)
"Menus whose expanded result never changes at runtime."
(in? name
'(style-menu add-package-menu
remove-package-menu
toggle-package-menu
basic-theme-menu
document-page-size-menu
document-language-menu
document-short-font-menu
document-font-base-size-menu
page-rendering-menu
page-layout-menu
document-columns-menu
print-menu-inline
new-file-menu
load-menu
save-menu
close-menu
cite-texmacs-menu
cite-texmacs-related-menu
color-menu
document-encryption-menu
document-columns-menu)
) ;in?
) ;define

(define (menu-expand-link p)
"Expand menu link @p."
(with linked ((eval (cadr p))) (if linked (menu-expand linked) p))
(let* ((name (cadr p))
(cached (and (static-menu-link? name) (ahash-ref menu-expand-link-cache name)))
) ;
(if cached
cached
(let* ((linked ((eval name))) (result (if linked (menu-expand linked) p)))
(when (and (static-menu-link? name) linked)
(ahash-set! menu-expand-link-cache name result)
) ;when
result
) ;let*
) ;if
) ;let*
) ;define

(define (menu-expand-dynamic p)
"Expand menu link @p."
(with dyn (eval (cadr p)) (if dyn (menu-expand dyn) p))
(let* ((dyn (eval (cadr p))) (result (if dyn (menu-expand dyn) p)))
result
) ;let*
) ;define

(define (menu-expand-resize p)
Expand Down Expand Up @@ -1367,9 +1410,16 @@
`(toggle ,(replace-procedures (cadr p)) ,((caddr p)))
) ;define

(define menu-expand-count 0)

(define (menu-expand-list l)
"Expand links and conditional menus in list of menus @l."
(map menu-expand l)
(map (lambda (item)
(set! menu-expand-count (+ menu-expand-count 1))
(menu-expand item)
) ;lambda
l
) ;map
) ;define

(define must-eval-list '(input enum choice filtered-choice toggle))
Expand Down
45 changes: 45 additions & 0 deletions devel/0146.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# [0146] 优化新建空白文档时焦点图标工具栏的性能

## 相关文档
- [dddd.md](dddd.md) - 任务文档模板

## 任务相关的代码文件
- `src/Edit/Interface/edit_interface.cpp` - `update_menus()` 和 `resume()` 中调用 `menu_icons(2, ...)`
- `src/Data/Document/new_style.cpp` - `get_style_menu()` 等 style/package 菜单缓存
- `TeXmacs/progs/generic/generic-menu.scm` - `texmacs-focus-icons`、`standard-focus-icons` 定义
- `TeXmacs/progs/generic/document-menu.scm` - `focus-style-icons` 定义
- `TeXmacs/progs/kernel/gui/menu-widget.scm` - `menu-expand` 展开逻辑及静态菜单缓存
- `TeXmacs/progs/texmacs/menus/main-menu.scm` - `style-menu` 等函数定义

## 如何测试

### 非确定性测试(性能验证)
1. 启动 Mogan
2. 使用 `Ctrl+N` 或菜单新建空白文档
3. 观察新建文档的响应速度

## 如何提交

```bash
xmake b stem
```

## What

优化新建空白文档时,焦点图标工具栏(focus icon toolbar)的 expand 过程的性能。

## Why

新建空白文档时,焦点图标工具栏的刷新存在性能瓶颈,冷启动 `menu-expand 2` 耗时约 60~80ms:

1. `resume()` 在视图激活时调用 `menu_icons(2, "(horizontal (link texmacs-focus-icons))")`
2. `texmacs-focus-icons` 调用 `(standard-focus-icons (focus-tree))`,该菜单包含大量动态内容
3. 其中的 `(link style-menu)` 触发 `get_style_menu()`,每次都要 `descendance` 遍历样式目录树 + `compute_style_menu` 构建菜单字符串 + `eval` 解析
4. scheme 层 `menu-expand` 对 `(assuming ...)`、`(=> ...)`、`(for ...)` 等的递归展开也有显著开销

## How

1. **C++ 层 static cache**:为 `get_style_menu()`、`get_add_package_menu()`、`get_remove_package_menu()`、`get_toggle_package_menu()` 添加 `static object cache`,首次计算后缓存结果,后续调用直接返回
2. **scheme 层静态菜单缓存**:在 `menu-expand-link` 中通过 `static-menu-link?` 识别不依赖运行时状态的菜单函数(如 `style-menu`、`add-package-menu` 等),用 `menu-expand-link-cache`(ahash-table)缓存展开结果,按函数符号名作为 key

当前状态:C++ static cache 和 scheme 静态菜单缓存已生效,`update_menus` 阶段 `menu-expand 2` 从约 30ms 降至约 10ms。冷启动首次展开约 60ms 的排查仍在进行中。
83 changes: 59 additions & 24 deletions src/Data/Document/new_style.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ init_style_data () {

extern hashmap<string, tree> style_tree_cache;
hashmap<string, bool> hidden_packages (false);
static hashmap<string, bool> hidden_package_set (false);
static bool hidden_package_set_initialized= false;

static url
resolve_local_style (string style_name) {
Expand Down Expand Up @@ -172,10 +174,14 @@ cache_file_name (tree t) {
return lolly::hash::md5_hexdigest (tmp) * ".scm";
}

void ensure_hidden_package_set ();

void
style_invalidate_cache () {
style_tree_cache= hashmap<string, tree> ();
hidden_packages = hashmap<string, bool> (false);
style_tree_cache = hashmap<string, tree> ();
hidden_packages = hashmap<string, bool> (false);
hidden_package_set = hashmap<string, bool> (false);
hidden_package_set_initialized= false;
if (sd != NULL) {
tm_delete<style_data_rep> (sd);
sd= NULL;
Expand Down Expand Up @@ -359,34 +365,45 @@ ignore_dir (string dir) {
(dir == "Standard") || (dir == "Test") || (dir == "Themes");
}

static bool
hidden_package (url u, string name, bool hidden) {
if (is_or (u))
return hidden_package (u[1], name, hidden) ||
hidden_package (u[2], name, hidden);
static void
collect_hidden_packages (url u, bool hidden, hashmap<string, bool>& pkgs) {
if (is_or (u)) {
collect_hidden_packages (u[1], hidden, pkgs);
collect_hidden_packages (u[2], hidden, pkgs);
return;
}
if (is_concat (u)) {
string dir= upcase_first (as_string (u[1]));
if (dir == "CVS" || dir == ".svn") return false;
return hidden_package (u[2], name, hidden || ignore_dir (dir));
if (dir == "CVS" || dir == ".svn") return;
collect_hidden_packages (u[2], hidden || ignore_dir (dir), pkgs);
return;
}
if (hidden && is_atomic (u)) {
string l= as_string (u);
if (ends (l, ".ts")) l= l (0, N (l) - 3);
else if (ends (l, ".hook")) l= l (0, N (l) - 5);
else return false;
return name == l;
else return;
pkgs (l)= true;
}
}

static url get_package_root ();

void
ensure_hidden_package_set () {
if (!hidden_package_set_initialized) {
bench_start ("hidden_package_init");
collect_hidden_packages (get_package_root (), false, hidden_package_set);
hidden_package_set_initialized= true;
bench_end ("hidden_package_init");
}
return false;
}

bool
hidden_package (string name) {
if (name == "std-latex") return false;
if (!hidden_packages->contains (name)) {
url pck_u = descendance ("$TEXMACS_PACKAGE_ROOT");
hidden_packages (name)= hidden_package (pck_u, name, false);
}
return hidden_packages[name];
ensure_hidden_package_set ();
return hidden_package_set->contains (name);
}

static string
Expand Down Expand Up @@ -426,30 +443,48 @@ compute_style_menu (url u, int kind) {
return "";
}

static url
get_package_root () {
static url pck_u= descendance ("$TEXMACS_PACKAGE_ROOT");
return pck_u;
}

object
get_style_menu () {
static object cache;
if (cache != null_object ()) return cache;
url sty_u= descendance ("$TEXMACS_STYLE_ROOT");
string sty = compute_style_menu (sty_u, 0);
return eval ("(menu-dynamic " * sty * ")");
cache = eval ("(menu-dynamic " * sty * ")");
return cache;
}

object
get_add_package_menu () {
url pck_u= descendance ("$TEXMACS_PACKAGE_ROOT");
static object cache;
if (cache != null_object ()) return cache;
url pck_u= get_package_root ();
string pck = compute_style_menu (pck_u, 1);
return eval ("(menu-dynamic " * pck * ")");
cache = eval ("(menu-dynamic " * pck * ")");
return cache;
}

object
get_remove_package_menu () {
url pck_u= descendance ("$TEXMACS_PACKAGE_ROOT");
static object cache;
if (cache != null_object ()) return cache;
url pck_u= get_package_root ();
string pck = compute_style_menu (pck_u, 2);
return eval ("(menu-dynamic " * pck * ")");
cache = eval ("(menu-dynamic " * pck * ")");
return cache;
}

object
get_toggle_package_menu () {
url pck_u= descendance ("$TEXMACS_PACKAGE_ROOT");
static object cache;
if (cache != null_object ()) return cache;
url pck_u= get_package_root ();
string pck = compute_style_menu (pck_u, 3);
return eval ("(menu-dynamic " * pck * ")");
cache = eval ("(menu-dynamic " * pck * ")");
return cache;
}
1 change: 1 addition & 0 deletions src/Data/Document/new_style.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ tree get_document_preamble (tree t);
drd_info get_document_drd (tree doc);

object get_style_menu ();
void ensure_hidden_package_set ();
bool hidden_package (string name);
object get_add_package_menu ();
object get_remove_package_menu ();
Expand Down
4 changes: 4 additions & 0 deletions src/Edit/Interface/edit_interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ edit_interface_rep::suspend () {
void
edit_interface_rep::resume () {
// cout << "Resume " << buf->buf->name << LF;
bench_start ("resume");
got_focus= true;
SERVER (menu_main ("(horizontal (link texmacs-menu))"));
SERVER (menu_icons (0, "(horizontal (link texmacs-main-icons))"));
Expand Down Expand Up @@ -145,6 +146,7 @@ edit_interface_rep::resume () {
// after a bugfix by Massimiliano during summer 2016
eval ("(delayed (:idle 1) (refresh-window))");
#endif
bench_end ("resume");
}

void
Expand Down Expand Up @@ -810,6 +812,7 @@ edit_interface_rep::change_time () {

void
edit_interface_rep::update_menus () {
bench_start ("update_menus");
SERVER (menu_main ("(horizontal (link texmacs-menu))"));
SERVER (menu_icons (0, "(horizontal (link texmacs-main-icons))"));
SERVER (menu_icons (1, "(horizontal (link texmacs-mode-icons))"));
Expand Down Expand Up @@ -840,6 +843,7 @@ edit_interface_rep::update_menus () {
cache_memorize ();
last_update= last_change;
save_user_preferences ();
bench_end ("update_menus");
}

int
Expand Down
2 changes: 2 additions & 0 deletions src/System/Boot/init_texmacs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include "merge_sort.hpp"
#include "moebius/tree_label.hpp"
#include "new_buffer.hpp"
#include "new_style.hpp"
#include "new_window.hpp"
#include "preferences.hpp"
#include "server.hpp"
Expand Down Expand Up @@ -969,6 +970,7 @@ TeXmacs_main (int argc, char** argv) {
#endif

if (N (extra_init_cmd) > 0) exec_delayed (scheme_cmd (extra_init_cmd));
ensure_hidden_package_set ();
gui_start_loop ();

if (DEBUG_STD) debug_boot << "Stopping server...\n";
Expand Down
Loading