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
91 changes: 91 additions & 0 deletions devel/0162.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# [0162] 字体处理性能优化

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

## 2 任务相关的代码文件
- `src/Graphics/Fonts/smart_font.cpp`
- `src/Graphics/Fonts/find_font.cpp`
- `src/Graphics/Fonts/font_translate.cpp`
- `src/Data/String/unicode.cpp`
- `src/Data/String/unicode.hpp`
- `tests/Graphics/Fonts/smart_font_test.cpp`
- `tests/Graphics/Fonts/font_size_test.cpp`

## 3 如何测试

### 3.1 确定性测试(单元测试)
```bash
xmake b smart_font_test
xmake r smart_font_test
xmake b font_size_test
xmake r font_size_test
```

### 3.2 性能测试
```bash
xmake b smart_font_test
TM_DEBUG_BENCH=1 xmake r smart_font_test
```

## 4 如何提交

```bash
xmake b smart_font_test
xmake r smart_font_test
xmake b font_size_test
xmake r font_size_test
```

## 5 What
对字体处理代码做实验性质的性能优化,采用 TDD 方式开发。

## 6 Why
字体处理代码逻辑非常绕,性能有瓶颈。每次字符解析都涉及大量字符串操作和哈希查找,在文档渲染时成为热点。

## 7 How
- 先写性能基准测试
- 用 `bench_start`/`bench_end` 定位热点
- 针对性优化,每个小优化一个 commit
- 关键位置用 `cout` 加日志验证优化效果

## 8 已完成的优化

### Commit 1: 优化 smart_font resolve 避免重复 get_unicode_range 调用
- `resolve(string c)` 中对同一个字符的 `get_unicode_range` 调用提取到循环外,只计算一次后传给内层循环
- 新增 `resolve(string c, string range, string fam, int attempt)` 重载

### Commit 2: 缓存 get_unicode_range 和 get_utf8_code 结果避免重复 UTF-8 转换
- `unicode.cpp` 中新增 `unicode_range_cache` 和 `utf8_code_cache`
- `get_utf8_code` 改为委托给缓存版本 `get_utf8_code_cached`
- 避免对同一字符反复做 `strict_cork_to_utf8` + `decode_from_utf8`

### Commit 3: 优化 find_font 减少重复 as_string 调用
- `find_font(string family, ...)` 中缓存 `sz_str` 和 `dpi_str`
- 避免在构造 `t1/t2/t3/panic` 等 tree 时重复调用 `as_string`

### Commit 4: 优化 smart_font_bis 字符串构造并移除多余 normalize
- 缓存 `vdpi_str`/`hdpi_str`,减少字符串拼接
- 移除 `smart_font`/`math_smart_font`/`prog_smart_font` 中多余的 `normalize_half_multiple_size` 调用(已在调用方处理)

### Commit 5: 移除 get_extents/draw_fixed 中不必要的字符串拷贝
- `get_extents`/`get_xpositions`/`draw_fixed` 中 `string r= s` 改为 `string r`
- 避免循环中不必要的引用计数增减

### Commit 6: 优化 closest_font 减少重复 as_string 调用
- `closest_font` 中缓存 `dpi_str` 和 `attempt_str`

### Commit 7: 缓存 substitute_math_letter 结果避免重复调用
- `smart_font_rep` 中新增 `math_letter_cache`
- 避免数学字母替换的重复计算

### Commit 8: 将 resolve 内层循环中的不变检查提升到循环外
- `is_rubber(c)` 和 `starts(c, "<wide-")` 提升到循环外
- 避免对同一字符重复执行这些检查

### Commit 9: 添加 occurs 快速路径避免不必要 trimmed_tokenize
- `resolve(c, range, fam, attempt)` 中先检查 `occurs("=", fam)`
- 避免对不含 `=` 的 fam 执行无意义的 `trimmed_tokenize`

### Commit 10: 清理临时 LIII_DEBUG 日志
- 移除验证阶段添加的临时调试输出
34 changes: 31 additions & 3 deletions src/Data/String/unicode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,43 @@

#include <lolly/data/unicode.hpp>

static hashmap<string, string> unicode_range_cache ("");

string
get_unicode_range (string c) {
if (unicode_range_cache->contains (c)) return unicode_range_cache[c];
string uc= strict_cork_to_utf8 (c);
if (N (uc) == 0) return "";
if (N (uc) == 0) {
unicode_range_cache (c)= "";
return "";
}
int pos = 0;
uint32_t code = lolly::data::decode_from_utf8 (uc, pos);
string range= lolly::data::unicode_get_range (code);
if (pos == N (uc)) return range;
return "";
if (pos != N (uc)) range= "";
unicode_range_cache (c)= range;
return range;
}

static hashmap<string, int> utf8_code_cache (-1);

int
get_utf8_code_cached (string c) {
if (utf8_code_cache->contains (c)) return utf8_code_cache[c];
int c_N= N (c);
if (c_N <= 2 || c_N > 6) {
utf8_code_cache (c)= -1;
return -1;
}
string uc = strict_cork_to_utf8 (c);
int pos = 0;
int code= lolly::data::decode_from_utf8 (uc, pos);
if (pos == c_N) {
utf8_code_cache (c)= code;
return code;
}
utf8_code_cache (c)= -1;
return -1;
}

bool
Expand Down
1 change: 1 addition & 0 deletions src/Data/String/unicode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "string.hpp"

string get_unicode_range (string c);
int get_utf8_code_cached (string c);
bool is_emoji_character (int uc);

#endif
24 changes: 11 additions & 13 deletions src/Graphics/Fonts/find_font.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -220,12 +220,12 @@ find_magnified_font (tree t, double zoomx, double zoomy) {
font
find_font (string family, string variant, string series, string shape,
double sz, int dpi) {
// 浮点尺寸字符串处理:整数如"10",0.5倍数如"10.5"
string sz_str;
if (sz == round (sz)) sz_str= as_string ((int) sz); // 整数
else sz_str= as_string (sz); // 0.5倍数,保留一位小数
if (sz == round (sz)) sz_str= as_string ((int) sz);
else sz_str= as_string (sz);
string dpi_str= as_string (dpi);
string s= family * "-" * variant * "-" * series * "-" * shape * "-" * sz_str *
"-" * as_string (dpi);
"-" * dpi_str;
if (font::instances->contains (s)) return font (s);

if (ends (shape, "-poorit")) {
Expand Down Expand Up @@ -284,10 +284,8 @@ find_font (string family, string variant, string series, string shape,
t1[1]= variant;
t1[2]= series;
t1[3]= shape;
// 浮点尺寸字符串处理
if (sz == round (sz)) t1[4]= as_string ((int) sz); // 整数
else t1[4]= as_string (sz); // 0.5倍数,保留一位小数
t1[5] = as_string (dpi);
t1[4]= sz_str;
t1[5] = dpi_str;
font fn= find_font (t1);
if (!is_nil (fn)) {
font::instances (s)= (pointer) fn.rep;
Expand All @@ -298,8 +296,8 @@ find_font (string family, string variant, string series, string shape,
t2[0]= family;
t2[1]= variant;
t2[2]= series;
t2[3]= as_string (sz);
t2[4]= as_string (dpi);
t2[3]= sz_str;
t2[4]= dpi_str;
fn = find_font (t2);
if (!is_nil (fn)) {
font::instances (s)= (pointer) fn.rep;
Expand All @@ -309,15 +307,15 @@ find_font (string family, string variant, string series, string shape,
tree t3 (TUPLE, 4);
t3[0]= family;
t3[1]= variant;
t3[2]= as_string (sz);
t3[3]= as_string (dpi);
t3[2]= sz_str;
t3[3]= dpi_str;
fn = find_font (t3);
if (!is_nil (fn)) {
font::instances (s)= (pointer) fn.rep;
return fn;
}

tree panic (TUPLE, "tex", "cmr", as_string (sz), as_string (dpi));
tree panic (TUPLE, "tex", "cmr", sz_str, dpi_str);
fn = find_font (panic);
font::instances (s)= (pointer) fn.rep;
return fn;
Expand Down
9 changes: 5 additions & 4 deletions src/Graphics/Fonts/font_translate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -290,11 +290,12 @@ find_closest (string& family, string& variant, string& series, string& shape,
font
closest_font (string family, string variant, string series, string shape,
double sz, int dpi, int attempt) {
// 浮点尺寸字符串处理:整数如"10",0.5倍数如"10.5"
string sz_str;
if (sz == round (sz)) sz_str= as_string ((int) sz); // 整数
else sz_str= as_string (sz); // 0.5倍数,保留一位小数
string extra= sz_str * "-" * as_string (dpi) * "-" * as_string (attempt);
if (sz == round (sz)) sz_str= as_string ((int) sz);
else sz_str= as_string (sz);
string dpi_str = as_string (dpi);
string attempt_str= as_string (attempt);
string extra = sz_str * "-" * dpi_str * "-" * attempt_str;
string s= family * "-" * variant * "-" * series * "-" * shape * "-" * extra;
if (font::instances->contains (s)) return font (s);
string orig_family= family;
Expand Down
Loading
Loading