stencilproject/Stencil

Critical performance issue

Closed this issue · 14 comments

After refactoring my app, I'm seeing extreme rendering times, as bad as 14s sometimes. I'm not entirely sure what causes this, but I wanted to report it already, as I believe the issue lies with Stencil.

Here's what I have so far:

  • I'm using Stencil 0.11.
  • I didn't notice any issue in development. On macOS, 0.11 works fine and render times are < 100ms usually.
  • When deploying the same code to IBM Cloud, the render times become horrible (1-15s). This leads me to believe it's a Linux-specific issue. I'm not sure about that however, as I haven't been able to reproduce it on Linux via Docker, only when deploying to cloud.
  • I did not have this issue before when I was using 0.10.1.
  • I was able to reproduce the issue to a lesser extent on macOS as well. If I use the latest master, my render times jump over 1s as well.
  • I've added logging all throughout my handlers as well as Kitura and didn't notice any issues there. Almost all of the delay is caused by Stencil. I went as far as Template.render but haven't narrowed it down further yet.
  • All my templates uses inheritance. I'm having other issues with inheritance on master as well, so perhaps it related?

I would really appreciate any help in tracking down and resolving this issue as I need to release this app in the next few weeks.

I've also tried reverting back to 0.10, but that's giving me a compilation error on Linux I've yet to resolve:

PathKit.swift:299:11: error: cannot convert value of type 'ObjCBool' to expected argument type 'Bool'

I've logged every render separately and came up with the following results:

 TextNode in 0ms
 TextNode in 0ms
 TextNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 IfNode in 1ms
 TextNode in 0ms
 BlockNode in 62ms
 TextNode in 0ms
 VariableNode in 19ms
 TextNode in 0ms
 VariableNode in 19ms
 TextNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 BlockNode in 3ms
 TextNode in 0ms
 BlockNode in 0ms
 TextNode in 0ms
 IfNode in 0ms
 TextNode in 0ms
 TextNode in 0ms
 IfNode in 2ms
 TextNode in 0ms
 TextNode in 0ms
 IfNode in 0ms
 TextNode in 0ms
 TextNode in 0ms
 TextNode in 0ms
 IfNode in 0ms
 TextNode in 0ms
 TextNode in 0ms
 IfNode in 0ms
 TextNode in 0ms
 TextNode in 0ms
 IfNode in 0ms
 TextNode in 0ms
 TextNode in 0ms
 IfNode in 0ms
 TextNode in 0ms
 IfNode in 0ms
 TextNode in 0ms
 TextNode in 0ms
 TextNode in 0ms
 TextNode in 0ms
 IfNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 58ms
 TextNode in 0ms
 VariableNode in 3ms
 TextNode in 0ms
 VariableNode in 141ms
 TextNode in 0ms
 VariableNode in 45ms
 TextNode in 0ms
 VariableNode in 4ms
 TextNode in 0ms
 VariableNode in 5ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 IfNode in 16ms
 TextNode in 0ms
 TextNode in 0ms
 TextNode in 0ms
 IfNode in 37ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 11ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 267ms
 TextNode in 0ms
 IfNode in 0ms
 TextNode in 0ms
 TextNode in 0ms
 TextNode in 0ms
 IfNode in 66ms
 TextNode in 0ms
 VariableNode in 1ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 12ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 22ms
 TextNode in 0ms
 VariableNode in 2ms
 TextNode in 0ms
 VariableNode in 1ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 8ms
 TextNode in 0ms
 VariableNode in 4ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 IfNode in 0ms
 TextNode in 0ms
 TextNode in 0ms
 TextNode in 0ms
 IfNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 15ms
 TextNode in 0ms
 VariableNode in 1ms
 TextNode in 0ms
 VariableNode in 1ms
 TextNode in 0ms
 VariableNode in 8ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 IfNode in 0ms
 TextNode in 0ms
 TextNode in 0ms
 TextNode in 0ms
 IfNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 35ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 1ms
 TextNode in 0ms
 IfNode in 0ms
 TextNode in 0ms
 TextNode in 0ms
 TextNode in 0ms
 IfNode in 0ms
 TextNode in 0ms
 VariableNode in 2ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 3ms
 TextNode in 0ms
 VariableNode in 1ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 VariableNode in 0ms
 TextNode in 0ms
 IfNode in 0ms
 TextNode in 0ms
 ForNode in 1263ms
 TextNode in 0ms
 IfNode in 1263ms
 TextNode in 0ms
 BlockNode in 1269ms
 TextNode in 0ms
 BlockNode in 0ms
 TextNode in 0ms
 ExtendsNode in 2781ms

So the performance seems to be bad in general.

This is with 0.11, pushed to IBM Cloud, rendering the following template:

{% extends "base.stencil" %}

{% block title %}
    {% if sort == "new" %}
        {{ block.super }} - Nieuwste spellen
    {% elif sort == "upcoming" %}
        {{ block.super }} - Binnenkort op de agenda
    {% elif sort == "near-me" %}
        {{ block.super }} - In mijn buurt
    {% endif %}
{% endblock %}

{% block content %}
<div class="d-flex justify-content-center">
    <!-- Sort options -->
    <ul class="pagination mr-md-5">
        {% if sort == "new" %}
            <li class="page-item active">
                <span class="page-link">
                    <i class="fa fa-asterisk d-none d-sm-inline"></i> Nieuw
                </span>
            </li>
        {% else %}
            <li class="page-item">
                <a class="page-link" href="/web/activities?sort=new">
                    <i class="fa fa-asterisk d-none d-sm-inline"></i> Nieuw
                </a>
            </li>
        {% endif %}
        {% if sort == "upcoming" %}
            <li class="page-item active">
                <span class="page-link">
                    <i class="fa fa-calendar d-none d-sm-inline"></i> Binnenkort
                </span>
            </li>
        {% else %}
            <li class="page-item">
                <a class="page-link" href="/web/activities?sort=upcoming">
                    <i class="fa fa-calendar d-none d-sm-inline"></i> Binnenkort
                </a>
            </li>
        {% endif %}
        {% if sort == "near-me" %}
            <li class="page-item active">
                <span class="page-link">
                    <i class="fa fa-map-marker d-none d-sm-inline"></i> Dichtbij
                </span>
            </li>
        {% else %}
            <li class="page-item">
                <a class="page-link" href="/web/activities?sort=near-me">
                    <i class="fa fa-map-marker d-none d-sm-inline"></i> Dichtbij
                </a>
            </li>
        {% endif %}
    </ul>
    <!-- View options, only visible in md and above -->
    <ul class="pagination d-none d-md-flex">
        <li class="page-item">
            <a class="page-link" href="/web/activities?view=grid">
                <i class="fa fa-th-large"></i> Raster
            </a>
        </li>
        <li class="page-item active">
            <span class="page-link">
                <i class="fa fa-list"></i> Lijst
            </span>
        </li>
    </ul>
</div>
<!-- Title -->
{% if sort == "new" %}
    <h2>Nieuwste spellen</h2>
{% elif sort == "upcoming" %}
    <h2>Binnenkort op de agenda</h2>
{% elif sort == "near-me" %}
    <h2>In mijn buurt</h2>
{% endif %}
<!-- Link to user activities -->
{% if base.user %}
    <div class="alert alert-info">
        Spellen die je zelf organiseert worden niet getoond op deze pagina.
        Deze spellen zijn te vinden in je persoonlijk menu, onder <a class="alert-link" href="/web/user/activities">Mijn spellen</a>.
    </div>
{% endif %}
<!-- Check if a location is set when showing activities near the user -->
{% if sort == "near-me" and not base.user.location %}
    <div class="alert alert-warning">
        {% if base.user %}
            Om deze functie te activeren moet je een adres ingeven bij <a class="alert-link" href="/web/user/settings">Instellingen</a>.
        {% else %}
            Om deze functie te activeren moet je eerst <a class="alert-link" href="/authentication/welcome?redirect=%2Fweb%2Factivities">aanmelden</a>.
            Daarna kan je een adres ingeven bij <a class="alert-link" href="/web/user/settings">Instellingen</a>.
        {% endif %}
    </div>
<!-- Activities -->
{% elif activities %}
    {% for activity in activities %}
        <a class="{% if activity.availableSeats == 0 %} red {% elif activity.availableSeats == 1 %} yellow {% else %} green {% endif %} autoborder item"
            href="/web/activity/{{ activity.id }}">
            <!-- Separate image sizing for xs, sm and md -->
            <img class="d-sm-none align-self-start" width="75" src="{{ activity.thumbnail }}">
            <img class="d-none d-sm-flex d-md-none align-self-start" width="150" src="{{ activity.picture }}">
            <img class="d-none d-md-flex align-self-start" width="200" src="{{ activity.picture }}">
            <div class="body">
                <h5>{{ activity.name }}</h5>
                <p>
                    <i class="fa fa-calendar"></i>
                    <!-- xs shows abbreviated weekday -->
                    <span class="d-sm-none">{{ activity.shortDate }}</span>
                    <!-- sm shows the full weekday -->
                    <span class="d-none d-sm-inline">{{ activity.longDate }}</span>
                    <!-- md adds the time -->
                    <span class="d-none d-md-inline">om {{ activity.time }}</span>
                    <br>
                    <!-- Show the time separately in xs and sm -->
                    <span class="d-md-none">
                        <i class="fa fa-clock-o"></i> {{ activity.time }}<br>
                    </span>
                    <i class="fa fa-user"></i> {{ activity.host.name }}<br>
                    <i class="fa fa-map-marker"></i> {{ activity.location.city }}
                    {% if base.user.location %}
                        ({{ activity.distance }}km)
                    {% endif %}
                </p>
            </div>
        </a>
    {% endfor %}
<!-- Placeholder -->
{% else %}
    <p>Geen spellen gepland.</p>
{% endif %}
{% endblock %}

With the following parent template:

<!DOCTYPE html>
<html lang="nl">
<head>
    <title>{% block title %}Rond De Tafel{% endblock %}</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <meta name="author" content="Steven Van Impe">
    <meta name="description" content="Rond De Tafel brengt mensen en spellen samen. Vind spellen, zoek medespelers en maak nieuwe vrienden.">
    <meta property="og:site_name" content="Rond De Tafel">
    <meta property="og:type" content="website">
    <meta property="fb:app_id" content="{{ base.facebook.app }}">
    <meta property="og:description" content="Rond De Tafel brengt mensen en spellen samen. Vind spellen, zoek medespelers en maak nieuwe vrienden.">
    <meta property="og:url" content="{{ base.opengraph.url }}">
    {% block opengraph %}
        <meta property="og:title" content="Rond De tafel">   
        <meta property="og:image" content="{{ base.opengraph.image }}">
    {% endblock %}
    <link href="/public/img/favicon.png" rel="icon" type="image/png">
    <link href="/public/css/bootstrap.min.css" rel="stylesheet">
    <link href="/public/css/font-awesome.min.css" rel="stylesheet">
    <link href="/public/css/pikaday.min.css" rel="stylesheet">
    <style>
        .navbar, .btn-primary {
            background-color: #2185D0;
        }
        h2, h3 {
            margin-bottom: 1rem;
        }
        img.avatar {
            width: 40px;
            height: 40px;
        }
        /* Pagination, used for sort and view options. */
        .page-link {
            color: #2185D0;
        }
        .page-item.active .page-link {
            background-color: #2185D0;
            border-color: #2185D0;
        }
        /* Activity cards, used in grid view. */
        a.card {
            width: 100%;
            color: inherit;
            text-decoration: none;
        }
        a.card.green {
            box-shadow: 0 1px 0 0 #21BA45;
        }
        a.card.yellow {
            box-shadow: 0 1px 0 0 #FBBD08;
        }
        a.card.red {
            box-shadow: 0 1px 0 0 #DB2828;
        }
        a.card .card-body {
            line-height: 2em;
        }
        /* Activity items, used in list view. */
        a.item {
            display: flex;
            color: inherit;
            text-decoration: none;
            border-radius: 0.25rem;
            margin-bottom: 1rem;
        }
        a.item:focus, a.item:hover {
            background-color: #f8f9fa;
        }
        a.item.green {
            box-shadow: 1px 0 0 0 #21BA45;
        }
        a.item.yellow {
            box-shadow: 1px 0 0 0 #FBBD08;
        }
        a.item.red {
            box-shadow: 1px 0 0 0 #DB2828;
        }
        a.item .body {
            display: flex;
            flex-direction: column;
            margin-left: 1rem;
            line-height: 2em;
        }
        @media(min-width:768px) {
            a.item.autoborder {
                border: 1px solid rgba(0, 0, 0, 0.125);
            }
            a.item.autoborder .body {
                margin-top: 0.5rem;
            }
        }
        /* Conversations and messages */
        a.conversation {
            display: flex;
            color: inherit;
            text-decoration: none;
        }
        a.conversation .body {
            display: flex;
            flex-direction: column;
            margin-left: 1rem;
        }
        @media(min-width:768px) {
            .messages {
                margin-left: 2rem;
            }
        }
        /* Margins for icons */
        .fa-calendar, .fa-clock-o, .fa-envelope, .fa-hourglass-o, .fa-map-marker, .fa-user, .fa-users {
            margin-right: 0.25rem;
        }
        .fa-home {
            margin-left: 0.25rem;
        }
        /* Don't use margins in input groups or buttons */
        button > i.fa, .input-group-text > i.fa {
            margin-right: 0;
        }
        .fa-facebook-official {
            color: #29487d;
        }
    </style>
    <!-- jQuery is included in head because it's used by included components in body. -->
    <script src="/public/js/jquery.min.js"></script>
    {% block additional-head %}{% endblock %}
</head>
<body>
    <!-- Collapsed navbar -->
    <nav class="d-md-none navbar navbar-dark">
        <a class="navbar-brand" href="/web/home">Rond De Tafel</a>
        <ul class="navbar-nav ml-auto mr-3">
            {% if base.unreadMessageCount > 0 %}
                <li class="nav-item">
                    <a class="nav-link" href="/web/user/messages">
                        <i class="fa fa-envelope"></i>
                    </a>
                </li>
            {% endif %}
        </ul>
        <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarResponsive">
            <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarResponsive">
            <ul class="navbar-nav">
                <li class="nav-item">
                    <a class="nav-link" href="/web/activities">Spellen</a>
                </li>
                <li class="nav-item">
                    <a class="nav-link" href="/web/host">Organiseer</a>
                </li>
                {% if base.user %}
                    <li class="nav-item">
                        <a class="nav-link" href="/web/user/activities">Mijn spellen</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="/web/user/messages">
                            Berichten
                            {% if base.unreadMessageCount > 0 %}
                                ({{ base.unreadMessageCount }})
                            {% endif %}
                        </a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="/web/user/settings">Instellingen</a>
                    </li>
                    <li class="nav-item">
                        <a class="nav-link" href="/authentication/signout">Afmelden</a>
                    </li>
                {% else %}
                    <li class="nav-item">
                        <!-- The href will be set in code -->
                        <a class="global-signin nav-link" href="#">Aanmelden</a>
                    </li>
                {% endif %}
                <li class="nav-item">
                    <a class="nav-link" href="/web/faq">Help</a>
                </li>
            </ul>
        </div>
    </nav>
    <!-- Full navbar -->
    <nav class="d-none d-md-flex navbar navbar-expand navbar-dark">
        <a class="navbar-brand" href="/web/home">Rond De Tafel</a>
        <ul class="navbar-nav mr-auto">
            <li class="nav-item">
                <a class="nav-link" href="/web/activities">Spellen</a>
            </li>
            <li class="nav-item">
                <a class="nav-link" href="/web/host">Organiseer</a>
            </li>
        </ul>
        <ul class="navbar-nav">
            {% if base.user %}
                {% if base.unreadMessageCount > 0 %}
                    <li class="nav-item">
                        <a class="nav-link" href="/web/user/messages">
                            <i class="fa fa-envelope"></i>
                        </a>
                    </li>
                {% endif %}
                <li class="nav-item dropdown">
                    <a class="nav-link dropdown-toggle" href="#" data-toggle="dropdown">
                        {{ base.user.name }}
                    </a>
                    <div class="dropdown-menu dropdown-menu-right">
                        <a class="dropdown-item" href="/web/user/activities">Mijn spellen</a>
                        <a class="dropdown-item" href="/web/user/messages">
                            Berichten
                            {% if base.unreadMessageCount > 0 %}
                                ({{ base.unreadMessageCount }})
                            {% endif %}
                        </a>
                        <a class="dropdown-item" href="/web/user/settings">Instellingen</a>
                        <a class="dropdown-item" href="/authentication/signout">Afmelden</a>
                    </div>
                </li>
            {% else %}
                <li class="nav-item">
                    <!-- The href will be set in code -->
                    <a class="global-signin nav-link" href="#">Aanmelden</a>
                </li>
            {% endif %}
            <li class="nav-item">
                <a class="nav-link" href="/web/faq">
                    <i class="fa fa-question-circle fa-lg"></i>
                </a>
            </li>
        </ul>
    </nav>
    <!-- Main content -->
    <div class="container mt-3">
        {% block content %}{% endblock %}
    </div>
    <!-- Footer -->
    <footer class="container py-3 text-center">
        © 2018 - Rond De Tafel<br>
        Like ons op <a href="https://www.facebook.com/ronddetafel.be" target="_blank">Facebook</a> <i class="fa fa-facebook-official"></i><br>
        Broncode beschikbaar op <a href="https://github.com/svanimpe/around-the-table.git" target="_blank">GitHub</a> <i class="fa fa-github"></i>
    </footer>
    <!-- Scripts -->
    <script src="/public/js/popper.min.js"></script>
    <script src="/public/js/bootstrap.min.js"></script>
    <script>
        // Set the href for the sign in link.
        var redirect = window.location.pathname;
        $(".global-signin").attr("href", "/authentication/welcome?redirect=" + encodeURIComponent(redirect));
    </script>
    {% block additional-body %}{% endblock %}
</body>
</html>
kylef commented

In the list of render timings, I am going to take a guess the hierachy looks roughly like the following (since extends would include all rendering times of nodes inside it, similiar for block node, for node and ifnode etc):

  • ExtendsNode in 2781ms
    • BlockNode in 1269ms
      • IfNode in 1263ms
        • ForNode in 1263ms
          • VariableNode in 267ms
          • VariableNode in 141ms

Since the ForNode and IfNode take exactly the same time, I am not sure if one is inside the other and which way round they would be. It would seem the most expensive rendering path could be some variable lookups which are performed multiple times in a loop.

I'd take a guess here there is some specific cases of variable lookups that take far more time than others and identifying which they are should help getting to the underlying problem. The variable lookup may also be done inside ForNode and IfNode to resolve them. It could be that mirroring is the bottleneck etc, since there are different types of variable lookups and certain casting (

current = Mirror(reflecting: value).getValue(for: bit)
).

Would you be able to add some additional logging to your code that produced the rendering times. I think something like the following:

if let node = node as VariableNode {
  print(node.token.contents)
}

So it would be printing out more context on the variable lookups that are slower than others. Then you could identify what kind of lookup is taking so long, if it is due to mirroring, or casting an array etc.

@svanimpe Which version of Swift are you testing with? I know Linux performance of Foundation and similiar isn't great on Linux in comparison to macOS, for example.

kylef commented

@svanimpe Since you've only benchmarked the rendering times. Are the lexer and token parser times pretty low? (Lexer.tokenize() and TokenParser.parse() respectively)

@kylef Thanks for taking a look at this.

I am using Swift 4.1.2 on both macOS and Linux.

Your comments made me think of something else: As part of my app refactor, I've adopted Kitura's codable rendering. Previously, I used to build a [String: Any] dictionary by hand. Now Kitura does this by encoding my context to JSON and converting that to [String: Any] using JSONSerialization. The folks at IBM have already checked that encoding phase and there is no performance issue there. But perhaps the resulting dictionaries are handled differently by Stencil?

I also checked the parser during one of my earlier tests but didn't notice any issues there, which is why I focused on the rendering.

I'll try to add some more logging and will report back what I find.

Well, I have some more information:

I tried upgrading to Swift 4.1.3 on Linux. When I did, I my requests were still slow, but I wasn't seeing the slow VariableNodes. My other logging, in ExtendsNode.render (as I first thought this was related to inheritance) showed that the line

let template = try context.environment.loadTemplate(name: templateName)

can take up to two seconds. If this happens twice (parent and child template), that's already 4 seconds. I think this is the main cause of the delay now, as the total request render time is usually twice the time it takes to load a template, plus a few 100ms.

I then tried reverting back to 4.1.2. to try to reproduce my previous results, but failed. This may be due to a IBM Cloud caching issue (which is particularly annoying). I'll try again tomorrow.

kylef commented

Ok, in that case it is likely due to the lexer. Could you try applying the patch from #207. I believe it will apply cleanly onto the last Stencil release.

Aha, success.

I've applied the 3 commits from #207 to my fork (branched at the 0.11 tag). Running that on Swift 4.1.3 on Linux gives me acceptable (< 1s) results:

 loadTemplate: 0.0185469388961792
 Rendered TextNode in 0ms
 Rendered TextNode in 0ms
 Rendered TextNode in 0ms
 sort context
 Rendered TextNode in 0ms
 block context
 super dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 Rendered IfNode in 0ms
 Rendered TextNode in 0ms
 Rendered BlockNode in 0ms
 Rendered TextNode in 0ms
 base context
 facebook dictionary
 app dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 base context
 opengraph dictionary
 url dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 Rendered TextNode in 0ms
 base context
 opengraph dictionary
 image dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 Rendered BlockNode in 0ms
 Rendered TextNode in 0ms
 Rendered BlockNode in 0ms
 Rendered TextNode in 0ms
 base context
 unreadMessageCount dictionary
 Rendered IfNode in 0ms
 Rendered TextNode in 0ms
 base context
 user dictionary
 Rendered TextNode in 0ms
 Rendered IfNode in 0ms
 Rendered TextNode in 0ms
 base context
 user dictionary
 Rendered TextNode in 0ms
 Rendered IfNode in 0ms
 Rendered TextNode in 0ms
 Rendered TextNode in 0ms
 sort context
 Rendered TextNode in 0ms
 Rendered IfNode in 0ms
 Rendered TextNode in 0ms
 sort context
 Rendered TextNode in 0ms
 Rendered IfNode in 0ms
 Rendered TextNode in 0ms
 sort context
 Rendered TextNode in 0ms
 Rendered IfNode in 0ms
 Rendered TextNode in 0ms
 sort context
 Rendered TextNode in 0ms
 Rendered IfNode in 0ms
 Rendered TextNode in 0ms
 base context
 user dictionary
 Rendered IfNode in 0ms
 Rendered TextNode in 0ms
 sort context
 activities context
 Rendered TextNode in 0ms
 activities context
 Rendered TextNode in 0ms
 activity context
 availableSeats dictionary
 activity context
 availableSeats dictionary
 Rendered TextNode in 0ms
 Rendered IfNode in 1ms
 Rendered TextNode in 0ms
 activity context
 id dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 thumbnail dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 picture dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 picture dictionary
 Rendered VariableNode in 1ms
 Rendered TextNode in 0ms
 activity context
 name dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 shortDate dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 longDate dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 time dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 time dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 host dictionary
 name dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 location dictionary
 city dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 base context
 user dictionary
 location nil
 Rendered IfNode in 0ms
 Rendered TextNode in 0ms
 Rendered TextNode in 0ms
 activity context
 availableSeats dictionary
 activity context
 availableSeats dictionary
 Rendered TextNode in 0ms
 Rendered IfNode in 0ms
 Rendered TextNode in 0ms
 activity context
 id dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 thumbnail dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 picture dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 picture dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 name dictionary
 Rendered VariableNode in 5ms
 Rendered TextNode in 0ms
 activity context
 shortDate dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 longDate dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 time dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 time dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 host dictionary
 name dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 location dictionary
 city dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 base context
 user dictionary
 location nil
 Rendered IfNode in 0ms
 Rendered TextNode in 0ms
 Rendered TextNode in 0ms
 activity context
 availableSeats dictionary
 activity context
 availableSeats dictionary
 Rendered TextNode in 0ms
 Rendered IfNode in 0ms
 Rendered TextNode in 0ms
 activity context
 id dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 thumbnail dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 picture dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 picture dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 name dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 shortDate dictionary
 Rendered VariableNode in 53ms
 Rendered TextNode in 0ms
 activity context
 longDate dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 time dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 time dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 host dictionary
 name dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 location dictionary
 city dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 base context
 user dictionary
 location nil
 Rendered IfNode in 0ms
 Rendered TextNode in 0ms
 Rendered TextNode in 0ms
 activity context
 availableSeats dictionary
 activity context
 availableSeats dictionary
 Rendered TextNode in 0ms
 Rendered IfNode in 0ms
 Rendered TextNode in 0ms
 activity context
 id dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 thumbnail dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 picture dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 picture dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 name dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 shortDate dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 longDate dictionary
 Rendered VariableNode in 1ms
 Rendered TextNode in 0ms
 activity context
 time dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 time dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 host dictionary
 name dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 location dictionary
 city dictionary
 Rendered VariableNode in 2ms
 Rendered TextNode in 0ms
 base context
 user dictionary
 location nil
 Rendered IfNode in 0ms
 Rendered TextNode in 0ms
 Rendered TextNode in 0ms
 activity context
 availableSeats dictionary
 activity context
 availableSeats dictionary
 Rendered TextNode in 0ms
 Rendered IfNode in 0ms
 Rendered TextNode in 0ms
 activity context
 id dictionary
 Rendered VariableNode in 27ms
 Rendered TextNode in 0ms
 activity context
 thumbnail dictionary
 Rendered VariableNode in 18ms
 Rendered TextNode in 0ms
 activity context
 picture dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 picture dictionary
 Rendered VariableNode in 4ms
 Rendered TextNode in 0ms
 activity context
 name dictionary
 Rendered VariableNode in 6ms
 Rendered TextNode in 0ms
 activity context
 shortDate dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 longDate dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 time dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 time dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 host dictionary
 name dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 location dictionary
 city dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 base context
 user dictionary
 location nil
 Rendered IfNode in 0ms
 Rendered TextNode in 0ms
 Rendered TextNode in 0ms
 activity context
 availableSeats dictionary
 Rendered TextNode in 0ms
 Rendered IfNode in 0ms
 Rendered TextNode in 0ms
 activity context
 id dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 thumbnail dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 picture dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 picture dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 name dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 shortDate dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 longDate dictionary
 Rendered VariableNode in 47ms
 Rendered TextNode in 0ms
 activity context
 time dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 time dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 host dictionary
 name dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 activity context
 location dictionary
 city dictionary
 Rendered VariableNode in 0ms
 Rendered TextNode in 0ms
 base context
 user dictionary
 location nil
 Rendered IfNode in 0ms
 Rendered TextNode in 0ms
 Rendered ForNode in 263ms
 Rendered TextNode in 0ms
 Rendered IfNode in 263ms
 Rendered TextNode in 0ms
 Rendered BlockNode in 264ms
 Rendered TextNode in 0ms
 Rendered BlockNode in 0ms
 Rendered TextNode in 0ms
 Rendered ExtendsNode in 292ms
 Total render: 0.839139938354492

There are still some slow variable lookups there, but nothing problematic. I've also logged the name of each variable and how it was resolved, if that helps.

I'll try to reproduce the issue with very slow lookups again tomorrow.

@kylef Is there any chance of a release with #207 merged in the near future, or should I deploy my app using my fork of Stencil?

@svanimpe It seems the OP of #207 don't use Stencil anymore and won't have time to take care of rebasing it. Feel free to rebase it yourself to resolve pending conflicts so it's ready to merge and that we can merge it ASAP for next release 👍

See #225. I've done the rebase but I need someone who understands the code to have a look at it and fix bugs.

I've posted some more timing insights in #226

@svanimpe @yonaskolb I suppose this should be now fixed with #230 or there is still something we should look into?

@ilyapuchka the performance regressions from the error handling look like they have been fixed, though there still some performance issues in Swift 4.1 and perhaps some larger issues in Linux that need to be investigated, and that look they may be fixed in #207 / #225 / #226

We might improve performance further but I think this one is directly related to a regression that was fixed so I'll close this, we can discuss improvements in the PRs that we have open.