# {{#let}} vs @cached Performance

**URL:** <https://discuss.emberjs.com/t/let-vs-cached-performance/18458>\
**Category:** Performance\
**Created:** [December 29, 2020, 2:39pm UTC](https://discuss.emberjs.com/t/let-vs-cached-performance/18458 "2020-12-29T14:39:56Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![paulyoder](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.emberjs.com/paulyoder/32/9797_2.png) [@paulyoder](https://discuss.emberjs.com/u/paulyoder)\
**Post date:** [December 29, 2020, 2:39pm UTC](https://discuss.emberjs.com/t/let-vs-cached-performance/18458/1 "2020-12-29T14:39:56Z")

</div>

Can anyone speak to the performance between using the {{#let}} helper to cache a component’s property versus using the @cached decorator on the property.

My hypothesis is the {{#let}} helper will have better performance because it doesn’t have to check if any tracked properties changed.

Below are code examples of what I’m talking about.

## **{{#let}} Helper**

_component_

```javascript
  get contact() {
    return this.allContacts.byId(this.args.contactId);
  }

```

_template_

```hbs
{{#let this.contact as |contact|}}
  <LinkTo @route='contacts.contact' @model={{contact.id}}>{{contact.name}}</LinkTo>
  <div>
    {{contact.address1}}
  </div>
  <div>
    {{contact.city}}, {{contact.state}} {{contact.zip}}
  </div>
{{/let}}

```

&nbsp; &nbsp;

## **@cached decorator**

_component_

```javascript
  @cached
  get contact() {
    return this.allContacts.byId(this.args.contactId);
  }

```

_template_

```hbs
  <LinkTo @route='contacts.contact' @model={{this.contact.id}}>{{this.contact.name}}</LinkTo>
  <div>
    {{this.contact.address1}}
  </div>
  <div>
    {{this.contact.city}}, {{this.contact.state}} {{this.contact.zip}}
  </div>

```

---

<div class="post-metadata">

**Author:** ![chriskrycho](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.emberjs.com/chriskrycho/32/17763_2.png) [@chriskrycho](https://discuss.emberjs.com/u/chriskrycho)\
**Post date:** [December 29, 2020, 5:40pm UTC](https://discuss.emberjs.com/t/let-vs-cached-performance/18458/2 "2020-12-29T17:40:26Z")

</div>

Yeah, I think `#let` should be _very_ slightly faster, and in a way that scales linearly with the number of uses in the template, because it’ll only need to check for tracked property value changes once—at the site of usage—rather than repeatedly asking for and receiving the cached value, which would have to check every time. (Helpers _do_ in fact check if the tracked properties they consume _change_—they have to in order to recompute appropriately!—but things like helper invocations, component invocations, etc. all represent nodes in the reactivity tree which can “no-op” everything below them for recomputation when doing a new render pass. More on this below for folks who are reading this later.)

This would only show up in a _meaningful_ way if you were re-using the cached value a _lot_, because in either case, the computation is extremely cheap. It’s _not_ comparing/caching the actual values (like `@computed` did); it’s only comparing the integers representing the last “time” it was updated vs. the current “time” it was updated—where “time” here is in terms of the “clock” that the tracking system uses, which is just an integer which gets incremented any time you set a tracked property. Both `@cached` and passing a value to `#let` in the template will perform exactly the same check: for _any_ tracked property the getter _or_ the value used in the template used, has it changed.

But the reason you might see a meaningful difference emerge at large numbers of uses of the property in a template is that, as you note, re-invoking the cached property _does_ still have to do the check every time, and even though the check is cheap, it’s not free. Even integer comparisons take time; more importantly, you slightly increase memory pressure with every use of `@cached`, and _that_ adds up over time as well, including for things like GC pauses.

So a `let` is probably _slightly_ faster in all cases, but it’s also a small enough difference that it likely doesn’t matter for _most_ user code. My general rule of thumb would be:

- default to _not_ caching at all unless you’re doing something you _know_ to be very expensive and/or where the caching semantics matter (like making an API call through a getter)
- if you need to cache the value, start by reaching for whatever makes your code clearer and easier to follow over worrying about the cost of comparing integer values
- if you’re doing something where you’re re-rendering the same value hundreds or thousands of times, then start caring about these kinds of low-level distinctions

These cases _definitely_ do exist; I’m tracing out that heuristic primarily because I regularly see folks _assume_ they need caching when in fact caching might cost them more than just running the code “naïvely” would.

* * *

I assume you already grok this, but for others reading along: if helpers _didn’t_ check whether the tracked properties they use have changed, then code like this would never update:

```nohighlight
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';

export default class MyComponent extends Component {
  @tracked count = 0;

  get countPlusOne() {
    return this.count + 1;
  }

  increment = () => (this.count++);
}

```

```nohighlight
// app/helpers/double.js
import { helper } from '@ember/component/helper';
export default helper(([a]) => a * 2);

```

```nohighlight
<p>Value: {{double this.countPlusOne}}</p>
<button {{on "click" this.increment"}}>MOAR</button>

```

_Any_ time you consume a property in a template, whether it’s rendering a value standalone, passing it to a helper, passing it to a modifier, or passing it to a component, that will connect it to the tracking system and trigger reevaluation when the tracked property changes. (Deep dive blog post [here](https://v5.chriskrycho.com/journal/autotracking-elegant-dx-via-cutting-edge-cs/) and even deeper dive walkthrough on YouTube [here](https://www.youtube.com/watch?v=BjKERSRpPeI&feature=youtu.be).) When you introduce `@cached`, you’re _basically_ just letting a getter have the same level of wiring-into-the-reactivity-system that template invocations do. However, as the original question rightly suggests, it has to check for that divergence _every_ time the decorated getter is invoked, rather than just once at the top of the `{{#let ...}}`, which would add up if used enough.

---

<div class="post-metadata">

**Author:** ![paulyoder](https://sea1.discourse-cdn.com/flex019/user_avatar/discuss.emberjs.com/paulyoder/32/9797_2.png) [@paulyoder](https://discuss.emberjs.com/u/paulyoder)\
**Post date:** [December 29, 2020, 7:24pm UTC](https://discuss.emberjs.com/t/let-vs-cached-performance/18458/3 "2020-12-29T19:24:45Z")

</div>

Thanks for the in-depth explanation @chriskrycho. From your response, I’m going to go with the `@cached` option since it’s the typical way to cache a value and `{{#let}}` would only be slightly faster
