Skip to content

Instantly share code, notes, and snippets.

@zkat
Last active August 29, 2015 14:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zkat/441379747a61beece62c to your computer and use it in GitHub Desktop.
Save zkat/441379747a61beece62c to your computer and use it in GitHub Desktop.
Javascript translation of writing inline caching through load-time-value and inlining.

Base code

Consider the following code, and how it can be affected by the following transformations:

function foo () {
  return {x: 5}
}
foo() === foo() // => false

loadTimeValue

This function is statically compiled away by supporting transformers (which need to have esprima/acorn-level knowledge about scope and the like). Otherwise, it should either NOOP or error. The idea is that any expressions wrapped in loadTimeValue are yanked out into a module-level cache, and evaluated only once -- at the top level (so they don't see local function scope!), and a reference into that cache is inserted into the original callsite.

Thus, consider then the effect of using loadTimeValue in the base code:

function foo () {
  return loadTimeValue({x: 5})
}

This would be transformed into code like:

const __LOAD_TIME_VALUES__ = [
  {x: 5}
]
function foo () {
  return __LOAD_TIME_VALUES__[0]
}

Which, would yield a different result in the expression we evaluated:

foo() === foo() // => true

"inline";

Now consider the more familiar use of function inlining, assuming the ability to explicitly do so for existing functions:

function foo () {
  "inline";
  return {x: 5}
}

Our original function would be extracted entirely, but our evaluated expression would transform into:

{x: 5} === {x: 5} // => false

Both together

Inlining is fairly familiar, of course, and stuff like loadTimeValue is done manually all the time. But what about when we combine both?

function foo () {
  "inline";
  return loadTimeValue({x: 5})
}

foo() === foo() // => false

Ideally, we would first transform "inline"; declarations away, leaving us with only this code:

loadTimeValue({x: 5}) === loadTimeValue({x: 5})

And the ltv would be replaced in the following pass, leaving us with:

const __LOAD_TIME_VALUES__ = [
  {x: 5},
  {x: 5}
]
__LOAD_TIME_VALUES__[0] === __LOAD_TIME_VALUES__[1] // false

WHY

The examples here were more meant to show the mechanics involved, but in practice, both of these can turn out to be tremendously useful. Put together, they make it trivial to add general or inline caching to any similar values!

function timesCalled () {
  "inline";
  var counter = loadTimeValue({count: 0})
  return ++counter.count
}

function caller() {
  if (randomTruthy()) {
    console.log('callsite #1 calls: ', timesCalled())
  } else {
    console.log('callsite #2 calls: ', timesCalled())
  }
}

repeat(100, caller) // Prints different counts depending on how `randomTruthy` goes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment