Skip to content

Add Marko - #109

Open
NullVoxPopuli-ai-agent wants to merge 2 commits into
NullVoxPopuli:mainfrom
NullVoxPopuli-ai-agent:add-marko
Open

Add Marko#109
NullVoxPopuli-ai-agent wants to merge 2 commits into
NullVoxPopuli:mainfrom
NullVoxPopuli-ai-agent:add-marko

Conversation

@NullVoxPopuli-ai-agent

@NullVoxPopuli-ai-agent NullVoxPopuli-ai-agent commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Adds Marko (v6, the tags API — compiled fine-grained reactivity, so it qualifies) as a benched framework.

What's here

  • frameworks/marko/ — one standalone Marko 6 app per bench: Vite + @marko/vite (linked: false, linked mode is for SSR entrypoints), client-side .mount(), common linked like every other framework. frameworks/marko/README.md documents the scaffold and the traps below.
  • results app registration (frameworks.ts + results/public/marko.svg, from the site's fav.svg; chart color is the logo's magenta stripe — the cyan/green/orange stripes sit too close to react/vue/ember)
  • tests/helpers.ts FRAMEWORKS entry — all 5 apps + the dbmon dev-server test pass
  • runner: notes.json can declare benches a framework cannot run ({ "skip": { "<bench app>": "why" } }) — logged and recorded into the result file instead of aborting the whole run after --timeout. marko declares incrementing-render-effect skipped (see below).

Marko traps hit along the way

  • A bare ${...} line at a template's root parses as a dynamic tag name (concise mode) — createElementNS('[undefined]') at runtime. Root-level text needs the -- prefix.
  • An attribute method shorthand that closes over a <for> loop variable, passed to a custom tag, compiles to a hoisted function referencing $scope that doesn't exist at runtime. ten-k passes a static register function + the index as separate attributes instead.
  • No deep reactivity: dbmon reassigns its Map / chat array; ten-k gives each item its own <let> in a tags/ child tag (the signal-per-item shape preact/lit/solid use).

The interesting one: a frame-rate floor

Marko's scheduler renders the first write of a frame in a microtask and parks every later write until the next animation frame (schedule() in marko's src/dom/schedule.ts). Two consequences:

  1. incrementing-render-effect pacing: each roundtrip costs a frame — ~17s for the test's 2k updates vs ~400ms for preact/react/svelte. At the bench's default 100k updates a sample can never reach :done inside any practical --timeout, and the runner used to abort the entire run on that — hence the notes.json skip mechanism above. (Forcing run() from marko/dom into the app would measure manual flushing instead of Marko.)
  2. Conformance: one update per task still coalesces into one render per frame, so the task-paced anti-cheat specs can't observe marko's per-write states. Per review, conformance adjustments live in their own PR: Conformance: trace frame-throttled frameworks via yield=frame #110 paces those specs with yield=frame for frame-throttled frameworks, keeping every trace assertion. Merge order: Conformance: trace frame-throttled frameworks via yield=frame #110 first, then this PR's conformance CI step goes green.

Verification

  • pnpm build green for all 5 apps
  • SKIP_BUILD=1 pnpm test -g marko: 6/6 (incl. dbmon dev-server; needed the same server.fs.allow workaround as every other framework)
  • with Conformance: trace frame-throttled frameworks via yield=frame #110: all 4 conformance specs pass for marko (frame-paced), preact/react/svelte unchanged
  • pnpm bench --framework=marko --bench="Incrementing Render Effect" completes, warns with the skip reason, and records it under notes.marko.skip

🤖 Generated with Claude Code

frameworks/marko/: one standalone Marko 6 (tags API) app per bench,
built with @marko/vite (linked: false) and mounted client-side via the
template .mount() API. Registered in results/app/frameworks.ts (logo
from markojs.com, the magenta chevron stripe as the chart color) and in
the playwright suite's FRAMEWORKS.

Marko-specific shapes, documented in frameworks/marko/README.md:

- reactivity is per-assignment (no deep proxies), so dbmon reassigns
  its Map and chat list, and ten-k gives every item its own <let> in a
  child tag whose setter the app registers by index
- root-level text needs the `--` prefix (a bare ${...} line parses as a
  dynamic tag name in concise mode)
- an attribute method shorthand closing over a <for> loop variable
  compiles to a hoisted function that loses the loop scope; a static
  function taking the index avoids it

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Aug 9, 2026

Copy link
Copy Markdown

@NullVoxPopuli-ai-agent is attempting to deploy a commit to the NullVoxPopuli's projects Team on Vercel.

A member of the Team first needs to authorize it.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds Marko 6 implementations for all benchmark workloads and integrates Marko into testing and result visualization.

Changes:

  • Adds five client-side Marko benchmark applications.
  • Registers Marko in tests and results metadata.
  • Documents Marko’s scheduler behavior and adjusts conformance tests accordingly.

Reviewed changes

Copilot reviewed 37 out of 48 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/specs/conformance.spec.ts Adds frame-throttled framework handling.
tests/README.md Documents conformance exclusions.
tests/helpers.ts Registers Marko for app tests.
results/public/marko.svg Adds the Marko logo.
results/app/frameworks.ts Adds Marko result metadata.
frameworks/marko/README.md Documents setup and scheduler constraints.
frameworks/marko/ten-k-items-one-time/.gitignore Adds generated-file exclusions.
frameworks/marko/ten-k-items-one-time/index.html Adds the client entry page.
frameworks/marko/ten-k-items-one-time/package.json Defines app dependencies and scripts.
frameworks/marko/ten-k-items-one-time/pnpm-lock.yaml Locks app dependencies.
frameworks/marko/ten-k-items-one-time/public/favicon.svg Adds the app favicon.
frameworks/marko/ten-k-items-one-time/src/App.marko Implements the list benchmark.
frameworks/marko/ten-k-items-one-time/src/main.js Mounts the Marko app.
frameworks/marko/ten-k-items-one-time/src/tags/bench-item.marko Provides per-item reactive state.
frameworks/marko/ten-k-items-one-time/vite.config.js Configures client-only Marko compilation.
frameworks/marko/one-item-many-updates/.gitignore Adds generated-file exclusions.
frameworks/marko/one-item-many-updates/index.html Adds the client entry page.
frameworks/marko/one-item-many-updates/package.json Defines app dependencies and scripts.
frameworks/marko/one-item-many-updates/pnpm-lock.yaml Locks app dependencies.
frameworks/marko/one-item-many-updates/public/favicon.svg Adds the app favicon.
frameworks/marko/one-item-many-updates/src/App.marko Implements repeated updates.
frameworks/marko/one-item-many-updates/src/main.js Mounts the Marko app.
frameworks/marko/one-item-many-updates/vite.config.js Configures client-only Marko compilation.
frameworks/marko/incrementing-render-effect/.gitignore Adds generated-file exclusions.
frameworks/marko/incrementing-render-effect/index.html Adds the client entry page.
frameworks/marko/incrementing-render-effect/package.json Defines app dependencies and scripts.
frameworks/marko/incrementing-render-effect/pnpm-lock.yaml Locks app dependencies.
frameworks/marko/incrementing-render-effect/public/favicon.svg Adds the app favicon.
frameworks/marko/incrementing-render-effect/src/App.marko Implements render-driven advancement.
frameworks/marko/incrementing-render-effect/src/main.js Mounts the Marko app.
frameworks/marko/incrementing-render-effect/vite.config.js Configures client-only Marko compilation.
frameworks/marko/fan-out/.gitignore Adds generated-file exclusions.
frameworks/marko/fan-out/index.html Adds the client entry page.
frameworks/marko/fan-out/package.json Defines app dependencies and scripts.
frameworks/marko/fan-out/pnpm-lock.yaml Locks app dependencies.
frameworks/marko/fan-out/public/favicon.svg Adds the app favicon.
frameworks/marko/fan-out/src/App.marko Implements fan-out rendering.
frameworks/marko/fan-out/src/main.js Mounts the Marko app.
frameworks/marko/fan-out/vite.config.js Configures client-only Marko compilation.
frameworks/marko/dbmon-with-chat/.gitignore Adds generated-file exclusions.
frameworks/marko/dbmon-with-chat/index.html Adds the client entry page.
frameworks/marko/dbmon-with-chat/package.json Defines app dependencies and scripts.
frameworks/marko/dbmon-with-chat/pnpm-lock.yaml Locks app dependencies.
frameworks/marko/dbmon-with-chat/public/favicon.svg Adds the app favicon.
frameworks/marko/dbmon-with-chat/src/App.marko Implements DBMon and chat rendering.
frameworks/marko/dbmon-with-chat/src/layout.css Adds benchmark layout styles.
frameworks/marko/dbmon-with-chat/src/main.js Mounts the Marko app.
frameworks/marko/dbmon-with-chat/vite.config.js Configures Marko and linked assets.
Files not reviewed (5)
  • frameworks/marko/dbmon-with-chat/pnpm-lock.yaml: Generated file
  • frameworks/marko/fan-out/pnpm-lock.yaml: Generated file
  • frameworks/marko/incrementing-render-effect/pnpm-lock.yaml: Generated file
  • frameworks/marko/one-item-many-updates/pnpm-lock.yaml: Generated file
  • frameworks/marko/ten-k-items-one-time/pnpm-lock.yaml: Generated file

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread frameworks/marko/README.md Outdated
Comment on lines +31 to +34
`incrementing-render-effect` bench advances exactly one update per
frame, so at its default 100k updates it will not finish inside the
runner's default `--timeout`. That pacing is the framework's real
behavior; run that bench with fewer `?updates=` or a larger timeout

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 14c5e92: notes.json gains { "skip": { "<bench app>": "why" } } — the runner logs the reason and continues instead of aborting, and the reason is recorded into the result file with the rest of the notes. marko declares incrementing-render-effect skipped. Verified end to end: pnpm bench --framework=marko --bench="Incrementing Render Effect" completes with the warning and writes notes.marko.skip.

Comment thread tests/specs/conformance.spec.ts Outdated
Comment on lines +290 to +293
test.skip(
FRAME_THROTTLED.has(framework) && spec.app !== SELF_ADVANCING,
'frame-rate-floor scheduler: per-task writes coalesce into per-frame renders by design',
);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oof, ya changing the anti-cheating coverage is not allowed. we can make adjustments in a separate PR to the conformance stuff tho

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved out of this PR → #110. This branch no longer touches the conformance suite or common — the anti-cheat specs here are exactly what's on main.

#110 keeps the coverage rather than skipping: yield=frame delivers one write per frame (the pacing you suggested), and all trace assertions apply to marko unchanged. Merge order: #110 first, then this PR's conformance CI step goes green (until then it fails for marko, since a frame-floored scheduler coalesces task-paced writes).

@NullVoxPopuli

Copy link
Copy Markdown
Owner

Potential issue with marko performance:

@NullVoxPopuli

Copy link
Copy Markdown
Owner

Marko scores pretty good outside of incrementing render effect

image

Review feedback: the runner hard-codes each bench's workload
(incrementing-render-effect at 100k updates) and aborts the whole run
when a sample never reaches :done inside --timeout -- so a framework
that architecturally cannot finish a bench (marko advances one update
per animation frame there: hours of frames) made every default
`--framework=all` run abort.

notes.json gains an optional skip map, `{ "skip": { "<bench app>":
"why" } }`: the runner logs the reason and moves on instead of dying,
and since notes.json is already recorded into the result file, the
reason travels with the run. marko declares
incrementing-render-effect skipped.

Verified end to end: `pnpm bench --framework=marko
--bench="Incrementing Render Effect"` completes, warns with the
reason, and writes it under notes.marko.skip in the result file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@@ -0,0 +1,5 @@
{
"skip": {
"incrementing-render-effect": "marko renders at most one update per animation frame (frame-rate-floor scheduler), and this bench advances one update per render: 100k updates is hours of frames, so the sample can never reach :done inside any practical --timeout"

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NullVoxPopuli-ai-agent does marko really not have a way to have multiple effects triggered within one frame?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants