johnandersen777.bsky.social/knotless
git that lives in your Atmosphere account
Forked from chadtmiller.com/knotless
0
0
//! What social cards cost: each stage of drawing one and the PNG it makes,
//! a cached card against a new one, and how many a second the cores draw
//! together.
//!
//! cargo run --release -p knotless-serve --example cards -- [--out <dir>]
//!
//! With `--out`, the cards are written there as PNGs, to look at.
use knotless_serve::card::{self, Card};
use knotless_serve::languages;
use std::time::{Duration, Instant};
/// Runs `f` until a second has passed, at least ten times; the median and
/// the slowest one in a hundred.
fn time<T>(mut f: impl FnMut() -> T) -> (Duration, Duration) {
let mut runs = Vec::new();
let start = Instant::now();
while runs.len() < 10 || start.elapsed() < Duration::from_secs(1) {
let t = Instant::now();
std::hint::black_box(f());
runs.push(t.elapsed());
}
runs.sort();
(runs[runs.len() / 2], runs[runs.len() * 99 / 100])
}
fn ms(d: Duration) -> String {
format!("{:.3}", d.as_secs_f64() * 1000.0)
}
fn cards() -> Vec<(&'static str, Card)> {
vec![
("home", Card::Home),
(
"repo",
Card::Repo {
owner: "alice.bsky.social".into(),
avatar: None,
name: "knotless".into(),
description: Some(
"Git repositories stored natively on atproto: no git server, nothing to install on the PDS."
.into(),
),
pulls: 3,
issues: 12,
languages: languages::breakdown(
[("a.rs", 700_000), ("a.ts", 120_000), ("a.svelte", 90_000), ("a.css", 30_000), ("a.sh", 4_000)]
.map(|(path, bytes)| (languages::kind(path).unwrap(), bytes)),
),
},
),
(
"issue",
Card::Item {
owner: "alice.bsky.social".into(),
avatar: None,
name: "knotless".into(),
pull: false,
title: "Cloning a repository with ten thousand refs takes a minute on a slow PDS"
.into(),
author: "bob.example.com".into(),
opened: Some("2026-11-01T09:30:00.000Z".into()),
comments: 4,
reviews: 0,
size: None,
},
),
(
"pull",
Card::Item {
owner: "alice.bsky.social".into(),
avatar: None,
name: "knotless".into(),
pull: true,
title: "Draw social cards in process".into(),
author: "carol.test".into(),
opened: Some("2026-11-01T09:30:00.000Z".into()),
comments: 1,
reviews: 2,
size: Some(card::Size {
commits: 3,
files: 4,
added: 181,
removed: 18,
}),
},
),
(
"long",
Card::Item {
owner: "alice.bsky.social".into(),
avatar: None,
name: "knotless".into(),
pull: true,
title: "Keep a comment being written when the page reloads, and offer it back the next time the same pull is opened".into(),
author: "erin.test".into(),
opened: Some("2026-11-01T09:30:00.000Z".into()),
comments: 0,
reviews: 1,
size: Some(card::Size {
commits: 1,
files: 4,
added: 181,
removed: 18,
}),
},
),
(
"emoji",
Card::Item {
owner: "alice.bsky.social".into(),
avatar: None,
name: "knotless".into(),
pull: false,
title: "Titles with emoji π draw in color: π©π½βπ» π―π΅ β€οΈ π¦ not as boxes".into(),
author: "dana.test".into(),
opened: Some("2026-11-01T09:30:00.000Z".into()),
comments: 2,
reviews: 0,
size: None,
},
),
]
}
/// The card's PNG, as a request for it gets it: kept, or drawn and kept.
async fn png(cache: &card::Cache, version: &str, svg: String) -> std::sync::Arc<[u8]> {
match cache.drawn.get(version) {
Some(png) => png,
None => cache.draw(version, svg, None, true).await,
}
}
fn main() {
let args: Vec<String> = std::env::args().collect();
let out = args
.iter()
.position(|a| a == "--out")
.map(|i| std::path::PathBuf::from(&args[i + 1]));
if let Some(dir) = &out {
std::fs::create_dir_all(dir).unwrap();
}
// The fonts load on first use; that is not what is measured.
card::render(&Card::Home.svg(), None);
println!(
"{:<6} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8}",
"card", "svg ms", "hash ms", "draw ms", "png ms", "all ms", "KB"
);
for (name, card) in cards() {
let svg = card.svg();
let (svg_ms, _) = time(|| card.svg());
let (hash_ms, _) = time(|| card::version(&svg));
let (draw_ms, _) = time(|| card::raster(&svg, None));
let pixmap = card::raster(&svg, None);
let (png_ms, _) = time(|| card::encode(&pixmap));
let (all_ms, _) = time(|| card::render(&svg, None));
let png = card::render(&svg, None);
println!(
"{name:<6} {:>8} {:>8} {:>8} {:>8} {:>8} {:>8.1}",
ms(svg_ms),
ms(hash_ms),
ms(draw_ms),
ms(png_ms),
ms(all_ms),
png.len() as f64 / 1024.0
);
if let Some(dir) = &out {
std::fs::write(dir.join(format!("{name}.png")), png).unwrap();
}
}
// A whole card as a request for it pays, new and cached; and the cores
// drawing new ones together.
let rt = tokio::runtime::Runtime::new().unwrap();
let (_, issue) = &cards()[2];
let svg = issue.svg();
let (new, new99) = time(|| card::render(&svg, None));
let cache = card::Cache::new(1 << 16);
let version = card::version(&svg);
rt.block_on(png(&cache, &version, svg.clone()));
let (hit, hit99) = time(|| {
rt.block_on(async {
let svg = issue.svg();
let version = card::version(&svg);
png(&cache, &version, svg).await
})
});
println!(
"\nan issue card, new: {} ms (p99 {}); cached, with its SVG and hash: {} ms (p99 {})",
ms(new),
ms(new99),
ms(hit),
ms(hit99)
);
let n = 256;
let start = Instant::now();
rt.block_on(futures::future::join_all((0..n).map(|i| {
let card = Card::Item {
owner: "alice.bsky.social".into(),
avatar: None,
name: "knotless".into(),
pull: i % 2 == 0,
title: format!("Issue number {i}, one of many drawn at once"),
author: "bob.example.com".into(),
opened: Some("2026-11-01T09:30:00.000Z".into()),
comments: i,
reviews: 0,
size: None,
};
let svg = card.svg();
let cache = &cache;
async move { png(cache, &card::version(&svg), svg).await }
})));
let took = start.elapsed();
let cores = std::thread::available_parallelism().map_or(1, |n| n.get());
println!(
"{n} new cards at once on {cores} cores: {} ms, {:.0} cards a second",
took.as_millis(),
n as f64 / took.as_secs_f64()
);
}