mirror of
https://github.com/lukaszraczylo/traefik-llmgateway.git
synced 2026-10-12 04:54:45 +00:00
perf(admin): stop wrapping baked assets in a 7500-literal + chain
webui/generate.mjs split each admin-panel asset's base64 into 120-char Go string literals joined with `+` (~7,550 literals, ~7,538 `+` ops across index.html + the JS/CSS bundle). A compiled Go binary folds that at compile time for free, but this plugin loads under Yaegi (github.com/traefik/yaegi), which folds a chain of N literal `+` operations at import time by repeatedly reallocating and copying the whole already-folded prefix: O(N^2). Measured: ~186ms of a ~223-240ms package import was this folding alone, versus ~3-4ms to decode the asset bytes. Replace the 120-char LINE_WIDTH with a 1,000,000-char CHUNK_CHARS cap. Every asset this panel ships today (largest ~516,000 base64 chars) fits in a single literal, so goBase64Expr now emits one decodeAdminAsset call with zero `+` folds per asset. The cap still bounds a future, much larger bundle to a small number of large chunks rather than one unbounded line; single-line Go string literals in this size class were verified against gofmt/go vet/go build before picking the constant. No behavior change: decoded asset bytes, contentType strings, and the asset map keys are unaffected by how the base64 is chunked into source literals — see the following regen commit for the byte-identity proof.
This commit is contained in:
+26
-6
@@ -111,16 +111,36 @@ function goStringLiteral(s) {
|
||||
|
||||
/**
|
||||
* goBase64Expr renders base64Body (a base64 string) as a Go expression
|
||||
* calling decodeAdminAsset, split into LINE_WIDTH-character chunks joined
|
||||
* with `+` — a single 400KB-wide line is valid Go but painful for humans
|
||||
* and diff tools alike; chunking keeps every line reviewable.
|
||||
* calling decodeAdminAsset, split into at most CHUNK_CHARS-character
|
||||
* literals joined with `+`.
|
||||
*
|
||||
* This used to split at a human-readable LINE_WIDTH of 120 chars —
|
||||
* ~7,550 literals / ~7,538 `+` operations for this panel's ~680KB of
|
||||
* base64. That's fine for a compiled Go binary (constant string
|
||||
* concatenation folds at compile time), but this plugin is loaded by
|
||||
* Yaegi (github.com/traefik/yaegi — Traefik's plugin interpreter), which
|
||||
* folds a chain of N literal `+` operations at *import* time, by
|
||||
* repeatedly reallocating and copying the whole already-folded prefix:
|
||||
* O(N^2). Measured: that alone cost ~186ms of a ~223-240ms package
|
||||
* import — the dominant share of Traefik's plugin-boot latency for this
|
||||
* repo, versus ~3-4ms for decoding the asset bytes themselves.
|
||||
*
|
||||
* CHUNK_CHARS is set well above this panel's current largest asset
|
||||
* (~516,000 base64 chars for the main JS bundle), so every asset today
|
||||
* emits exactly one literal — zero `+` folds, zero O(N^2) cost. It still
|
||||
* caps chunk size (rather than emitting one unbounded literal) so a
|
||||
* future, much larger bundle degrades to a handful of large chunks
|
||||
* instead of one arbitrarily long source line; gofmt/go vet/go build
|
||||
* were verified against single-line string literals in this size class
|
||||
* (see the perf-fix commit that introduced this constant), so this cap
|
||||
* sits well inside that verified range.
|
||||
*/
|
||||
const LINE_WIDTH = 120
|
||||
const CHUNK_CHARS = 1_000_000
|
||||
function goBase64Expr(base64Body) {
|
||||
if (base64Body.length === 0) return 'decodeAdminAsset("")'
|
||||
const chunks = []
|
||||
for (let i = 0; i < base64Body.length; i += LINE_WIDTH) {
|
||||
chunks.push(goStringLiteral(base64Body.slice(i, i + LINE_WIDTH)))
|
||||
for (let i = 0; i < base64Body.length; i += CHUNK_CHARS) {
|
||||
chunks.push(goStringLiteral(base64Body.slice(i, i + CHUNK_CHARS)))
|
||||
}
|
||||
return `decodeAdminAsset(\n\t\t${chunks.join(' +\n\t\t')},\n\t)`
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user