From b3ce38e25c5f3a6ee81a1e63c4d74f74530548e7 Mon Sep 17 00:00:00 2001 From: lorenzofox3 Date: Wed, 3 Apr 2024 13:58:31 +0200 Subject: [PATCH] add ejs baseline --- benchmark/ejs/blog.ejs | 6 ++++++ benchmark/ejs/index.js | 29 +++++++++++++++++++++++++++++ benchmark/ejs/layout.ejs | 25 +++++++++++++++++++++++++ benchmark/ejs/navigation-link.ejs | 11 +++++++++++ benchmark/ejs/navigation.ejs | 8 ++++++++ benchmark/ejs/post.ejs | 8 ++++++++ benchmark/index.js | 2 ++ benchmark/package.json | 9 +++++---- benchmark/readme.md | 29 +++++++++++++++++++++++++++++ readme.md | 9 +++++++-- 10 files changed, 130 insertions(+), 6 deletions(-) create mode 100644 benchmark/ejs/blog.ejs create mode 100644 benchmark/ejs/index.js create mode 100644 benchmark/ejs/layout.ejs create mode 100644 benchmark/ejs/navigation-link.ejs create mode 100644 benchmark/ejs/navigation.ejs create mode 100644 benchmark/ejs/post.ejs create mode 100644 benchmark/readme.md diff --git a/benchmark/ejs/blog.ejs b/benchmark/ejs/blog.ejs new file mode 100644 index 0000000..92f1145 --- /dev/null +++ b/benchmark/ejs/blog.ejs @@ -0,0 +1,6 @@ +
+

Latest articles

+ <% posts.forEach(function (post) { -%> + <%- include('post', {post}) %> + <% }); -%> +
diff --git a/benchmark/ejs/index.js b/benchmark/ejs/index.js new file mode 100644 index 0000000..2500ac9 --- /dev/null +++ b/benchmark/ejs/index.js @@ -0,0 +1,29 @@ +import { dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; +import view from '@fastify/view'; +import ejs from 'ejs'; +import { getPosts } from '../blog-posts.js'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +export const ejsPlugin = async (instance) => { + instance.register(view, { + engine: { + ejs, + }, + root: __dirname, + }); + + instance.route({ + method: 'GET', + url: '/', + async handler(req, reply) { + reply.type('text/html'); + const posts = await getPosts(); + return reply.view('./layout', { + title: 'Blog', + posts, + currentPage: '/blog', + }); + }, + }); +}; diff --git a/benchmark/ejs/layout.ejs b/benchmark/ejs/layout.ejs new file mode 100644 index 0000000..46aca44 --- /dev/null +++ b/benchmark/ejs/layout.ejs @@ -0,0 +1,25 @@ + + + + + + + + <%= title %> + + +
+ +

<%= title %>

+ <%- include('./navigation', { currentPage}) %> +
+
+

+ Hi! I am Laurent and this is my dev blog. This is where I collect what + I learn, what I experiment and what I find interesting. +

+ <%- include('blog', {posts}) %> +
+ + + diff --git a/benchmark/ejs/navigation-link.ejs b/benchmark/ejs/navigation-link.ejs new file mode 100644 index 0000000..7de2781 --- /dev/null +++ b/benchmark/ejs/navigation-link.ejs @@ -0,0 +1,11 @@ +
  • + <% if(isCurrentPage) { %> + + <%= link.name %> + + <% } else { %> + + <%= link.name %> + + <% } %> +
  • diff --git a/benchmark/ejs/navigation.ejs b/benchmark/ejs/navigation.ejs new file mode 100644 index 0000000..78d30cd --- /dev/null +++ b/benchmark/ejs/navigation.ejs @@ -0,0 +1,8 @@ + diff --git a/benchmark/ejs/post.ejs b/benchmark/ejs/post.ejs new file mode 100644 index 0000000..797fc55 --- /dev/null +++ b/benchmark/ejs/post.ejs @@ -0,0 +1,8 @@ +
    +

    <%= post.title%>

    +

    + Published by <%= post.author %> on +

    +

    <%= post.description %>

    + Read full article +
    diff --git a/benchmark/index.js b/benchmark/index.js index c1b3bea..302b68c 100644 --- a/benchmark/index.js +++ b/benchmark/index.js @@ -4,6 +4,7 @@ import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { tplStreamPlugin } from './tpl-stream/index.js'; import { pugPlugin } from './pug/index.js'; +import { ejsPlugin } from './ejs/index.js'; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -18,5 +19,6 @@ app.register(fastifyStatic, { app.register(tplStreamPlugin, { prefix: '/tpl-stream' }); app.register(pugPlugin, { prefix: '/pug' }); +app.register(ejsPlugin, { prefix: '/ejs' }); app.listen({ port: 3000 }); diff --git a/benchmark/package.json b/benchmark/package.json index c881e54..2850379 100644 --- a/benchmark/package.json +++ b/benchmark/package.json @@ -3,15 +3,16 @@ "scripts": { "dev": "node --watch index.js", "start": "NODE_ENV=production node index.js", - "bench:tpl-stream":"autocannon http://localhost:3000/tpl-stream", - "bench:pug":"autocannon http://localhost:3000/pug" + "bench:tpl-stream": "autocannon http://localhost:3000/tpl-stream", + "bench:pug": "autocannon http://localhost:3000/pug", + "bench:ejs": "autocannon http://localhost:3000/ejs" }, "devDependencies": { "@fastify/static": "^7.0.2", "autocannon": "^7.15.0", "ejs": "^3.1.9", - "eta": "^3.4.0", "fastify": "^4.26.2", - "pug": "^3.0.2" + "pug": "^3.0.2", + "@fastify/view": "^9.0.0" } } diff --git a/benchmark/readme.md b/benchmark/readme.md new file mode 100644 index 0000000..327a7e1 --- /dev/null +++ b/benchmark/readme.md @@ -0,0 +1,29 @@ +# benchmark + +This is not really a _benchmark_, but rather a toy application to assess the performances of the library in a _real world_ use case, versus +different baselines: the same [fastify](https://fastify.dev/) application built with the popular template engines [pug](https://pugjs.org/) (the one I would use by default) and [ejs](https://ejs.co/)(a very popular one in the community with 13M weekly downloads). + +## The application + +The application is a blog page where posts are loaded from a fake database. To simulate some latency we run the following code: +```js +const LATENCY = env.DB_LATENCY || 10; + +export async function getPosts() { + const latency = Math.round(Math.random() * LATENCY); + await setTimeout(latency); + return [...postList]; +} +``` +## Load simulation + +Then, we run [autocannon](https://github.com/mcollina/autocannon) to see how many requests the server can process. +On my machine, I get the following results (median requests by second): + +| tpl-stream | pug | ejs | +|------------|-----|-----| +| 1550 | 1632| 670 | + +## conclusion + +``tpl-stream`` is more than capable and I will use it as my default template engine from now. diff --git a/readme.md b/readme.md index f533844..ea835c0 100644 --- a/readme.md +++ b/readme.md @@ -5,7 +5,7 @@ ``tpl-stream`` is a template library that supports streaming. You can use it in your server, but not only, to generate html: it works everywhere as long as the runtime implements [web streams](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream). -It is very small compared to the alternatives and does not require build process, while providing very good performances. +It is very small compared to the alternatives and does not require any build process, while providing [very good performances](./benchmark). ## Installation @@ -94,7 +94,7 @@ html`` // ``` -## render +### render The ``render`` function takes a template as input and returns a ``ReadableStream``. The chunks are split every time there is a pending Promise: @@ -106,3 +106,8 @@ The ``render`` function takes a template as input and returns a ``ReadableStream You can also render as a string, by awaiting the Promise returned by ``renderAsString`` function +## Perceived speed. + +Note that streaming can also improve the _perceived_ speed as the browser renders the HTML (and eventually fetch some resources) while the server has not fully responded to the request. +This is the behavior you can observe below with an exaggerated latency of 1s. You can combine libraries such ``tpl-stream`` with techniques such [Out Of Order streaming](https://lamplightdev.com/) to improve the user experience even further. +