Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions devel/0124.md
Original file line number Diff line number Diff line change
@@ -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`
- 原有性能测试和正确性测试均继续通过
40 changes: 40 additions & 0 deletions devel/0124.tmu
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<TMU|<tuple|1.1.0|2026.2.5>>

<style|<tuple|generic|chinese>>

<\body>
<doc-data|<doc-title|表格性能优化测试文档>>

0124 任务:优化表格 position_columns 中 has_lazy_cells 的重复扫描问题。

<section|简单文本表格>

以下是一个 5x5 的简单文本表格,用于验证基本排版正确性:

<\table>
<tformat|<table|<row|<cell|A1>|<cell|B1>|<cell|C1>|<cell|D1>|<cell|E1>>|<row|<cell|A2>|<cell|B2>|<cell|C2>|<cell|D2>|<cell|E2>>|<row|<cell|A3>|<cell|B3>|<cell|C3>|<cell|D3>|<cell|E3>>|<row|<cell|A4>|<cell|B4>|<cell|C4>|<cell|D4>|<cell|E4>>|<row|<cell|A5>|<cell|B5>|<cell|C5>|<cell|D5>|<cell|E5>>>>
</table>

<section|开启 cell-hyphen 的表格>

以下表格开启了 cell-hyphen,用于验证 has_lazy_cells 缓存机制下列宽自适应仍然正确:

<\table>
<tformat|<cwith|1|-1|1|-1|cell-hyphen|t>|<table|<row|<cell|这是一段比较长的文本内容,用于测试自动换行功能是否正常>|<cell|短文本>>|<row|<cell|另一段长文本,需要验证在多列情况下列宽是否会超出页面宽度>|<cell|测试>>|<row|<cell|第三行长文本内容,确保缓存优化后排版结果一致>|<cell|OK>>>>
</table>

<section|包含子表格的复杂表格>

<\table>
<tformat|<table|<row|<cell|普通单元格>|<cell|<math|<matrix|<tformat|<table|<row|<cell|a>|<cell|b>>|<row|<cell|c>|<cell|d>>>>>>>>|<row|<cell|另一行>|<cell|子表格上方>>>>
</table>
</body>

<\initial>
<\collection>
<associate|font|roman>
<associate|font-family|rm>
<associate|page-medium|paper>
<associate|page-screen-margin|false>
</collection>
</initial>
16 changes: 9 additions & 7 deletions src/Typeset/Table/table.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ lazy make_lazy_paragraph (edit_env env, array<box> 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) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
7 changes: 4 additions & 3 deletions src/Typeset/Table/table.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 ();
Expand Down
42 changes: 42 additions & 0 deletions tests/Typeset/Table/table_performance_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 ();
};

Expand Down Expand Up @@ -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 () {}

Expand Down
Loading