Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented chart visualization #911

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 105 additions & 0 deletions docs/chart.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<!doctype html>
<html lang="en">

<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="Lambda Cold Starts benchmark">
<meta name="author" content="maxday">
<meta name="google-site-verification" content="hiFRDth4TrOFHqrvMoorH687i5XQ5Ad0y1X_AbaHUOs" />

<meta property="og:url" content="https://maxday.github.io/lambda-perf/">
<meta property="og:type" content="website">
<meta property="og:title" content="Lambda Cold Starts benchmark | maxday">
<meta property="og:description" content="Visualize 10 Cold Starts for each runtime, updated daily">
<meta property="og:image" content="https://maxday.github.io/lambda-perf/og.png">
<meta name=twitter:card content="summary_large_image">
<meta property="twitter:domain" content="maxday.github.io/lambda-perf">
<meta property="twitter:url" content="https://maxday.github.io/lambda-perf/">
<meta name=twitter:title content="Lambda Cold Starts benchmark | maxday">
<meta name=twitter:description content="Visualize 10 Cold Starts for each runtime, updated daily">
<meta name=twitter:image content="https://maxday.github.io/lambda-perf/og.png">

<link rel="icon" href="./img/favicon.ico">
<title>Lambda Cold Starts analysis</title>
<link href="css/bootstrap.min.css" rel="stylesheet">
<link href="css/custom.css" rel="stylesheet">
<script src="chart.js" type="module"></script>
<script src="shared.js" type="module"></script>
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
</head>

<body>
<main class="container">
<h1 class="mt-5">Lambda Cold Starts benchmark <br />by <a href="https://maxday.dev">maxday</a></h1>
<p class="lead">Visualize 10 Cold Starts for each runtime, updated daily.
<br /><a href="https://www.youtube.com/watch?v=Idys2BAmqIU">[How to deploy a Rust Lambda function?]</a>
<br /><a href="https://github.com/maxday/lambda-perf">[How does it work?]</a>
<br /><a href="/docs/index.html">🏎️ Speed comparison</a>

</p>
<p class="align-right">
<span>
<input type="button" id="zip" class="packageTypeBtn badge bg-primary bg-success" value="zip" /> |
<input type="button" id="image" class="packageTypeBtn badge bg-primary" value="image" />
</span>
<br />
<span>
<input type="button" id="x86_64" class="architectureBtn badge bg-primary bg-success" value="x86_64" /> |
<input type="button" id="arm64" class="architectureBtn badge bg-primary" value="arm64" />
</span>
<br />
<span>
<input type="button" id="128" class="memorySizeBtn badge bg-primary bg-success" value="128MB" /> |
<input type="button" id="256" class="memorySizeBtn badge bg-primary" value="256MB" /> |
<input type="button" id="512" class="memorySizeBtn badge bg-primary" value="512MB" /> |
<input type="button" id="1024" class="memorySizeBtn badge bg-primary" value="1024MB" />
</span>
<br />
<span class="badge bg-primary">us-east1</span>
<br />
<span class="badge bg-success">Last update: <span id="lastUpdate">loading</span></span>
</p>
<p>
<span>
<p>Selected Metric:</p>
<span>
<input type="button" id="coldStart" class="metricButton badge bg-primary" value="Cold Start" />
<input type="button" id="memoryUsed" class="metricButton badge bg-primary bg-success"
value="Memory Used" />
<input type="button" id="avgDuration" class="metricButton badge bg-primary " value="Avg Duration" />
</span>
</span>
</p>


<hr />
<div id="sampleRuntimeElement" class="runtime">
<span class="badge bg-success"><span class="runtimeName">runtime name</span></span>
<div class="coldstarts" class="fixedHeight"></div>
❄ <span class="itemData averageColdStartDuration"></span></span>
💾 <span class="itemData averageMemoryUsed"></span></span>
⚡ <span class="itemData averageDuration"></span></span>
<hr />

</div>
<div id="runtimes"></div>
</main>
<div class="chart-container">
<div id="chart" class="chart"></div>
</div>


<footer class="footer">
<div class="container">
<span class="text-muted">Made with ♥ in Montreal 🇨🇦 by <a href="https://maxday.dev">maxday</a>
</span>
</div>
<div class="disclaimer">This site is not associated, affiliated, endorsed, or sponsored by any companies nor
have
they been reviewed tested or certified by any company.
</div>
</footer>
</body>

</html>
114 changes: 114 additions & 0 deletions docs/chart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import {
dataManager,
getCurrentMemorySize,
getCurrentArchitecture,
getCurrentPackageType,
setupFilterEvent,
load
} from "./shared.js";

let chart = null;

const animate = async dataManager => {
try {
const memorySize = getCurrentMemorySize();
const architecture = getCurrentArchitecture();
const packageType = getCurrentPackageType();
const selectedMetric = getCurrentMetric();
if (!dataManager.fetchData) {
await load(dataManager);
}
const data = dataManager.fetchData;
document.getElementById("lastUpdate").innerHTML = data.metadata.generatedAt;
const promiseArray = [];
let i = 0;
data.runtimeData.sort((a, b) => a.acd - b.acd);
const filteredData = data.runtimeData.filter(
r => r.m == memorySize && r.a === architecture && r.p === packageType
);

buildChart(filteredData, selectedMetric);

await Promise.all(promiseArray);
} catch (e) {
console.error(e);
}
};

const getCurrentMetric = () => {
const buttons = document.getElementsByClassName("metricButton");
for (const btn of buttons) {
if (btn.classList.contains("bg-success")) {
return btn.id;
}
}
return "Cold Start";
};

const loaded = async dataManager => {
setupFilterEvent(".memorySizeBtn", dataManager, updateFilter);
setupFilterEvent(".architectureBtn", dataManager, updateFilter);
setupFilterEvent(".packageTypeBtn", dataManager, updateFilter);
setupFilterEvent(".metricButton", dataManager, updateFilter);

await animate(dataManager);
};

const buildChart = (filteredData, selectedMetric) => {
const categories = filteredData.map(data => data.d);

let seriesData = [];
if (selectedMetric == "avgDuration") {
seriesData = filteredData.map(data => data.ad);
} else if (selectedMetric == "coldStart") {
seriesData = filteredData.map(data => data.acd);
} else {
seriesData = filteredData.map(data => data.mu);
}

var options = {
series: [
{
name: selectedMetric,
data: seriesData
}
],
chart: {
height: "600px",
type: "bar"
},
plotOptions: {
bar: {
borderRadius: 4,
horizontal: true
}
},
dataLabels: {
enabled: false
},
xaxis: {
categories: categories
}
};

if (!chart) {
chart = new ApexCharts(document.querySelector("#chart"), options);
chart.render();
} else {
chart.updateOptions(options);
}
};

document.addEventListener(
"DOMContentLoaded",
dataManager => loaded(dataManager),
false
);

export const updateFilter = async (e, className, dataManager) => {
const newValue = e.target.id;
const btns = document.querySelectorAll(className);
btns.forEach(el => el.classList.remove("bg-success"));
document.getElementById(newValue).classList.add("bg-success");
await animate(dataManager);
};
22 changes: 21 additions & 1 deletion docs/css/custom.css
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,24 @@ hr {
}
.badge:empty {
display: inline-block;
}
}

.chart-container {
display: flex;
align-items: center;
justify-content: center;
}

.chart {
width: 100%;
max-height: 400px;
}



@media (min-width: 768px) {
.chart {
min-height: 1000px;
max-width: 90vw;
}
}
5 changes: 4 additions & 1 deletion docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@
<title>Lambda Cold Starts analysis</title>
<link href="css/bootstrap.min.css" rel="stylesheet">
<link href="css/custom.css" rel="stylesheet">
<script src="script.js"></script>
<script src="script.js" type="module"></script>
<script src="shared.js" type="module"></script>

<script defer data-domain="maxday.github.io/lambda-perf" src="https://plausible.io/js/script.js"></script>
</head>

Expand All @@ -34,6 +36,7 @@ <h1 class="mt-5">Lambda Cold Starts benchmark <br />by <a href="https://maxday.d
<p class="lead">Visualize 10 Cold Starts for each runtime, updated daily.
<br /><a href="https://www.youtube.com/watch?v=Idys2BAmqIU">[How to deploy a Rust Lambda function?]</a>
<br /><a href="https://github.com/maxday/lambda-perf">[How does it work?]</a>
<br /><a href="/docs/chart.html">📈 Chart visualization</a>
</p>
<p class="align-left">
<button type="button" id="replayAnimationBtn" class="btn btn-secondary">
Expand Down
67 changes: 11 additions & 56 deletions docs/script.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,5 @@
const dataManager = {
fetchData: null,
};
import { dataManager, getCurrentMemorySize, getCurrentArchitecture, getCurrentPackageType, setupFilterEvent, load, formatData } from './shared.js';

const load = async (dataManager) => {
const request = await fetch(
"https://raw.githubusercontent.com/maxday/lambda-perf/main/data/last.json?0.057112075338791035"
);
const json = await request.json();
dataManager.fetchData = json;
};

const animate = async (dataManager) => {
try {
Expand All @@ -26,7 +17,7 @@ const animate = async (dataManager) => {
const filteredData = data.runtimeData.filter(
(r) => r.m == memorySize && r.a === architecture && r.p === packageType
);
for (runtime of filteredData) {
for (const runtime of filteredData) {
promiseArray.push(drawLang(i, runtime));
++i;
}
Expand All @@ -44,51 +35,11 @@ const updateFilter = async (e, className, dataManager) => {
await replayAnimation(dataManager);
};

const getCurrentMemorySize = () => {
const buttons = document.getElementsByClassName("memorySizeBtn");
for (btn of buttons) {
if (btn.classList.contains("bg-success")) {
return btn.id;
}
}
return 128;
};

const getCurrentArchitecture = () => {
const buttons = document.getElementsByClassName("architectureBtn");
for (btn of buttons) {
if (btn.classList.contains("bg-success")) {
return btn.id;
}
}
return "x86_64";
};

const getCurrentPackageType = () => {
const buttons = document.getElementsByClassName("packageTypeBtn");
for (btn of buttons) {
if (btn.classList.contains("bg-success")) {
return btn.id;
}
}
return "zip";
};

const replayAnimation = async (dataManager) => {
document.getElementById("runtimes").innerHTML = "";
await animate(dataManager);
};

const setupFilterEvent = (className, dataManager) => {
const btnMemorySize = document.querySelectorAll(className);
btnMemorySize.forEach((el) =>
el.addEventListener("click", (e) => updateFilter(e, className, dataManager))
);
};
const loaded = async (dataManager) => {
setupFilterEvent(".memorySizeBtn", dataManager);
setupFilterEvent(".architectureBtn", dataManager);
setupFilterEvent(".packageTypeBtn", dataManager);
setupFilterEvent(".memorySizeBtn", dataManager, updateFilter);
setupFilterEvent(".architectureBtn", dataManager, updateFilter);
setupFilterEvent(".packageTypeBtn", dataManager, updateFilter);
document
.getElementById("replayAnimationBtn")
.addEventListener("click", (dataManager) => replayAnimation(dataManager));
Expand Down Expand Up @@ -131,8 +82,6 @@ const addSquare = (parent) => {
parent.appendChild(span);
};

const formatData = (data) =>
typeof data === "number" ? data.toFixed(2) : data;

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

Expand All @@ -141,3 +90,9 @@ document.addEventListener(
(dataManager) => loaded(dataManager),
false
);


export const replayAnimation = async dataManager => {
document.getElementById("runtimes").innerHTML = "";
await animate(dataManager);
};
Loading