【问题标题】:Django - rendering many templates using templatetags is very slowDjango - 使用模板标签渲染许多模板非常慢
【发布时间】:2012-02-29 06:16:54
【问题描述】:

比如说,我有一个带有照片库的页面。每个缩略图都有例如照片、国家、作者等。我使用模板标签(加载指定的模板)呈现这些项目/小部件 - 因为 DRY(我在页面上的不同位置分别使用这些项目/小部件),所以会这样。

而且速度很慢。

我使用 django-debug-toolbar 进行了一些分析:

SQL Queries: default 84.81 ms (147 queries)

但是:

Total CPU time: 5768.360 msec

等待的时间太长了。

经过一番分析,原来罪魁祸首是模板引擎。

当我想显示时正在通过模板呈现 150 张照片、600 个相关项目/小部件。这意味着 600 次 I/O 操作甚至更多。将这些小部件移动到主模板可以解决问题,但不会保持 DRY。

所以我的问题是如何避免这种行为?干燥且缓慢或不干燥且快速? 我宁愿干而且快...

【问题讨论】:

  • 能否提供templatetag代码?
  • 花在查询上的时间相对来说很少。但是 ORM 需要更多时间来生成这些查询并将结果解析为模型实例

标签: django optimization django-templates


【解决方案1】:

经过几个小时的分析和搜索...

感谢您的帮助,但在我看来,到目前为止最好的解决方案是使用Template fragment caching

我试了一下,速度提升了 70-80%!

{% load cache %}
{% cache 3600 mywidget_id %}
    .. rendered mywidget is cached ..
{% endcache %}

【讨论】:

    【解决方案2】:

    您可能想尝试caching template loaderdjango.template.loaders.cached.Loader - 它肯定会减少所需的 IO 量。

    编辑以添加您需要小心地假设仅仅因为大部分时间都花在模板呈现阶段,查询计数不应该受到指责。不要忘记查询集是惰性的,除非您在视图中专门对它们进行切片或迭代,否则它们只会在加载模板时进行评估。我想说的是,通过充分利用 select_related 和其他技术来减少查询次数应该会有很大帮助。

    【讨论】:

    • 谢谢。它将其降低到 5054.582 毫秒,即约 11% 的增益。然而,渲染仍然需要不可接受的 5 秒。这里还能做什么?
    • 第二次页面加载的表现如何?
    • 相同 - 最少 5 秒。
    • 这太令人惊讶了。缓存的模板加载器将解析后的模板存储在进程内存中,这意味着 I/O 可能不是瓶颈。您是否尝试过使用调试工具栏查看发生了什么?
    • 缓存模板加载器工作正常,因为模板中的更改不会实时显示。需要冲洗。将所有内容移动到主模板可以非常快地加载该页面(1 - 1.5 秒)。这就是为什么我认为它是 I/O。但正如我在问题中所说,它破坏了 DRY。
    【解决方案3】:

    我假设由于您使用的是调试工具栏,因此您正在开发中获得这些数字。然而,正因为如此,这些不是“真实”的数字。

    内置的 Django 服务器非常适合开发,但它有许多缺点使其比真正的网络服务器慢得多。首先,它是单线程的,所以这意味着没有并行请求。这也意味着 IO 操作是离散的。其次,它的任务不仅是为 Django 提供请求,还包括静态资源。

    总而言之,如果您想真正分析您的网站的页面加载时间,您需要在本地安装一个真正的网络服务器。基本上像在生产环境中一样设置它。那么,我愿意打赌请求时间会更好。

    【讨论】:

    • 注意,从 Django 1.4 开始,runserver 是线程化的,因此它可以处理并行请求
    【解决方案4】:

    这可能不适用于这个特定问题,但在某些情况下,它帮助我在查询中使用select_related。可能不是这样,但它可能会降低您的查询数。

    【讨论】:

    • select_related 不是在 84 毫秒的查询集上抛出的东西。这是关于模板的。
    【解决方案5】:

    花在查询上的时间相对较少。 但是 ORM 需要更多时间来生成这些查询并将结果解析到模型实例中。

    因此,尽管有大量查询,但由于 ORM 缓慢(在您的情况下可能需要一秒钟),您的应用仍受 CPU 限制。所以无论如何你都必须减少查询的数量。

    很可能查询是在您的模板标签内进行的。因此,您必须在几个查询中获得所需的数据,并将其设置在照片实例上。

    {% for photo in photos|annotate_comment_count %}
       ...
    {% endfor %}
    
    def annotate_comment_count(photo_list):
        counts = dict(Comment.objects.filter(photo__in=photo_list).values('photo') \
                                     .annotate(count=models.Count('id')))
        for photo in photo_list:
            photo.comments_count = counts[photo.pk]
        return photo_list
    

    因此,在您的模板标签中,您不必查询单张照片的 cmets 计数,但您已经在 comments_count 属性中获得了此信息。而您在一个查询中实现了这一目标。

    【讨论】:

      【解决方案6】:

      我遇到了同样的问题,也许是出于同样的原因。我优化了自定义模板标签的性能。 db 请求数从 640 下降到 2,产生的 db 时间不到 20ms。然而,我的页面变慢了! 7 秒 -> 10 秒。叹。我尝试了一个缓存的模板加载器,没有效果。

      我放弃了,禁用了django调试工具栏,之后响应时间降到了1.2s,难以置信!!就我而言,巨大的响应时间只是由调试工具栏引起的!可以在here 找到相关问题。我没有深入研究这个工具栏问题,因为我本来打算使用模板片段缓存来缓存模板标签。在开发过程中,我每 15 分钟有 10 秒的响应时间,我可以忍受。无论如何,希望这会有所帮助。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-05-20
        • 2019-09-26
        • 2022-07-22
        • 2018-11-25
        • 2010-12-15
        • 2011-12-01
        • 2011-06-15
        • 2014-03-20
        相关资源
        最近更新 更多