From 93020dd25db87ccc9e046812a634b04f2f33d230 Mon Sep 17 00:00:00 2001
From: hrfmartins
Date: Sun, 21 Jan 2024 22:00:19 +0100
Subject: [PATCH 1/2] feat: implemented charts to help visualizing data
---
docs/chart.js | 194 ++++++++++++++++++++++++++++++++++++++++++++
docs/charts.html | 104 ++++++++++++++++++++++++
docs/css/custom.css | 22 ++++-
docs/index.html | 1 +
4 files changed, 320 insertions(+), 1 deletion(-)
create mode 100644 docs/chart.js
create mode 100644 docs/charts.html
diff --git a/docs/chart.js b/docs/chart.js
new file mode 100644
index 0000000000..e1c3aed5cc
--- /dev/null
+++ b/docs/chart.js
@@ -0,0 +1,194 @@
+const dataManager = {
+ fetchData: null
+};
+
+let chart = null;
+
+const load = async dataManager => {
+ const request = await fetch(
+ "https://raw.githubusercontent.com/maxday/lambda-perf/main/data/last.json?0.769589841097639"
+ );
+ const json = await request.json();
+ dataManager.fetchData = json;
+};
+
+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 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 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 getCurrentMetric = () => {
+ const buttons = document.getElementsByClassName("metricButton");
+ for (btn of buttons) {
+ if (btn.classList.contains("bg-success")) {
+ return btn.id;
+ }
+ }
+ return "Cold Start";
+};
+
+const replayAnimation = async dataManager => {
+ 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(".metricButton", dataManager);
+
+ await animate(dataManager);
+};
+
+const drawLang = async (idx, data) => {
+ const newElement = document
+ .getElementById("sampleRuntimeElement")
+ .cloneNode(true);
+ newElement.id = `runtime_${idx}`;
+ document.getElementById("runtimes").appendChild(newElement);
+ const coldStartElement = newElement.getElementsByClassName("coldstarts")[0];
+
+ const averageColdStartDuration = newElement.getElementsByClassName(
+ "averageColdStartDuration"
+ )[0];
+ averageColdStartDuration.innerHTML = `${formatData(data.acd)}ms`;
+
+ const averageMemoryUsed = newElement.getElementsByClassName(
+ "averageMemoryUsed"
+ )[0];
+ averageMemoryUsed.innerHTML = `${data.mu}MB`;
+
+ const averageDuration = newElement.getElementsByClassName(
+ "averageDuration"
+ )[0];
+ averageDuration.innerHTML = `${formatData(data.ad)}ms`;
+
+ const runtimeName = newElement.getElementsByClassName("runtimeName")[0];
+ runtimeName.innerHTML = `${data.d}`;
+
+ for (let i = 0; i < data.i.length; ++i) {
+ await sleep(data.i[i]);
+ addSquare(coldStartElement);
+ }
+};
+
+const formatData = data => (typeof data === "number" ? data.toFixed(2) : data);
+
+const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
+
+document.addEventListener(
+ "DOMContentLoaded",
+ dataManager => loaded(dataManager),
+ false
+);
+
+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);
+ }
+};
diff --git a/docs/charts.html b/docs/charts.html
new file mode 100644
index 0000000000..5fd914cb2e
--- /dev/null
+++ b/docs/charts.html
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Lambda Cold Starts analysis
+
+
+
+
+
+
+
+
+ Lambda Cold Starts benchmark
by maxday
+ Visualize 10 Cold Starts for each runtime, updated daily.
+
[How to deploy a Rust Lambda function?]
+
[How does it work?]
+
🏎️ Speed comparison
+
+
+
+
+ |
+
+
+
+
+ |
+
+
+
+
+ |
+ |
+ |
+
+
+
+ us-east1
+
+ Last update: loading
+
+
+
+ Selected Metric:
+
+
+
+
+
+
+
+
+
+
+
+
runtime name
+
+ ❄
+ 💾
+ ⚡
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/docs/css/custom.css b/docs/css/custom.css
index e2745121cd..9c4371edef 100644
--- a/docs/css/custom.css
+++ b/docs/css/custom.css
@@ -68,4 +68,24 @@ hr {
}
.badge:empty {
display: inline-block;
-}
\ No newline at end of file
+}
+
+.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;
+ }
+}
\ No newline at end of file
diff --git a/docs/index.html b/docs/index.html
index 943acc811a..2a92ec75a2 100644
--- a/docs/index.html
+++ b/docs/index.html
@@ -34,6 +34,7 @@