diff --git a/devel/0124.md b/devel/0124.md new file mode 100644 index 0000000000..6788839b04 --- /dev/null +++ b/devel/0124.md @@ -0,0 +1,68 @@ +# [0124] 表格 position_columns 性能优化 + +## 1 相关文档 +- [dddd.md](dddd.md) - 任务文档模板 +- [0124.tmu](0124.tmu) - 测试文档 + +## 2 任务相关的代码文件 +- `src/Typeset/Table/table.hpp` +- `src/Typeset/Table/table.cpp` +- `tests/Typeset/Table/table_performance_test.cpp` + +## 3 如何测试 + +### 3.1 确定性测试(单元测试) +```bash +xmake b table_performance_test +xmake r table_performance_test +``` + +### 3.2 非确定性测试(文档验证) +```bash +# 使用 Mogan 打开 0124.tmu 文档,验证表格排版正常 +# 特别检查开启 cell-hyphen 的表格是否正确换行、列宽是否自适应 +``` + +## 4 如何提交 + +提交前执行以下最少步骤: + +```bash +xmake b table_performance_test +xmake r table_performance_test +``` + +## 5 What + +优化表格排版中 `position_columns` 方法的性能。 + +1. 在 `table_rep` 中新增 `has_lazy_cells` 标志位 +2. 在 `typeset_row` 过程中缓存该标志,避免后续重复扫描 +3. 在 `handle_decorations` 中正确传播子表格和 decoration 的 lazy cells 状态 +4. 在 `position_columns` 中直接使用缓存标志,替换 `O(nr_rows * nr_cols)` 的扫描循环 +5. 添加 C++ 单元测试验证标志位的正确性 + +## 6 Why + +在 `position_columns` 中,每次调用都需要扫描整个表格(`nr_rows * nr_cols`)来检测是否存在 `lz`(lazy stream)非空的单元格。对于大型表格(如 100x100),这涉及遍历 10000 个单元格。而 `position_columns` 可能被多次调用(如 `compute_width` 和 `lazy_table_rep::produce`),导致不必要的性能开销。 + +由于 lazy cells 的状态在 `typeset` 阶段即已确定,后续不会改变,因此可以在 typeset 阶段缓存该状态,避免重复扫描。 + +## 7 How + +### 7.1 实现思路 + +- `table_rep` 新增 `bool has_lazy_cells` 字段,默认 `false` +- `typeset_row` 中,当任意 cell 的 `lz` 非空时,置 `has_lazy_cells = true` +- `handle_decorations` 中,递归处理子表格和 decoration 后,若子表格有 lazy cells,也置当前表格的 `has_lazy_cells = true` +- `position_columns` 中,将原来的双重循环扫描替换为直接使用 `has_lazy_cells` + +### 7.2 兼容性 + +该优化不改变任何排版结果,仅消除了重复计算。所有现有单元测试(21 个)均通过。 + +### 7.3 测试覆盖 + +- `test_lazy_cells_flag_simple`:验证无 cell-hyphen 的表格 `has_lazy_cells` 为 `false` +- `test_lazy_cells_flag_with_hyphen`:验证开启 cell-hyphen 的表格 `has_lazy_cells` 为 `true` +- 原有性能测试和正确性测试均继续通过 diff --git a/devel/0124.tmu b/devel/0124.tmu new file mode 100644 index 0000000000..ee7fff650c --- /dev/null +++ b/devel/0124.tmu @@ -0,0 +1,40 @@ +> + +> + +<\body> + > + + 0124 任务:优化表格 position_columns 中 has_lazy_cells 的重复扫描问题。 + + + + 以下是一个 5x5 的简单文本表格,用于验证基本排版正确性: + + <\table> + ||||>|||||>|||||>|||||>|||||>>> + + + + + 以下表格开启了 cell-hyphen,用于验证 has_lazy_cells 缓存机制下列宽自适应仍然正确: + + <\table> + ||>||>||>>> + + + + + <\table> + ||>||>>>>>>>||>>> + + + +<\initial> + <\collection> + + + + + + diff --git a/src/Typeset/Table/table.cpp b/src/Typeset/Table/table.cpp index dd7578f0d0..a07bff0582 100644 --- a/src/Typeset/Table/table.cpp +++ b/src/Typeset/Table/table.cpp @@ -24,7 +24,8 @@ lazy make_lazy_paragraph (edit_env env, array bs, path ip); table_rep::table_rep (edit_env env2, int status2, int i0b, int j0b) : var (""), env (env2), status (status2), i0 (i0b), j0 (j0b), T (NULL), - nr_rows (0), mw (NULL), lw (NULL), rw (NULL), width (0), height (0) {} + nr_rows (0), mw (NULL), lw (NULL), rw (NULL), width (0), height (0), + has_lazy_cells (false) {} table_rep::~table_rep () { if (T != NULL) { @@ -127,6 +128,7 @@ table_rep::typeset_row (int i, tree fm, tree t, path ip) { if (i == nr_rows - 1) C->border_flags+= 2; tree old= env->local_begin (CELL_COL_NR, as_string (j)); C->typeset (subformat[j], t[j], descend (ip, j)); + if (!is_nil (C->lz)) has_lazy_cells= true; env->local_end (CELL_COL_NR, old); C->row_span= min (C->row_span, nr_rows - i); C->col_span= min (C->col_span, nr_cols - j); @@ -335,9 +337,13 @@ table_rep::handle_decorations () { for (i= 0; i < nr_rows; i++) for (j= 0; j < nr_cols; j++) { cell C= T[i][j]; - if ((!is_nil (C)) && (!is_nil (C->T))) C->T->handle_decorations (); + if ((!is_nil (C)) && (!is_nil (C->T))) { + C->T->handle_decorations (); + if (C->T->has_lazy_cells) has_lazy_cells= true; + } if ((!is_nil (C)) && (!is_nil (C->D))) { C->D->handle_decorations (); + if (C->D->has_lazy_cells) has_lazy_cells= true; if (C->D->status == 1) { ii = i + C->row_span - 1; jj = j + C->col_span - 1; @@ -633,11 +639,7 @@ table_rep::position_columns (bool large) { SI page_w, d1, d2, d3, d4, d5, d6, d7; env->get_page_pars (page_w, d1, d2, d3, d4, d5, d6, d7); if (total > page_w) { - bool has_hyphen= false; - for (int i= 0; i < nr_rows && !has_hyphen; i++) - for (int j= 0; j < nr_cols && !has_hyphen; j++) - if (!is_nil (T[i][j]) && !is_nil (T[i][j]->lz)) has_hyphen= true; - if (has_hyphen) { + if (has_lazy_cells) { // Proportional scaling for (int j= 0; j < nr_cols; j++) { mw[j]= (SI) ((((long long) mw[j]) * page_w) / total); diff --git a/src/Typeset/Table/table.hpp b/src/Typeset/Table/table.hpp index b484aaa6d2..ecefe51e97 100644 --- a/src/Typeset/Table/table.hpp +++ b/src/Typeset/Table/table.hpp @@ -58,9 +58,10 @@ class table_rep : public concrete_struct { string vmode; // how to interpret the height string halign; // horizontal alignment string valign; // vertical alignment - string hyphen; // vertical hypenation - int row_origin; // row span (not yet implemented) - int col_origin; // column span (not yet implemented) + string hyphen; // vertical hypenation + int row_origin; // row span (not yet implemented) + int col_origin; // column span (not yet implemented) + bool has_lazy_cells; // true if any cell has a lazy stream table_rep (edit_env env, int status, int i0, int j0); ~table_rep (); diff --git a/tests/Typeset/Table/table_performance_test.cpp b/tests/Typeset/Table/table_performance_test.cpp index 802587a4f7..2dfdd9608e 100644 --- a/tests/Typeset/Table/table_performance_test.cpp +++ b/tests/Typeset/Table/table_performance_test.cpp @@ -168,6 +168,8 @@ private slots: void test_handle_decorations_performance (); void test_cell_hyphen_wrapping (); void test_cell_hyphen_multi_column (); + void test_lazy_cells_flag_simple (); + void test_lazy_cells_flag_with_hyphen (); void cleanupTestCase (); }; @@ -727,6 +729,46 @@ TestTablePerformance::test_cell_hyphen_multi_column () { } } +void +TestTablePerformance::test_lazy_cells_flag_simple () { + cache_refresh (); + edit_env env= create_test_env (); + + // Simple text table without cell-hyphen: has_lazy_cells should be false + tree simple_table (TFORMAT, tree (TABLE, 1)); + tree simple_row (ROW, 1); + simple_row[0] = tree (CELL, "hello"); + simple_table[0][0]= simple_row; + + table tab (env); + tab->typeset (simple_table, path ()); + tab->handle_decorations (); + tab->handle_span (); + + QVERIFY (!tab->has_lazy_cells); +} + +void +TestTablePerformance::test_lazy_cells_flag_with_hyphen () { + cache_refresh (); + edit_env env= create_test_env (); + + // Table with cell-hyphen enabled: has_lazy_cells should be true + tree table_wrap (TFORMAT); + table_wrap << tree (CWITH, "1", "1", "1", "1", "cell-hyphen", "t"); + table_wrap << tree (TABLE, 1); + tree row_wrap (ROW, 1); + row_wrap[0] = tree (CELL, tree (DOCUMENT, "some text content")); + table_wrap[1][0]= row_wrap; + + table tab (env); + tab->typeset (table_wrap, path ()); + tab->handle_decorations (); + tab->handle_span (); + + QVERIFY (tab->has_lazy_cells); +} + void TestTablePerformance::cleanupTestCase () {}