From ebed937b7663535aa80d2d829dfce1ccffc355b1 Mon Sep 17 00:00:00 2001 From: Erikas Date: Sun, 14 Jul 2024 17:51:34 +0300 Subject: [PATCH] Add FPS density chart, add better way of serving static files --- server.go | 44 +++++++++++++- static/js/benchmark.js | 127 +++++++++++++++++++++++++++++++++++++++ templates/benchmark.tmpl | 2 + 3 files changed, 171 insertions(+), 2 deletions(-) diff --git a/server.go b/server.go index 1afa10b..c74ebd3 100644 --- a/server.go +++ b/server.go @@ -1,7 +1,11 @@ package flightlesssomething import ( + "bytes" + "crypto/sha1" + "encoding/hex" "html/template" + "io/fs" "net/http" "os" "path/filepath" @@ -80,10 +84,46 @@ func Start(c *Config) { r.SetHTMLTemplate(tmpl) // Serve static files - fileServer := http.FileServer(http.FS(staticFS)) r.GET("/static/*filepath", func(c *gin.Context) { + filepath := c.Param("filepath") + file, err := staticFS.Open("static" + filepath) + if err != nil { + c.Status(http.StatusNotFound) + return + } + defer file.Close() + + // Get file info + fileInfo, err := file.Stat() + if err != nil { + c.Status(http.StatusInternalServerError) + return + } + + // Read file content into a byte slice + content, err := fs.ReadFile(staticFS, "static"+filepath) + if err != nil { + c.Status(http.StatusInternalServerError) + return + } + + // Generate ETag based on file content + hash := sha1.New() + hash.Write(content) + etag := hex.EncodeToString(hash.Sum(nil)) + + // Set ETag and Cache-Control headers + c.Header("ETag", etag) c.Header("Cache-Control", "public, max-age=3600") - fileServer.ServeHTTP(c.Writer, c.Request) + + // Check if the ETag matches + if match := c.GetHeader("If-None-Match"); match == etag { + c.Status(http.StatusNotModified) + return + } + + // Serve the file with ETag and Last-Modified headers + http.ServeContent(c.Writer, c.Request, fileInfo.Name(), fileInfo.ModTime(), bytes.NewReader(content)) }) r.GET("/", func(c *gin.Context) { c.Redirect(http.StatusTemporaryRedirect, "/benchmarks") }) diff --git a/static/js/benchmark.js b/static/js/benchmark.js index c9a004c..294789f 100644 --- a/static/js/benchmark.js +++ b/static/js/benchmark.js @@ -461,6 +461,133 @@ Highcharts.chart('avgChart', { }] }); +// Function to filter out the top and bottom 3% of FPS values +function filterOutliers(data) { + data.sort((a, b) => a - b); + var start = Math.floor(data.length * 0.01); // Ignore bottom 1% + var end = Math.ceil(data.length * 0.97); // Ignore top 1% + return data.slice(start, end); +} + +// Function to count occurrences of each FPS value +function countFPS(data) { + var counts = {}; + data.forEach(function(fps) { + var roundedFPS = Math.round(fps); + counts[roundedFPS] = (counts[roundedFPS] || 0) + 1; + }); + + var fpsArray = Object.keys(counts).map(function(key) { + return [parseInt(key), counts[key]]; + }).sort(function(a, b) { + return a[0] - b[0]; + }); + + // Combine closest FPS values until we have 100 or fewer points + while (fpsArray.length > 100) { + var minDiff = Infinity; + var minIndex = -1; + + // Find the pair with the smallest difference + for (var i = 0; i < fpsArray.length - 1; i++) { + var diff = fpsArray[i + 1][0] - fpsArray[i][0]; + if (diff < minDiff) { + minDiff = diff; + minIndex = i; + } + } + + // Combine the closest pair + fpsArray[minIndex][1] += fpsArray[minIndex + 1][1]; + fpsArray[minIndex][0] = (fpsArray[minIndex][0] + fpsArray[minIndex + 1][0]) / 2; + fpsArray.splice(minIndex + 1, 1); + } + + return fpsArray; +} + +// Calculate counts for each dataset after filtering outliers +var densityData = fpsDataArrays.map(function(dataArray) { + var filteredData = filterOutliers(dataArray.data); + return { + name: dataArray.label, + data: countFPS(filteredData) + }; +}); + +// Create the chart +Highcharts.chart('densityChart', { + chart: { + type: 'areaspline', + backgroundColor: null + }, + title: { + text: 'FPS Density', + style: { + color: '#FFFFFF', + fontSize: '16px' + } + }, + xAxis: { + title: { + text: 'FPS', + style: { + color: '#FFFFFF' + } + }, + labels: { + style: { + color: '#FFFFFF' + } + } + }, + yAxis: { + title: { + text: 'Count', + style: { + color: '#FFFFFF' + } + }, + labels: { + style: { + color: '#FFFFFF' + } + }, + gridLineColor: 'rgba(255, 255, 255, 0.1)' + }, + tooltip: { + shared: true, + backgroundColor: '#1E1E1E', + borderColor: '#FFFFFF', + style: { + color: '#FFFFFF' + }, + formatter: function() { + var points = this.points; + var tooltipText = '' + points[0].series.name + ': ' + points[0].y + ' points at ~' + Math.round(points[0].x) + ' FPS'; + return tooltipText; + } + }, + plotOptions: { + areaspline: { + fillOpacity: 0.5, + marker: { + enabled: false + } + } + }, + legend: { + enabled: true, + itemStyle: { + color: '#FFFFFF' + } + }, + credits: { + enabled: false + }, + series: densityData +}); + function calculateSpikes(data, threshold) { if (data.length < 6) { throw new Error("Data length must be greater than or equal to 6."); diff --git a/templates/benchmark.tmpl b/templates/benchmark.tmpl index 8afb270..a843ac1 100644 --- a/templates/benchmark.tmpl +++ b/templates/benchmark.tmpl @@ -90,6 +90,8 @@
+
+