Skip to content

Commit

Permalink
perf: add string flattening to reduce retained memory (node only)
Browse files Browse the repository at this point in the history
add a memory footprint benchmark
  • Loading branch information
pmalouin authored and harttle committed Oct 10, 2019
1 parent 7bae5bc commit 3ad512c
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 6 deletions.
2 changes: 2 additions & 0 deletions benchmark/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import { output } from './output'
import { tag } from './tag'
import { demo } from './demo'
import { layout } from './layout'
import { memory } from './memory'

async function main () {
await output()
await tag()
await demo()
await layout()
await memory()
}

main()
32 changes: 32 additions & 0 deletions benchmark/memory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { join } from 'path'
import { readFileSync } from 'fs'
import { getHeapStatistics } from 'v8'
import { Liquid } from '../src/liquid'

const engineOptions = {
root: __dirname,
extname: '.liquid'
}

const engine = new Liquid(engineOptions)

const templateString = readFileSync(join(__dirname, 'templates/lorem-html.liquid'), 'utf8')
const templateSizeKb = templateString.length / 1024

const NB_INSTANCES_TO_RETAIN = 250

export function memory () {
console.log('--- memory ---')
const initialUsedHeapSize = getHeapStatistics().used_heap_size
const templates = []

for (let i = 0; i < NB_INSTANCES_TO_RETAIN; i++) {
templates.push(engine.parse(templateString))
}

const finalUsedHeapSize = getHeapStatistics().used_heap_size
const heapDifference = finalUsedHeapSize - initialUsedHeapSize
const avgRetainedPerTemplate = (heapDifference / NB_INSTANCES_TO_RETAIN) / 1024

console.log(`retained memory for a ${templateSizeKb}KB template: ${avgRetainedPerTemplate}KB (${NB_INSTANCES_TO_RETAIN} samples)`)
}
38 changes: 38 additions & 0 deletions benchmark/templates/lorem-html.liquid
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"><head>

<meta http-equiv="content-type" content="text/html; charset=utf-8">
<!-- You may use a different charset if needed -->

<title>Latin Lipsum - Courtesy generator.lorem-ipsum.info</title>
</head>

<body>
<p>Lorem ipsum dolor sit amet, falli detraxit facilisis sit ex. Nec vidit nihil ut, vis utamur definitionem in. Ancillae phaedrum ut eos. Ne eam errem cotidieque.
</p>
<p>
Sit amet vitae referrentur ad, oportere necessitatibus vituperatoribus vim ne. Audiam quaeque nusquam ei eos, mel ex adhuc prompta complectitur. Decore voluptua cotidieque vel ei, veri novum persecuti te sea. Nisl novum sea cu. Dicta delicatissimi ut est. Et has eripuit impedit mediocrem.
</p>
<p>
Eu vim evertitur argumentum, pro legendos iudicabit voluptatibus ne. Ne alii nullam deserunt duo, vis no animal verterem. Ne quo veniam euripidis. Pri discere mentitum vituperatoribus ne. Meliore corrumpit vim at, has ea admodum convenire.
</p>
<p>
Ne mea alii detraxit electram, his ne epicurei gloriatur consetetur. Ex pro aeque omnes appareat, possit integre accommodare eu quo. Qui in porro molestiae, no sit equidem petentium patrioque, omnes voluptaria vim te. Odio quot rebum ad sed, ei sale reque inani sea.
</p>
<p>
At mea vidit petentium, mei lorem pertinacia signiferumque ne. Per eu dicam postea probatus, sea pericula accusamus omittantur ad. Id mei munere ullamcorper. Oportere ocurreret cu vel. Pri ne dicant soluta graecis, duis ponderum corrumpit sed eu, pro ei rebum vivendum.
</p>
<p>
Eum ea exerci sententiae constituam, ut lorem putant volutpat pro. Vix ex iudicabit salutatus principes, pri nisl erat ut, ne cum persius verterem phaedrum. Vix mundi vitae eu. Vel summo vocent albucius in. Qui omnis partem possim ex, wisi doctus efficiantur cu has. Aperiri denique ea pri, cum dissentias signiferumque eu. Per posse dissentiet necessitatibus id, omnis eleifend te mel.
</p>
<p>
Phaedrum erroribus adolescens ut nec, duo mutat viris id. In tale purto dolores sit, ei consul inermis est, his ea movet expetenda. Cibo scribentur id eam, per an quem iracundia, has erat zril solet eu. Falli fierent ne cum, eam placerat facilisis suscipiantur te. Id quo dicat tractatos definiebas, est copiosae splendide id.
</p>
<p>
Constituto neglegentur qui ne, eum ne putent phaedrum, no quod nominati consulatu per. Mei cu accusam principes interesset, vim fugit abhorreant eu, ludus feugait an sed. Clita expetendis definitionem has in, meis pericula no mea, soluta nemore cotidieque eos cu. Summo populo signiferumque pri eu, eum no labores democritum posidonium, pro ignota adipisci in. Tollit molestie id mei.
</p>
<p>
Ipsum impetus et eam, ei his epicuri instructior. Impedit ullamcorper vim an. Impedit ullamcorper vim an. Et eum senserit sententiae reprimique, quem detracto sententiae at sed, no inermis ocurreret rationibus sea pericula no.

</body></html>
10 changes: 10 additions & 0 deletions rollup.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,11 @@ const umd = {
delimiters: ['', ''],
'./fs/node': './fs/browser'
}),
replace({
include: './src/parser/tokenizer.ts',
delimiters: ['', ''],
'./flatten/node': './flatten/browser'
}),
typescript({
tsconfigOverride: {
include: [ 'src' ],
Expand Down Expand Up @@ -99,6 +104,11 @@ const min = {
delimiters: ['', ''],
'./fs/node': './fs/browser'
}),
replace({
include: './src/parser/tokenizer.ts',
delimiters: ['', ''],
'./flatten/node': './flatten/browser'
}),
typescript({
tsconfigOverride: {
include: [ 'src' ],
Expand Down
3 changes: 3 additions & 0 deletions src/parser/flatten/browser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function flatten (str: string) {
return str
}
10 changes: 10 additions & 0 deletions src/parser/flatten/node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* This function forces a string to be re-instantiated in memory using a flat representation instead of a graph
* of concatenated strings. This is an optimization to reduce the memory footprint of token string fragments after
* they are parsed.
* This optimization targets the V8 javascript engine and only works on Node.js.
* @param {string} str
*/
export function flatten (str: string) {
return Buffer.from(str).toString()
}
13 changes: 7 additions & 6 deletions src/parser/tokenizer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { Token } from './token'
import { OutputToken } from './output-token'
import { TokenizationError } from '../util/error'
import { NormalizedFullOptions, applyDefault } from '../liquid-options'
import { flatten } from './flatten/node'

enum ParseState { HTML, OUTPUT, TAG }

Expand Down Expand Up @@ -36,15 +37,15 @@ export class Tokenizer {
}
if (state === ParseState.HTML) {
if (input.substr(p, outputDelimiterLeft.length) === outputDelimiterLeft) {
if (buffer) tokens.push(new HTMLToken(buffer, input, line, col, file))
if (buffer) tokens.push(new HTMLToken(flatten(buffer), input, line, col, file))
buffer = outputDelimiterLeft
line = curLine
col = p - lineBegin + 1
p += outputDelimiterLeft.length
state = ParseState.OUTPUT
continue
} else if (input.substr(p, tagDelimiterLeft.length) === tagDelimiterLeft) {
if (buffer) tokens.push(new HTMLToken(buffer, input, line, col, file))
if (buffer) tokens.push(new HTMLToken(flatten(buffer), input, line, col, file))
buffer = tagDelimiterLeft
line = curLine
col = p - lineBegin + 1
Expand All @@ -57,7 +58,7 @@ export class Tokenizer {
input.substr(p, outputDelimiterRight.length) === outputDelimiterRight
) {
buffer += outputDelimiterRight
tokens.push(new OutputToken(buffer, buffer.slice(outputDelimiterLeft.length, -outputDelimiterRight.length), input, line, col, this.options, file))
tokens.push(new OutputToken(flatten(buffer), buffer.slice(outputDelimiterLeft.length, -outputDelimiterRight.length), input, line, col, this.options, file))
p += outputDelimiterRight.length
buffer = ''
line = curLine
Expand All @@ -66,7 +67,7 @@ export class Tokenizer {
continue
} else if (input.substr(p, tagDelimiterRight.length) === tagDelimiterRight) {
buffer += tagDelimiterRight
tokens.push(new TagToken(buffer, buffer.slice(tagDelimiterLeft.length, -tagDelimiterRight.length), input, line, col, this.options, file))
tokens.push(new TagToken(flatten(buffer), buffer.slice(tagDelimiterLeft.length, -tagDelimiterRight.length), input, line, col, this.options, file))
p += tagDelimiterRight.length
buffer = ''
line = curLine
Expand All @@ -81,10 +82,10 @@ export class Tokenizer {
const str = buffer.length > 16 ? buffer.slice(0, 13) + '...' : buffer
throw new TokenizationError(
`${t} "${str}" not closed`,
new Token(buffer, input, line, col, file)
new Token(flatten(buffer), input, line, col, file)
)
}
if (buffer) tokens.push(new HTMLToken(buffer, input, line, col, file))
if (buffer) tokens.push(new HTMLToken(flatten(buffer), input, line, col, file))

whiteSpaceCtrl(tokens, this.options)
return tokens
Expand Down

0 comments on commit 3ad512c

Please sign in to comment.