bigywater.tech
Flask · 65

模板中执行函数的性能问题

模板里写 {{ fmt(price) }} 有多贵?宏和 include 的代价呢?什么该在模板里算、什么该在视图里先算好?这篇对 1000 行表格做一组苹果对苹果的对比(输出相同,只改写法)。

206×
模板里每行调用一个 0.1 ms 慢函数,200 行渲染相对不调用的倍数
3.2×
宏(每行 3 次调用)相对直接写的耗时倍数
1000
1000 行循环里全局函数被调用的次数

函数调用的代价

实测 · 本机执行输出
  预先在 Python 里算好 r.price_text(基线)                1.927 ms  ( 1.00×)
  全局函数 fmt_price(r.price)                        2.715 ms  ( 1.41×)
  过滤器 r.price|fmt                                2.245 ms  ( 1.16×)
  对象方法 r.price_text()                            2.003 ms  ( 1.04×)

上一篇:Jinja2 Bytecode Cache;下一篇:Flask Config 加载顺序。宏与 include 的写法见Jinja2 宏。

宏与 include

实测 · 本机执行输出
  直接写                                            2.084 ms  ( 1.00×)
  macro cell() 每行 3 次调用                          6.643 ms  ( 3.19×)
  每行 include 一个模板                                4.278 ms  ( 2.05×)

set 复用

实测 · 本机执行输出
  set:1.521 ms;不 set(算三遍):2.119 ms;不 set 是 set 的 1.39 倍

集合运算的位置

实测 · 本机执行输出
  selectattr(price>500):模板里做 0.885 ms;Python 里先过滤再渲染 0.443 ms(这一步 Python 过滤本身另需约 0.03 ms)
  sort(price 降序):模板里做 0.949 ms;Python 里先排序再渲染 0.653 ms

调用次数

实测 · 本机执行输出
  渲染 3 行,全局函数调用 3 次
  渲染 1000 行,全局函数调用 1000 次

慢函数

实测 · 本机执行输出
  不调用慢函数 0.152 ms;每行调用一次慢函数 31.44 ms(约 206 倍);200 行 × 0.1 ms ≈ 20 ms 的纯等待(sleep 实际约 0.15 ms)

分步演示

模板性能
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / Python 3.14.7 / Flask 3.1.3 / Werkzeug 3.1.9 / Jinja2 3.1.6 / SQLAlchemy 2.1.1 / gunicorn 26.2.0(都装在自己的虚拟环境里);大多数实验用 Flask 的 test_client(直接调用 WSGI 应用,不经过网络),需要真实 HTTP 时另起 gunicorn 或开发服务器(端口 9600 以上,本机 127.0.0.1);计时用 time.perf_counter,数字每次运行会有小幅抖动。每项取 3 组中的最小值,每组渲染 15 次;不同机器的绝对值会不同,看相对关系
    • 没有验证:更大的数据量(10 万行)下的内存占用与流式渲染、PyPy 或 free-threaded 构建下的差别、模板预编译成 Python 模块的效果、Jinja 的 {% for %} loop 变量的代价(本次实验里不同输出的对照已去掉)
    京ICP备2026067502号-1京公网安备11010502063487号