ETAF Performance Monitoring (etaf-perf) 是一个用于优化 ETAF 首屏加载时间的性能监控工具。它可以跟踪渲染管道每个阶段的执行时间,帮助开发者识别性能瓶颈并进行优化。
ETAF Performance Monitoring (etaf-perf) is a performance monitoring tool designed to optimize ETAF's first-screen loading time. It tracks the execution time of each stage in the rendering pipeline, helping developers identify performance bottlenecks and optimize accordingly.
etaf-perf 监控以下渲染管道阶段:
- check-dynamic-content - 检查模板是否包含动态内容
- etml-to-dom - ETML 转换为 DOM(静态模板)
- etml-compile-and-render - ETML 编译和渲染(动态模板)
- build-stylesheet - 构建样式表
- build-cssom - 构建 CSS 对象模型
- add-stylesheet - 添加额外样式表
- build-render-tree - 构建渲染树
- build-layout-tree - 构建布局树
- layout-to-string - 布局转换为字符串
- ✅ 自动跟踪每个渲染阶段的执行时间
- ✅ 历史记录保存(可配置最大保存数量)
- ✅ 计算平均性能指标
- ✅ 生成详细的性能报告
- ✅ 性能瓶颈分析和优化建议
- ✅ 静态模板 vs 动态模板性能对比
- ✅ 零性能开销(禁用时)
etaf-perf 模块已包含在 ETAF 中,无需额外安装:
(require 'etaf-perf);; 1. 启用性能监控(单个命令)
(etaf-perf-toggle)
;; 2. 正常渲染内容(自动收集性能数据)
(etaf-paint-to-buffer "*demo*"
'(div :class "container"
(h1 "Hello ETAF!")
(p "Performance monitoring is active.")))
;; 3. 查看性能报告
(etaf-perf-show-report)
;; 4. 分析性能瓶颈
(etaf-perf-analyze)
;; 5. 清除数据
(etaf-perf-clear)
;; 6. 禁用性能监控(再次切换)
(etaf-perf-toggle);; 获取最后一次测量
(etaf-perf-get-last)
;; 获取最近 N 次测量的平均值
(etaf-perf-get-average 10)
;; 显示包含平均值的报告
(etaf-perf-show-report 10)(etaf-perf-toggle) ; Enable monitoring
(etaf-perf-start)
;; 测量特定操作
(etaf-perf-measure my-operation
(some-expensive-function))
(etaf-perf-measure another-operation
(another-function))
(etaf-perf-finish) ; 返回总执行时间(require 'etaf-perf)
(etaf-perf-toggle)
(etaf-perf-clear)
;; 渲染简单模板
(etaf-paint-to-buffer "*perf-test*"
'(div :class "container"
(h1 "Performance Test")
(p "Testing performance monitoring.")
(ul
(li "Item 1")
(li "Item 2")
(li "Item 3"))))
;; 查看报告
(etaf-perf-show-report)(etaf-perf-toggle)
(etaf-perf-clear)
;; 渲染复杂模板
(etaf-paint-to-buffer "*perf-tailwind*"
'(div :class "flex flex-col items-center w-800px bg-gray-100 p-4"
(div :class "bg-white rounded-lg shadow-md p-4 mb-4"
(h1 :class "text-2xl font-bold text-gray-900 mb-2"
"Tailwind Performance")
(p :class "text-gray-600"
"Testing with Tailwind classes."))
(div :class "grid grid-cols-3 gap-4 w-full"
(div :class "bg-blue-500 text-white p-3 rounded"
(h3 :class "font-bold mb-1" "Fast")
(p "Stage 1"))
(div :class "bg-green-500 text-white p-3 rounded"
(h3 :class "font-bold mb-1" "Faster")
(p "Stage 2"))
(div :class "bg-purple-500 text-white p-3 rounded"
(h3 :class "font-bold mb-1" "Fastest")
(p "Stage 3")))))
(etaf-perf-show-report)(etaf-perf-toggle)
(etaf-perf-clear)
(let ((data '(:title "Dynamic Content"
:items ("Apple" "Banana" "Cherry" "Date")
:count 4)))
(etaf-paint-to-buffer "*perf-dynamic*"
'(div
(h1 "{{ title }}")
(p "Total items: {{ count }}")
(ul
(li :e-for "item in items" "• {{ item }}")))
data))
(etaf-perf-show-report)(etaf-perf-toggle)
(etaf-perf-clear)
;; 运行 20 次渲染
(dotimes (i 20)
(etaf-paint-to-buffer "*perf-batch*"
`(div :class "p-4"
(h1 ,(format "Render #%d" (1+ i)))
(div :class "grid grid-cols-4 gap-2"
,@(cl-loop for j from 1 to 16
collect `(div :class "bg-blue-100 p-2"
,(format "Item %d" j)))))))
;; 查看平均性能
(etaf-perf-show-report 20)(etaf-perf-toggle)
;; 测试静态模板
(etaf-perf-clear)
(dotimes (i 10)
(etaf-paint-to-buffer "*static*"
'(div (h1 "Static") (p "Content"))))
(let ((static-time (plist-get (etaf-perf-get-average) :total)))
;; 测试动态模板
(etaf-perf-clear)
(dotimes (i 10)
(etaf-paint-to-buffer "*dynamic*"
'(div (h1 "{{ title }}") (p "{{ text }}"))
'(:title "Dynamic" :text "Content")))
(let ((dynamic-time (plist-get (etaf-perf-get-average) :total)))
(message "Static: %.2f ms, Dynamic: %.2f ms, Overhead: %.1f%%"
static-time
dynamic-time
(* 100.0 (/ (- dynamic-time static-time) static-time)))))性能报告包含以下信息:
=== ETAF Performance Report ===
Last Measurement:
Total Time: 15.4235 ms
Stages:
check-dynamic-content : 0.0512 ms ( 0.3%)
etml-to-dom : 2.1523 ms ( 13.9%)
build-stylesheet : 0.0234 ms ( 0.1%)
build-cssom : 3.4512 ms ( 22.4%)
add-stylesheet : 0.1845 ms ( 1.2%)
build-render-tree : 4.2312 ms ( 27.4%)
build-layout-tree : 3.8934 ms ( 25.2%)
layout-to-string : 1.4523 ms ( 9.4%)
Average of Last 10 Measurements:
Total Time: 14.8512 ms
Stages:
check-dynamic-content : 0.0445 ms ( 0.3%)
etml-to-dom : 2.0201 ms ( 13.6%)
build-stylesheet : 0.0223 ms ( 0.1%)
build-cssom : 3.2801 ms ( 22.1%)
add-stylesheet : 0.1734 ms ( 1.1%)
build-render-tree : 4.1512 ms ( 27.9%)
build-layout-tree : 3.7601 ms ( 25.3%)
layout-to-string : 1.4112 ms ( 9.5%)
Total Measurements: 10
使用 etaf-perf-analyze 自动识别占用超过 30% 时间的阶段:
(etaf-perf-analyze)输出示例:
Performance Bottlenecks:
- build-render-tree takes 32.5% of total time (4.85 ms)
- build-layout-tree takes 30.2% of total time (4.51 ms)
根据监控数据,可以采取以下优化措施:
-
CSSOM 构建较慢
- 减少 CSS 规则数量
- 使用 CSS 选择器索引(已自动优化)
- 使用样式缓存
-
渲染树构建较慢
- 简化 DOM 结构
- 减少嵌套层级
- 使用虚拟 DOM diff(动态模板)
-
布局计算较慢
- 减少 Flexbox 嵌套
- 使用固定尺寸而非自动计算
- 避免复杂的布局结构
-
动态模板编译较慢
- 尽可能使用静态模板
- 缓存编译后的渲染函数
- 减少模板指令使用
(etaf-perf-toggle)- 开启/关闭性能监控(推荐使用,自动处理钩子安装)(etaf-perf-clear)- 清除所有性能数据
(etaf-perf-start)- 开始新的测量会话(etaf-perf-record-stage STAGE START-TIME)- 记录阶段时间(etaf-perf-finish)- 完成测量并保存到历史(etaf-perf-measure STAGE &rest BODY)- 测量代码块执行时间
(etaf-perf-get-last)- 获取最后一次测量(etaf-perf-get-average &optional N)- 获取平均性能指标(etaf-perf-report &optional N)- 生成性能报告字符串(etaf-perf-show-report &optional N)- 在缓冲区显示报告(etaf-perf-analyze)- 分析性能瓶颈
仅在需要细粒度控制时使用:
(etaf-perf-enable)- 仅启用监控(不安装钩子)(etaf-perf-disable)- 仅禁用监控(不卸载钩子)(etaf-perf-install-hooks)- 手动安装监控钩子(etaf-perf-uninstall-hooks)- 手动卸载监控钩子
etaf-perf-enabled- 性能监控是否启用etaf-perf-max-history- 最大历史记录数(默认 100)
- 禁用时: 零开销(宏在编译时展开为
progn) - 启用时: 每个阶段约 0.01-0.05ms 的测量开销
- 建议: 仅在开发和调试时启用,生产环境禁用
-
开发阶段
;; 在 init.el 中 (when (getenv "ETAF_PERF") (etaf-perf-toggle)
-
性能测试
;; 运行多次以获得稳定的平均值 (dotimes (i 20) (etaf-paint-to-buffer "*test*" template)) (etaf-perf-show-report 20)
-
持续监控
;; 定期检查性能趋势 (add-hook 'after-init-hook (lambda () (when etaf-perf-enabled (run-with-timer 300 300 'etaf-perf-analyze))))
解决方案:
;; 确保监控已启用
(etaf-perf-toggle)
;; 检查状态
(message "Enabled: %s, History: %d"
etaf-perf-enabled
(length etaf-perf-history))解决方案:
- 运行多次测量并查看平均值
- 确保没有其他进程占用 CPU
- 在测量前清除缓存
(etaf-perf-clear)
GNU General Public License v3.0 or later.