Graph: fabricate overview of timing scales

Content generated by Knowledge-Technology

 - collect information with the help of Claude
 - validate my own measurements with sources from the net
 - find a suitable form for the presentation
 - instructed Claude to build a SVG with a logarithmic time scale
 - instructed Claude to summarise the findings as Asciidoc table
This commit is contained in:
Fischlurch 2026-08-04 19:20:45 +02:00
parent 7650da5d67
commit f46f117eda
5 changed files with 1210 additions and 147 deletions

View file

@ -49,9 +49,7 @@
<node CREATED="1784566743858" ID="ID_374363115" MODIFIED="1784566748447" TEXT="Verwendung von KI">
<node CREATED="1784566749890" ID="ID_208298802" MODIFIED="1784566821115" TEXT="Leitlinien nach Alexandrescu">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
hab daf&#252;r zwar keinen Beleg (jenseits von ein paar Videos), aber ich nehme hier den Namen &#187;Alexandrescu&#171; in Gebrauch, um etwas &#8212; f&#252;r sich selbst plausibles &#8212; auch mit Autorit&#228;t sagen zu k&#246;nnen
@ -107,16 +105,13 @@
<node CREATED="1785790716243" ID="ID_1869514045" MODIFIED="1785790725942" TEXT="mu&#xdf; &#xbb;Synchronisation&#xab; eigens behandeln">
<node CREATED="1785791037159" ID="ID_396828809" MODIFIED="1785791086458" TEXT="hier &#xbb;trennt sich die Spreu vom Weizen&#xab;">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
die allermeisten Entwickler heutzutage gehen niemals &#252;ber diese Schwelle, und verstecken sich deshalb hinter Frameworks
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1785791092863" ID="ID_709044928" MODIFIED="1785791103479" TEXT="es ist viel Halbwissen unterwegs"/>
</node>
@ -160,9 +155,7 @@
<node CREATED="1785529171087" ID="ID_1945346295" MODIFIED="1785529186416" TEXT="want speedy response to new arrangements"/>
<node CREATED="1785529228286" ID="ID_1074909901" MODIFIED="1785529301885" TEXT="integrate very expensive workloads">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
raw video with very high quality parameters; elaborate processing stack
@ -205,9 +198,7 @@
<node CREATED="1785537107740" ID="ID_1255119744" MODIFIED="1785537135326" TEXT="and especially for Video &#x27f6; a new twist...."/>
<node CREATED="1785536747451" ID="ID_1634555006" MODIFIED="1785537079438">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
I/O is no longer slow &#8212; and <i>that's the problem</i>
@ -370,7 +361,101 @@
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1754271487261" ID="ID_1152628369" MODIFIED="1779389322292" TEXT="Inhalte aufbereiten">
<icon BUILTIN="flag-pink"/>
<node CREATED="1754271622745" ID="ID_1711166940" MODIFIED="1754271624175" TEXT="Texte"/>
<node CREATED="1754271624646" ID="ID_52826289" MODIFIED="1754271626857" TEXT="Grafik"/>
<node CREATED="1754271624646" ID="ID_52826289" MODIFIED="1754271626857" TEXT="Grafik">
<node CREATED="1785817833206" ID="ID_327257827" MODIFIED="1785817840819" TEXT="Diagramm &quot;Time Scales&quot;">
<node CREATED="1785817842583" ID="ID_306380330" MODIFIED="1785817861311" TEXT="Idee: m&#xf6;chte die gewaltigen Zeitspannen visualisieren">
<icon BUILTIN="idea"/>
<node CREATED="1785817863252" ID="ID_299103430" MODIFIED="1785817884168" TEXT="m&#xf6;chte klarmachen, warum wir es mit &#xbb;Stockwerken&#xab; zu tun haben"/>
<node CREATED="1785817885505" ID="ID_1002287440" MODIFIED="1785817898591" TEXT="brauche ein Framework, in dem ich die vierschiednen Zahlen einordnen kann"/>
</node>
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1785817904306" ID="ID_1762091251" MODIFIED="1785817932470" STYLE="fork" TEXT="diese Idee mit Claude ausarbeiten....">
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
<icon BUILTIN="wizard"/>
<node CREATED="1785817934211" ID="ID_1193226285" MODIFIED="1785817983642" TEXT="lasse Claude die Timings aufsammeln aus dem anderen Diskussionsthread">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
habe jetzt einen ziemlich langen Thread mit Recherche / Hintergrundwissen f&#252;r mich; in dem Rahmen sind die diversen Zeitangaben bereits alle aufgetaucht
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1785817989939" ID="ID_1738971982" MODIFIED="1785818000822" TEXT="lasse Zahlen nochmal &#xfc;berpr&#xfc;fen und mir einige Quellen suchen"/>
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1785818001802" ID="ID_420827912" MODIFIED="1785818058289" STYLE="fork" TEXT="Vorschlag von Claude: plausibel machen durch Analogie: 1 clock-tick &#x2259; 1 sec">
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
<icon BUILTIN="idea"/>
<node CREATED="1785818059596" ID="ID_1473752885" MODIFIED="1785818077088" TEXT="Geil">
<icon BUILTIN="ksmiletris"/>
</node>
<node CREATED="1785818061762" ID="ID_1056499967" MODIFIED="1785818074164" TEXT="das sind alle Goldhamlster Deutschlands aneinandergereicht"/>
<node BACKGROUND_COLOR="#ccb59b" COLOR="#6e2a38" CREATED="1785818084662" ID="ID_1277933693" MODIFIED="1785818095365" TEXT="werde das als separates Slide bringen">
<font ITALIC="true" NAME="SansSerif" SIZE="14"/>
<icon BUILTIN="yes"/>
</node>
</node>
<node CREATED="1785818213769" ID="ID_1124818051" MODIFIED="1785818244989" TEXT="lasse mir einen Vorschlag machen, mit reduzierter Auswahl und didaktisch-verk&#xfc;rzten Titeln"/>
<node CREATED="1785818246457" ID="ID_249476374" MODIFIED="1785818250452" TEXT="einige wenige Korrekturen"/>
<node CREATED="1785818251079" ID="ID_306091121" MODIFIED="1785818264085" TEXT="diskutiere Tabellenform vs. Diagramm"/>
<node CREATED="1785818264742" ID="ID_1640435583" MODIFIED="1785818276130" TEXT="lasse Claude ausrechnen, ob das Diagramm lesbar ist">
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1785818298026" ID="ID_987123563" MODIFIED="1785818345795" STYLE="fork" TEXT="Vorschlag Claude: aufbrechen in &#xbb;Stockwerke&#xab;">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
Interessante Idee ... Claude greift also mein Framing vom ersten Prompt wieder auf ... erscheint mir sehr plausibel
</p>
</body>
</html></richcontent>
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
<icon BUILTIN="wizard"/>
</node>
<node CREATED="1785818347664" ID="ID_1543794705" MODIFIED="1785818898650" TEXT="damit lie&#xdf;e sich das in 4 &#xbb;Stockwerke&#xab; unterbringen">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
Das hat Claude interessanterweise erst gesehen, nachdem ich an den l&#228;ngsten Zeitspannen etwas getweakt hatte. Mir war aufgefallen 1000sec &#8776; 15min &#10233; damit kommen wir mit einer Sekunden-Skala als 4.Zeile gut aus
</p>
</body>
</html></richcontent>
</node>
<node CREATED="1785818395777" ID="ID_1984967393" MODIFIED="1785818581196" TEXT="Idee: teile auf in &#xbb;Maschine&#xab; &#x2014; &#xbb;Mensch&#xab;">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
...das ergab sich aus der Konversation: im Grunde reichen die ganzen Maschinen-relevanten Zeiten hoch bis 10ms / etwa an dem Punkt beginnen Timings, die vom Menschen wahrnehmbar sind.
</p>
</body>
</html></richcontent>
</node>
</node>
<node BACKGROUND_COLOR="#ccb59b" COLOR="#6e2a38" CREATED="1785818286987" ID="ID_346780786" MODIFIED="1785818292938" TEXT="Entscheidung f&#xfc;r ein Diagramm">
<font ITALIC="true" NAME="SansSerif" SIZE="14"/>
<icon BUILTIN="yes"/>
</node>
<node COLOR="#338800" CREATED="1785818430680" ID="ID_662868064" MODIFIED="1785818900154" TEXT="lasse Claude ein geeignetes SVG generieren">
<icon BUILTIN="button_ok"/>
<node CREATED="1785818441734" ID="ID_953591500" MODIFIED="1785818447505" TEXT="nach meinen Vorgaben..."/>
<node CREATED="1785818447838" ID="ID_161232521" MODIFIED="1785818484948" TEXT="generiert ein zun&#xe4;chst sichtbares &quot;scaffold&quot;"/>
<node CREATED="1785818487850" ID="ID_206426899" MODIFIED="1785818500771" TEXT="dann jeweils einen transparenten Layer f&#xfc;r jedes &#xbb;Stockwerk&#xab;"/>
<node CREATED="1785818501622" ID="ID_613662939" MODIFIED="1785818517560" TEXT="ein Dim-Layer den man zuschalten kann, wenn man in den &quot;menschlichen&quot;-Bereich geht"/>
<node BACKGROUND_COLOR="#ece0bb" CREATED="1785818702064" ID="ID_540432980" MODIFIED="1785818738052" TEXT="Claude schreibt tats&#xe4;chlich ein Python-Programm, das er mir auch mitgibt">
<icon BUILTIN="ksmiletris"/>
<node CREATED="1785863757380" ID="ID_1079327219" MODIFIED="1785863764251" TEXT="Compile-Check">
<node CREATED="1785863765224" ID="ID_24534958" MODIFIED="1785863774760" TEXT="python3 -m py_compile gen-time-scales-diagram.py">
<icon BUILTIN="idea"/>
</node>
</node>
</node>
</node>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1785818905823" ID="ID_1365657985" MODIFIED="1785861408296" TEXT="Diagramm grafisch ausfeilen und an Seitenlayout anpassen">
<icon BUILTIN="flag-yellow"/>
</node>
</node>
</node>
<node CREATED="1754874947759" ID="ID_920995429" MODIFIED="1754874949169" TEXT="Code">
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1754875923722" FOLDED="true" ID="ID_1802306052" MODIFIED="1779389626734" TEXT="Hau-zu">
<icon BUILTIN="idea"/>
@ -578,9 +663,7 @@
<node CREATED="1723038081191" ID="ID_1463117390" MODIFIED="1779388529297" TEXT="au&#xdf;erdem: Image &#xbb;erfahrener Experte&#xab;"/>
<node CREATED="1753133297344" ID="ID_1778242194" MODIFIED="1784566420704" TEXT="Recherche f&#xfc;r Lumiera: wir brauchen ein eigenes IO-Framework">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Es war von Anfang an klar, da&#223; wir <i>speziell dieses Thema selber unter Kontrolle bringen </i>m&#252;ssen.
@ -621,16 +704,13 @@
<node CREATED="1723508279705" ID="ID_1768919179" MODIFIED="1723508290155" TEXT="da&#xdf; ich hier einen durchaus eigenwilligen Weg gehe"/>
<node CREATED="1784566608876" ID="ID_1680364545" MODIFIED="1785790359493" TEXT="da&#xdf; die eigentlichen Probleme f&#xfc;r Lumiera ungel&#xf6;st sind">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Damit meine ich, ob der Ansatz mit symbolisch repr&#228;sentierten externen F&#228;higkeiten &#252;berhaupt aufgeht, und ob es tats&#228;chlich gelingt, ein Werkzeug-Paradigma auf der Basis von Placements zu schaffen. Und, last but not least, ob ich Leute finden, die mich unterst&#252;tzen und sich diese Vision aneignen
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1785790294093" ID="ID_1612420528" MODIFIED="1785790618638" TEXT="innerer Widerspruch bez&#xfc;glich &#xbb;Audience&#xab;">
<linktarget COLOR="#9c3f50" DESTINATION="ID_1612420528" ENDARROW="Default" ENDINCLINATION="-1002;67;" ID="Arrow_ID_1857563368" SOURCE="ID_642718439" STARTARROW="None" STARTINCLINATION="-316;34;"/>
@ -638,16 +718,13 @@
<node CREATED="1785790510700" ID="ID_94720075" MODIFIED="1785790531089" TEXT="aber ich richte mich gezielt an diese &#x2014; weil es eigentlich um Lumiera geht"/>
<node CREATED="1785790544447" ID="ID_652954139" MODIFIED="1785790586779">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
der Talk kommt <i>fachlich</i>&#160;daher &#8212; ist aber eigentlich ein <i>Narrativ</i>&#160; und <i>allgemeines Lehrst&#252;ck</i>
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
</node>
</node>
@ -658,9 +735,7 @@
<node CREATED="1753885985959" ID="ID_1416320830" MODIFIED="1753886099702" TEXT="VBlank-Sync">
<node CREATED="1784562447086" ID="ID_1448869178" MODIFIED="1784562583608" TEXT="in der Design-Diskussion kurz ber&#xfc;hrt">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
ich habe zur Vorbereitung meine anf&#228;nglichen Ideen in einer Konversation mit Claude validiert....
@ -1255,11 +1330,35 @@
</node>
</node>
</node>
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1785860775384" ID="ID_1509658937" LINK="https://claude.ai/share/8fa0fb6e-992b-40a1-adce-f03f3f748edb" MODIFIED="1785861103377" STYLE="fork">
<richcontent TYPE="NODE"><html>
<head/>
<body>
<p>
<b>Recherche / Background</b>&#160;- Konversation mit <u>Claude</u>
</p>
</body>
</html></richcontent>
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
<linktarget COLOR="#25bbd3" DESTINATION="ID_1509658937" ENDARROW="Default" ENDINCLINATION="-1299;65;" ID="Arrow_ID_1234011492" SOURCE="ID_402484787" STARTARROW="None" STARTINCLINATION="-369;0;"/>
<font NAME="SansSerif" SIZE="14"/>
<icon BUILTIN="wizard"/>
<node COLOR="#335352" CREATED="1785860960381" ID="ID_333032086" MODIFIED="1785861084358" TEXT="lang laufender Thread &#x2014; parallel zu meinen eigenen Recherchen">
<font NAME="SansSerif" SIZE="11"/>
</node>
<node COLOR="#335352" CREATED="1785860994323" ID="ID_525300724" MODIFIED="1785861084358" TEXT="Ausgangspunkt war meine eigene Unsicherheit im Begriffsgebrauch">
<font NAME="SansSerif" SIZE="11"/>
</node>
<node COLOR="#335352" CREATED="1785861008363" ID="ID_1530707411" MODIFIED="1785861084358" TEXT="habe mir dann Details erkl&#xe4;ren lassen, die ich nur ungef&#xe4;r wu&#xdf;te">
<font NAME="SansSerif" SIZE="11"/>
</node>
<node COLOR="#335352" CREATED="1785861023766" ID="ID_391668172" MODIFIED="1785861084356" TEXT="gegen Ende wurde es eine Debatte zu Unix/Design-Philosophien">
<font NAME="SansSerif" SIZE="11"/>
</node>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1784562284612" ID="ID_1770923455" MODIFIED="1785373442117" TEXT="Performance-Aspekte">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Ich verf&#252;ge &#252;ber vertieftes Wissen und einiges an Erfahrung in diesen Bereichen, sollte aber meine Kenntnisse &#252;berpr&#252;fen und festigen, um auch auf Fragen eingehen zu k&#246;nnen. Im Besonderen zu Kernel-nahen Themen wei&#223; ich ehr wenig.
@ -1299,9 +1398,7 @@
<node CREATED="1784903760129" ID="ID_1989117356" MODIFIED="1784905020228" TEXT="das war die erste Mainstream-Sprache mit einem Memory-Model"/>
<node CREATED="1784903786078" ID="ID_1216686049" LINK="https://gee.cs.oswego.edu/dl/jmm/cookbook.html" MODIFIED="1784905020228">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
Doug Lea's &quot;<b>JSR-133 Cookbook for Compiler Writers</b>&quot;
@ -1312,9 +1409,7 @@
</node>
<node CREATED="1784903807771" ID="ID_904190729" MODIFIED="1784905020229" TEXT="Adve &amp; Gharachorloo, &quot;Shared Memory Consistency Models: A Tutorial&quot; (1996)">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Gilt als Quelle f&#252;r Doug Lea
@ -1332,9 +1427,7 @@
<node CREATED="1784903888398" ID="ID_863086016" MODIFIED="1784905020229" TEXT="Common names">
<node CREATED="1784903888399" ID="ID_12480073" MODIFIED="1784905020229" TEXT="LoadLoad read-read fence">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
loads before must complete before loads after
@ -1344,9 +1437,7 @@
</node>
<node CREATED="1784903888399" ID="ID_296525436" MODIFIED="1784905020229" TEXT="StoreStore write-write fence">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
stores before must become visible before stores after
@ -1356,9 +1447,7 @@
</node>
<node CREATED="1784903888399" ID="ID_655305278" MODIFIED="1784905020229" TEXT="LoadStore read-write fence">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
loads before must complete before stores after
@ -1368,9 +1457,7 @@
</node>
<node CREATED="1784903888400" ID="ID_636786121" MODIFIED="1784905020229" TEXT="StoreLoad write-read fence">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
stores before must become visible before loads after
@ -1393,9 +1480,7 @@
<icon BUILTIN="wizard"/>
<node CREATED="1784904935445" ID="ID_1352377610" MODIFIED="1784905006877">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
&quot;barrier/fence is hardware vocabulary for a specific machine instruction; memory_order is C++'s hardware-independent vocabulary for a guarantee; the compiler's job is to translate one into the other &#8212; and 'fence' in the standard specifically means the standalone
@ -1411,9 +1496,7 @@
<node CREATED="1784908320566" ID="ID_1732195522" MODIFIED="1784908327105" TEXT="Reactor / Proactor">
<node CREATED="1784908328417" ID="ID_1472819180" LINK="https://en.wikipedia.org/wiki/Pattern-Oriented_Software_Architecture" MODIFIED="1784908462711">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
<a rel="mw:WikiLink" href="https://en.wikipedia.org/wiki/Douglas_C._Schmidt" title="Douglas C. Schmidt">Douglas C. Schmidt</a><br /><u>Pattern-Oriented Software Architecture Volume 2</u>:<br />Patterns for Concurrent and Networked Objects
@ -1453,9 +1536,7 @@
</node>
<node CREATED="1784906198381" ID="ID_1790908983" MODIFIED="1784906267764">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
ABER: gemeinsamer Speicherzugriff <b>kostet immer</b>
@ -1470,9 +1551,7 @@
</node>
<node CREATED="1784906268926" ID="ID_66218233" MODIFIED="1784906290776">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
ABER: der Compiler kann auch <b>umordnen</b>
@ -1484,9 +1563,7 @@
</node>
<node COLOR="#435e98" CREATED="1784906855164" ID="ID_301489633" MODIFIED="1784906865587">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
Bezug zu <b>liburing</b>
@ -1512,9 +1589,7 @@
</node>
<node CREATED="1784907202617" ID="ID_673513114" MODIFIED="1784907235721" TEXT="Kosten h&#xe4;ngen praktisch nicht von anderen Threads ab">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
weil die Benchmark-Thread(s) warm
@ -1544,9 +1619,7 @@
</node>
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1784907639420" ID="ID_421389218" MODIFIED="1784907652689" STYLE="fork">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
&quot;A synchronization primitive's instruction-level cost is what a hot microbenchmark measures; its systemic cost &#8212; coherence traffic under contention, and blocking/wake latency when it isn't contended &#8212; is what your production tail latency measures, and the two can differ by two to four orders of magnitude.&quot;
</body>
@ -1556,9 +1629,7 @@
</node>
<node CREATED="1784908696776" ID="ID_1195619223" MODIFIED="1784908716530">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
Verbindung zum <b>Proactor</b>
@ -1582,9 +1653,7 @@
<node CREATED="1784909034361" ID="ID_1332104626" MODIFIED="1784909049037" TEXT="Claude ordnet das ehr als &#xbb;Duty-cycle&#xab; ein"/>
<node CREATED="1784909107922" ID="ID_901244335" MODIFIED="1784909111887">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
<strong http-equiv="content-type" content="text/html; charset=utf-8">server-vacation</strong>&#160;model
@ -1596,9 +1665,7 @@
</node>
<node CREATED="1784908878383" ID="ID_1154107045" MODIFIED="1784909008360" TEXT="Hinweis auf Submission SQPOLL">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
man kann einen Kernel-Thread konfiguieren, der die Submission-Queue pollt. Dort konfiguriert man eine Einschlafe-Periode. Wenn der Poller schl&#228;ft, setzt er ein Bit und mu&#223; von der Applikation explizit wieder reaktiviert werden.
@ -1622,9 +1689,7 @@
<node CREATED="1785373651891" ID="ID_852212282" MODIFIED="1785373656476" TEXT="der Mechanismus ist generisch">
<node CREATED="1785373696131" ID="ID_1324517392" MODIFIED="1785373774336">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
Windows has <code http-equiv="content-type" content="text/html; charset=utf-8" class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]"><font face="Monospaced" color="#733a3a">WaitOnAddress</font></code><font face="Monospaced" color="#733a3a">/<code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">WakeByAddressSingle</code>/<code class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]">WakeByAddressAll</code></font>
@ -1634,9 +1699,7 @@
</node>
<node CREATED="1785373716197" ID="ID_1245015075" MODIFIED="1785373788200">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
FreeBSD has <code http-equiv="content-type" content="text/html; charset=utf-8" class="bg-text-200/5 border border-0.5 border-border-300 text-danger-000 whitespace-pre-wrap rounded-[0.4rem] px-1 py-px text-[0.9rem]"><font face="Monospaced" color="#7a5454">_umtx_op</font></code>
@ -1654,9 +1717,7 @@
<node CREATED="1785373860741" ID="ID_1986824982" MODIFIED="1785373862088" TEXT="futex(FUTEX_WAKE, addr, n)">
<node CREATED="1785373863108" ID="ID_1980037133" MODIFIED="1785373891097">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
weckt n threads auf,
@ -1671,9 +1732,7 @@
</node>
<node CREATED="1785373936627" ID="ID_261363018" MODIFIED="1785373963603">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
beachte: der <i>uncontended-case</i>&#160;bleibt <b>komplett im userspace</b>
@ -1729,9 +1788,7 @@
<node CREATED="1785376300386" ID="ID_366808888" MODIFIED="1785376310031" TEXT="und kann vom userspace nicht manipuliert werden"/>
<node CREATED="1785376310664" ID="ID_1139400273" MODIFIED="1785376386487" TEXT="das stellt die Integrit&#xe4;t des Kernels sicher">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
man kann nur aufrufen, was der Kernel an Einsprungspunkten bietet; eigentlicher Kernel-Code ist nicht vom Userpace zug&#228;nglich
@ -1744,9 +1801,7 @@
<node CREATED="1785376387374" ID="ID_10839834" MODIFIED="1785376421394" TEXT="im Kernel dann gibt es einen entry-Stup (sichert weiteren Prozessor-State)"/>
<node CREATED="1785376444393" ID="ID_1935728415" MODIFIED="1785376496241" TEXT="Page-Table swap / hier passieren Meltdown-Mititgations">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...um Ausspionieren des Kernel via speculative execution zu verhindern
@ -1809,9 +1864,7 @@
<node CREATED="1785460704814" ID="ID_431321839" MODIFIED="1785460714572" TEXT="warum hat Linux so lange gebraucht?">
<node CREATED="1785460715693" ID="ID_1245470107" MODIFIED="1785460751768" TEXT="man hat nicht &#xbb;geschlafen&#xab;">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
man hat die Herausforderung durch die SSDs zeitnah erkannt und mit tiefgreifenden internen Umbauten begonnen
@ -1824,9 +1877,7 @@
<node CREATED="1785460833117" ID="ID_335280595" MODIFIED="1785460858629" TEXT="das scheitert daran, da&#xdf; man dann sukzessive viele Kernel-Strukturen aufdoppeln m&#xfc;&#xdf;te"/>
<node CREATED="1785460866904" ID="ID_1886883732" MODIFIED="1785460932865" TEXT="oder (wie bei libaio) da&#xdf; jedes einzelne Subsystem eine &quot;non-blocking&quot;-Variante bieten m&#xfc;&#xdf;te">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
...was nicht realisierbar ist, und deshalb ist der Ansatz von libaio stecken geblieben und bleibt auf einige Randf&#228;lle begrenzt (und noch schlimmer, kann jederzeit auf blocking waits zur&#252;ckfallen)
@ -1849,9 +1900,7 @@
<node CREATED="1785461094856" ID="ID_521796255" MODIFIED="1785461114055" TEXT="deshalb hatte BSD bisher noch keine brennende Notwendigkeit, etwas zu tun"/>
<node CREATED="1785461139724" ID="ID_997488870" MODIFIED="1785461163800">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
FreeBSD: <b>linuxulator</b>&#160;wird erzwingen, da&#223; man io_uring einfach nachbaut
@ -1874,9 +1923,7 @@
<node CREATED="1785461219863" ID="ID_537546788" MODIFIED="1785461231348" TEXT="der Paradigmenwechsel: es verwendet keinen Syscall">
<node CREATED="1785461254333" ID="ID_334883335" MODIFIED="1785461261299">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
Stichwort: <b>seccomp</b>
@ -1891,9 +1938,7 @@
<node CREATED="1785461370936" ID="ID_1804212027" MODIFIED="1785461389925" TEXT="was machen die Cloud-Provider?">
<node CREATED="1785461391129" ID="ID_1602564687" MODIFIED="1785461409560" TEXT="Google deaktiviert es f&#xfc;r seinen eigenen Code">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
u.a. auf Android
@ -1908,9 +1953,7 @@
<node CREATED="1785466404044" ID="ID_562222456" MODIFIED="1785466426860" TEXT="der Hauptautor (Axboe) entwickelt ein neues Sicherheitsframework"/>
<node CREATED="1785466427380" ID="ID_841041241" MODIFIED="1785466459628" TEXT="es beruht auf classic BPF (nicht eBPF)">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Grund ist: eBPF erfordert h&#246;here Privilegien
@ -1920,9 +1963,7 @@
</node>
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1785466487794" ID="ID_13728980" MODIFIED="1785466521731" STYLE="fork" TEXT="Claude findet das bemerkenswert">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<h3 http-equiv="content-type" content="text/html; charset=utf-8" dir="ltr" class="text-text-100 mt-3 -mb-1 text-[1.125rem] font-bold">
Where it stands now &#8212; and a lovely structural irony
@ -1964,16 +2005,13 @@
<node CREATED="1785804799556" ID="ID_1705484855" MODIFIED="1785804939678" TEXT="Gro&#xdf;rechner">
<node CREATED="1785804803735" ID="ID_1712110487" MODIFIED="1785804939678" TEXT="hatten schon in den 60ern echtes Async-processing">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
Stichwort: Channel Programs
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
<node CREATED="1785804820899" ID="ID_1792223474" MODIFIED="1785804939678" TEXT="damals war die Core extrem teuer, und die Peripherie billig"/>
</node>
@ -1981,14 +2019,11 @@
<node CREATED="1785804891691" ID="ID_570139726" MODIFIED="1785804939678" TEXT="DEC und VMS"/>
<node CREATED="1785804904554" ID="ID_565305668" MODIFIED="1785804939678" TEXT="AST: Asynchronous System Traps">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
a completion routine the system delivers by interrupting your thread. Results land in an I/O Status Block; you synchronize with event flags. Submit, continue, get called back.
</body>
</html>
</richcontent>
</html></richcontent>
</node>
</node>
<node CREATED="1785805001938" ID="ID_1225715009" MODIFIED="1785805004288" TEXT="Dave Cutler">
@ -2027,16 +2062,13 @@
<node CREATED="1785805498849" ID="ID_1654664703" MODIFIED="1785805536048" TEXT="Erlang, Aktoren, Goroutines, Coroutinen"/>
<node CREATED="1785805586557" ID="ID_945535370" MODIFIED="1785805615976" TEXT="&#xbb;function coloration&#xab; leicht gemacht">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
d.h. das Problem mit den <i>coloured functions</i>&#160;wird nicht gel&#246;st, aber der Schmerz wird reduziert
</p>
</body>
</html>
</richcontent>
</html></richcontent>
</node>
</node>
</node>
@ -2067,9 +2099,7 @@
<node CREATED="1785806906727" ID="ID_290906212" MODIFIED="1785806928635" TEXT="sein erster Artikel was als satirische &#xdc;berzeichnung gedacht"/>
<node CREATED="1785806929283" ID="ID_1833959351" MODIFIED="1785807147619">
<richcontent TYPE="NODE"><html>
<head>
</head>
<head/>
<body>
<p>
nachdem der Artikel so stark eingeschlagen hat,
@ -2078,8 +2108,7 @@
hat er selber, unter einem Pseudonym, eine Replik geschrieben
</p>
</body>
</html>
</richcontent>
</html></richcontent>
<node CREATED="1785807193346" ID="ID_977154454" LINK="http://dreamsongs.com/Files/worse-is-worse.pdf" MODIFIED="1785807201901" TEXT="&quot;Worse is Better is Worse&quot;"/>
</node>
<node CREATED="1785807148579" ID="ID_1979393125" LINK="https://dreamsongs.com/Essays.html" MODIFIED="1785807160610" TEXT="Essays von Richard Gabriel">
@ -4005,9 +4034,7 @@
<icon BUILTIN="back"/>
<node CREATED="1784226890979" ID="ID_473596944" MODIFIED="1784562049044" TEXT="ffplay">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
ffplay -f rawvideo -pixel_format rgb24 -video_size 1200x675 -framerate 50 demovideo-1200.raw
@ -4018,9 +4045,7 @@
</node>
<node CREATED="1784226921919" ID="ID_1975886749" MODIFIED="1784562062109" TEXT="vlc">
<richcontent TYPE="NOTE"><html>
<head>
</head>
<head/>
<body>
<p>
vlc --demux rawvideo --rawvid-width=1200 --rawvid-height=675 --rawvid-fps=50 --rawvid-chroma=RV24 --vout=opengl demovideo-1200.raw
@ -4350,6 +4375,61 @@
</node>
</node>
</node>
<node CREATED="1785860391030" ID="ID_1841666778" MODIFIED="1785860400265" TEXT="Research-Sektion auf Lumiera.org">
<node CREATED="1785860406146" ID="ID_17114163" MODIFIED="1785860420192" TEXT="Overview"/>
<node CREATED="1785860425383" ID="ID_468739218" MODIFIED="1785860427904" TEXT="Resources">
<node BACKGROUND_COLOR="#eef0c5" COLOR="#990000" CREATED="1785860434326" ID="ID_1458908971" MODIFIED="1785860443825" TEXT="TimeScales">
<icon BUILTIN="pencil"/>
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1785860445180" ID="ID_153676570" MODIFIED="1785860464957" STYLE="fork" TEXT="Informationen zusammengetragen mit Claude">
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
<icon BUILTIN="wizard"/>
<node CREATED="1785860471809" ID="ID_402484787" MODIFIED="1785861103377" TEXT="teilweise bereits im Background/Recherche-Chat">
<arrowlink COLOR="#25bbd3" DESTINATION="ID_1509658937" ENDARROW="Default" ENDINCLINATION="-1299;65;" ID="Arrow_ID_1234011492" STARTARROW="None" STARTINCLINATION="-369;0;"/>
</node>
<node CREATED="1785861111621" ID="ID_1704710749" MODIFIED="1785861189290" TEXT="habe meine eigenen Erfahrungswerte validieren lassen">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
alle meine eigenen Erfahrungen und Richtwerte konnten vollumf&#228;nglich best&#228;tigt werden; manche Me&#223;werte kann ich nun besser einordnen, z.B. durch die Unterscheidung in hott-pressed und systemic-cost. Erheblich dazugelernt habe ich bez&#252;glich <b>Futex</b>es
</p>
</body>
</html></richcontent>
</node>
<node BACKGROUND_COLOR="#c8c0b6" COLOR="#435e98" CREATED="1785861193146" ID="ID_387177535" LINK="#ID_1762091251" MODIFIED="1785861228218" TEXT="habe daraus ein Diagramm bauen lassen">
<icon BUILTIN="idea"/>
</node>
<node CREATED="1785861229354" ID="ID_162556201" MODIFIED="1785861244236" TEXT="au&#xdf;erdem: alle Zahlenwerte in Tabelle konsolidiert">
<node CREATED="1785861248495" ID="ID_677703208" MODIFIED="1785861264545" TEXT="Richtwerte(Zeit/Latenz)"/>
<node CREATED="1785861265022" ID="ID_256243482" MODIFIED="1785861266825" TEXT="Bedeutung"/>
<node CREATED="1785861267284" ID="ID_1608529429" MODIFIED="1785861270480" TEXT="Quellen als Fu&#xdf;noten"/>
<node CREATED="1785861270940" ID="ID_1066989785" MODIFIED="1785861302195" TEXT="Spalte &#xbb;gef&#xfc;hlte Zeit&#xab;">
<richcontent TYPE="NOTE"><html>
<head/>
<body>
<p>
wenn ein Clock-cycle eine Sekunde dauern w&#252;rde....
</p>
</body>
</html></richcontent>
<icon BUILTIN="ksmiletris"/>
</node>
</node>
<node CREATED="1785861304416" ID="ID_1042454460" MODIFIED="1785861317095" TEXT="lasse einge Abschnitte mit Erl&#xe4;uterungen generieren"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1785861321077" ID="ID_1405791777" MODIFIED="1785861334834" TEXT="Dokument formal/technisch fertigstellen">
<icon BUILTIN="flag-yellow"/>
</node>
<node BACKGROUND_COLOR="#fdfdcf" COLOR="#ff0000" CREATED="1785861335967" ID="ID_493138343" MODIFIED="1785861342325" TEXT="Quellen nochmal gegenchecken">
<icon BUILTIN="flag-pink"/>
</node>
<node BACKGROUND_COLOR="#eee5c3" COLOR="#990000" CREATED="1785861381365" ID="ID_1527233926" MODIFIED="1785861393616" TEXT="Text &#xfc;berarbeiten / erg&#xe4;nzen">
<icon BUILTIN="flag-yellow"/>
</node>
</node>
</node>
<node CREATED="1785860420848" ID="ID_1771994994" MODIFIED="1785860424896" TEXT="Demo-Code"/>
</node>
</node>
<node CREATED="1781400461948" FOLDED="true" ID="ID_1876863038" MODIFIED="1783362593603" TEXT="Laptop">
<icon BUILTIN="go"/>

View file

@ -0,0 +1,245 @@
#!/usr/bin/env python3
"""Generate the FrOSCon io_uring talk 'time scales' wrapped-log-scale diagram.
Page: 254 x 142.9 mm (== LibreOffice Impress 16:9 slide, 25.4 x 14.29 cm)
User unit == 1 mm.
Structure: four bands, each spanning exactly three decades (1 -> 1000 of its
unit), all at identical horizontal scale. Each band is a separate Inkscape
layer so the whole thing can be exported step by step (Export > Page) and
stacked as transparent overlays in Impress.
@note Code generated 8/2026 by Claude -- prompted by Ichthyostega
"""
import math
W, H = 254.0, 142.9
X0, X1 = 34.0, 240.0 # axis: value 1 .. value 1000
DEC = (X1 - X0) / 3.0 # mm per decade
AXIS_END = 243.5 # slight overshoot + chevron
BANDS_Y = [40.0, 75.0, 102.0, 120.0]
# ---- colours -------------------------------------------------------------
C_AXIS = "#555555"
C_MINOR = "#b3b3b3"
C_MACH = "#14507d"
C_HUMAN = "#b4530a"
C_VAL_M = "#4a7ba6"
C_VAL_H = "#c98342"
C_MUTED = "#8a8a8a"
C_TITLE = "#222222"
# ---- type ----------------------------------------------------------------
F_NAME, F_VAL, F_BAND, F_TITLE, F_NOTE, F_DISC = 3.8, 3.2, 5.5, 8.0, 3.6, 3.0
FONT = "DejaVu Sans, Verdana, sans-serif"
# tier offsets relative to band axis Y
UP1_NAME, UP1_VAL, UP1_STUB = -10.3, -6.5, -5.0
UP2_NAME, UP2_VAL, UP2_STUB = -18.3, -14.5, -13.0
LO1_NAME, LO1_VAL, LO1_STUB = 11.2, 7.5, 5.0
BRACKET_Y = 4.0
def X(v):
return X0 + DEC * math.log10(v)
def esc(s):
return (s.replace("&", "&amp;").replace("<", "&lt;").replace(">", "&gt;"))
def text(x, y, s, size, fill, anchor="middle", weight="normal"):
return (f'<text x="{x:.2f}" y="{y:.2f}" font-family="{FONT}" '
f'font-size="{size}" font-weight="{weight}" fill="{fill}" '
f'text-anchor="{anchor}">{esc(s)}</text>')
def line(x1, y1, x2, y2, stroke, w, dash=None):
d = f' stroke-dasharray="{dash}"' if dash else ""
return (f'<path d="M {x1:.2f},{y1:.2f} L {x2:.2f},{y2:.2f}" '
f'stroke="{stroke}" stroke-width="{w}" fill="none"{d}/>')
# ==========================================================================
# entry data
# ==========================================================================
# tier: 'up1' | 'up2' | 'lo1'
# kind: 'machine' | 'human'
# span: (v_lo, v_hi) -> draw a bracket instead of a dot
BANDS = [
dict(unit="1 ns", ay=BANDS_Y[0], layer="ns", entries=[
dict(v=1, name="L1 cache", val="1 ns", tier="up1", kind="machine"),
dict(v=4, name="L2 cache", val="4 ns", tier="up1", kind="machine"),
dict(v=10, name="full memory barrier", val="10 ns", tier="up2", kind="machine"),
dict(v=15, name="L3 cache", val="15 ns", tier="up1", kind="machine"),
dict(v=100, name="DRAM access", val="100 ns", tier="up1", kind="machine"),
dict(span=(50, 100), name="contended cache line", val="50-100 ns",
tier="lo1", kind="machine"),
dict(offscale=True, name="1 clock cycle", val="\u2248 0.3 ns",
tier="lo1", kind="machine"),
]),
dict(unit="1 \u00b5s", ay=BANDS_Y[1], layer="us", entries=[
dict(v=1, name="system call", val="1 \u00b5s", tier="up1", kind="machine"),
dict(v=2, name="context switch", val="2 \u00b5s", tier="up2", kind="machine"),
dict(v=50, name="OS timer slack", val="50 \u00b5s", tier="up1", kind="machine"),
dict(span=(10, 30), name="thread wake-up", val="10-30 \u00b5s",
tier="lo1", kind="machine"),
dict(span=(50, 100), name="SSD read", val="50-100 \u00b5s",
tier="lo1", kind="machine"),
]),
dict(unit="1 ms", ay=BANDS_Y[2], layer="ms", entries=[
dict(v=10, name="disk seek", val="10 ms", tier="up1", kind="machine"),
dict(v=40, name="one frame @ 25 fps", val="40 ms", tier="up1", kind="machine"),
dict(v=10, name="barely noticeable", val="10 ms", tier="lo1", kind="human"),
dict(v=100, name="UI feels sluggish", val="100 ms", tier="lo1", kind="human"),
]),
dict(unit="1 s", ay=BANDS_Y[3], layer="s", entries=[
dict(v=1, name="\u201cit\u2019s thinking\u2026\u201d", val="1 s",
tier="lo1", kind="human"),
dict(v=180, name="another coffee", val="3 min", tier="lo1", kind="human"),
dict(v=900, name="go do something else", val="\u2248 15 min",
tier="lo1", kind="human", anchor="end", ax=250.0),
]),
]
# ==========================================================================
# scaffold
# ==========================================================================
def band_axis(ay, unit):
o = []
o.append(line(X0, ay, AXIS_END, ay, C_AXIS, 0.6))
# continuation chevron
o.append(f'<path d="M {AXIS_END-2.4:.2f},{ay-1.6:.2f} L {AXIS_END:.2f},{ay:.2f} '
f'L {AXIS_END-2.4:.2f},{ay+1.6:.2f}" stroke="{C_AXIS}" '
f'stroke-width="0.6" fill="none"/>')
# ticks
for d in range(3):
base = 10 ** d
for m in range(1, 10):
v = base * m
x = X(v)
if m == 1:
o.append(line(x, ay - 3.0, x, ay + 3.0, C_AXIS, 0.6))
else:
o.append(line(x, ay - 1.6, x, ay + 1.6, C_MINOR, 0.25))
o.append(line(X(1000), ay - 3.0, X(1000), ay + 3.0, C_AXIS, 0.6))
o.append(text(32.0, ay + 2.0, unit, F_BAND, C_MUTED, "end", "600"))
return o
def scaffold():
o = []
o.append(text(10.0, 15.0, "Time scales", F_TITLE, C_TITLE, "start", "600"))
o.append(text(244.0, 12.5, "each row spans three decades", F_NOTE, C_MUTED, "end"))
o.append(text(244.0, 17.0, "one row down = \u00d7 1000", F_NOTE, C_MUTED, "end"))
for b in BANDS:
o += band_axis(b["ay"], b["unit"])
o.append(text(10.0, 137.0,
"Order of magnitude only \u2014 contemporary desktop / laptop x86. "
"Expect \u00b1 2\u20133\u00d7; the ratios are the point.",
F_DISC, C_MUTED, "start"))
return o
# ==========================================================================
# entries
# ==========================================================================
def render_entry(e, ay):
kind = e.get("kind", "machine")
cn = C_MACH if kind == "machine" else C_HUMAN
cv = C_VAL_M if kind == "machine" else C_VAL_H
tier = e["tier"]
anchor = e.get("anchor", "middle")
o = []
if tier == "up1":
ny, vy, sy = ay + UP1_NAME, ay + UP1_VAL, ay + UP1_STUB
elif tier == "up2":
ny, vy, sy = ay + UP2_NAME, ay + UP2_VAL, ay + UP2_STUB
else:
ny, vy, sy = ay + LO1_NAME, ay + LO1_VAL, ay + LO1_STUB
if e.get("offscale"):
# dashed stub running off the left-hand end of the scale
y = ay + BRACKET_Y + 0.5
o.append(line(X0, y, 22.5, y, cn, 0.5, dash="1.6,1.2"))
o.append(f'<path d="M 25.4,{y-1.6:.2f} L 22.0,{y:.2f} L 25.4,{y+1.6:.2f}" '
f'stroke="{cn}" stroke-width="0.6" fill="none"/>')
ax = 22.0
o.append(text(ax, vy, e["val"], F_VAL, cv, "start"))
o.append(text(ax, ny, e["name"], F_NAME, cn, "start", "600"))
return o
if "span" in e:
x1, x2 = X(e["span"][0]), X(e["span"][1])
by = ay + BRACKET_Y
o.append(line(x1, by, x2, by, cn, 0.7))
o.append(line(x1, by, x1, ay, cn, 0.7))
o.append(line(x2, by, x2, ay, cn, 0.7))
ax = e.get("ax", (x1 + x2) / 2.0)
else:
x = X(e["v"])
o.append(f'<circle cx="{x:.2f}" cy="{ay:.2f}" r="1.3" fill="{cn}"/>')
stub_from = ay - 2.0 if tier.startswith("up") else ay + 2.0
o.append(line(x, stub_from, x, sy, cn, 0.4))
ax = e.get("ax", x)
o.append(text(ax, vy, e["val"], F_VAL, cv, anchor))
o.append(text(ax, ny, e["name"], F_NAME, cn, anchor, "600"))
return o
# ==========================================================================
# assemble
# ==========================================================================
def layer(lid, label, body, display="inline"):
return (f'<g inkscape:groupmode="layer" id="{lid}" '
f'inkscape:label="{label}" style="display:{display}">\n '
+ "\n ".join(body) + "\n</g>")
def build():
layers = []
layers.append(layer("layer-1-scaffold", "1 - scaffold", scaffold()))
for i, b in enumerate(BANDS):
if b["layer"] == "ms":
mach = [x for e in b["entries"] if e["kind"] == "machine"
for x in render_entry(e, b["ay"])]
hum = [x for e in b["entries"] if e["kind"] == "human"
for x in render_entry(e, b["ay"])]
layers.append(layer("layer-4-ms-machine", "4 - ms band (machine)", mach))
layers.append(layer("layer-5-ms-human", "5 - ms band (human)", hum))
elif b["layer"] == "ns":
body = [x for e in b["entries"] for x in render_entry(e, b["ay"])]
layers.append(layer("layer-2-ns", "2 - ns band", body))
elif b["layer"] == "us":
body = [x for e in b["entries"] for x in render_entry(e, b["ay"])]
layers.append(layer("layer-3-us", "3 - us band", body))
else:
body = [x for e in b["entries"] for x in render_entry(e, b["ay"])]
layers.append(layer("layer-6-s", "6 - s band (human)", body))
dim = [f'<rect x="0" y="22" width="{W}" height="67" fill="#ffffff" '
f'fill-opacity="0.74"/>']
layers.append(layer("layer-7-dim", "7 - dim ns+us (optional)", dim,
display="none"))
return f'''<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
width="{W}mm" height="{H}mm" viewBox="0 0 {W} {H}" version="1.1">
<sodipodi:namedview inkscape:document-units="mm" units="mm"/>
<title>Time scales - ns to minutes, wrapped logarithmic</title>
{chr(10).join(layers)}
</svg>
'''
if __name__ == "__main__":
open("/mnt/user-data/outputs/time-scales.svg", "w").write(build())
print("written")

View file

@ -0,0 +1,230 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
width="254.0mm" height="142.9mm" viewBox="0 0 254.0 142.9" version="1.1">
<sodipodi:namedview inkscape:document-units="mm" units="mm"/>
<title>Time scales - ns to minutes, wrapped logarithmic</title>
<g inkscape:groupmode="layer" id="layer-1-scaffold" inkscape:label="1 - scaffold" style="display:inline">
<text x="10.00" y="15.00" font-family="DejaVu Sans, Verdana, sans-serif" font-size="8.0" font-weight="600" fill="#222222" text-anchor="start">Time scales</text>
<text x="244.00" y="12.50" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.6" font-weight="normal" fill="#8a8a8a" text-anchor="end">each row spans three decades</text>
<text x="244.00" y="17.00" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.6" font-weight="normal" fill="#8a8a8a" text-anchor="end">one row down = × 1000</text>
<path d="M 34.00,40.00 L 243.50,40.00" stroke="#555555" stroke-width="0.6" fill="none"/>
<path d="M 241.10,38.40 L 243.50,40.00 L 241.10,41.60" stroke="#555555" stroke-width="0.6" fill="none"/>
<path d="M 34.00,37.00 L 34.00,43.00" stroke="#555555" stroke-width="0.6" fill="none"/>
<path d="M 54.67,38.40 L 54.67,41.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 66.76,38.40 L 66.76,41.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 75.34,38.40 L 75.34,41.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 82.00,38.40 L 82.00,41.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 87.43,38.40 L 87.43,41.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 92.03,38.40 L 92.03,41.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 96.01,38.40 L 96.01,41.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 99.52,38.40 L 99.52,41.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 102.67,37.00 L 102.67,43.00" stroke="#555555" stroke-width="0.6" fill="none"/>
<path d="M 123.34,38.40 L 123.34,41.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 135.43,38.40 L 135.43,41.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 144.01,38.40 L 144.01,41.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 150.66,38.40 L 150.66,41.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 156.10,38.40 L 156.10,41.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 160.70,38.40 L 160.70,41.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 164.68,38.40 L 164.68,41.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 168.19,38.40 L 168.19,41.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 171.33,37.00 L 171.33,43.00" stroke="#555555" stroke-width="0.6" fill="none"/>
<path d="M 192.00,38.40 L 192.00,41.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 204.10,38.40 L 204.10,41.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 212.67,38.40 L 212.67,41.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 219.33,38.40 L 219.33,41.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 224.77,38.40 L 224.77,41.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 229.36,38.40 L 229.36,41.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 233.35,38.40 L 233.35,41.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 236.86,38.40 L 236.86,41.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 240.00,37.00 L 240.00,43.00" stroke="#555555" stroke-width="0.6" fill="none"/>
<text x="32.00" y="42.00" font-family="DejaVu Sans, Verdana, sans-serif" font-size="5.5" font-weight="600" fill="#8a8a8a" text-anchor="end">1 ns</text>
<path d="M 34.00,75.00 L 243.50,75.00" stroke="#555555" stroke-width="0.6" fill="none"/>
<path d="M 241.10,73.40 L 243.50,75.00 L 241.10,76.60" stroke="#555555" stroke-width="0.6" fill="none"/>
<path d="M 34.00,72.00 L 34.00,78.00" stroke="#555555" stroke-width="0.6" fill="none"/>
<path d="M 54.67,73.40 L 54.67,76.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 66.76,73.40 L 66.76,76.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 75.34,73.40 L 75.34,76.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 82.00,73.40 L 82.00,76.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 87.43,73.40 L 87.43,76.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 92.03,73.40 L 92.03,76.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 96.01,73.40 L 96.01,76.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 99.52,73.40 L 99.52,76.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 102.67,72.00 L 102.67,78.00" stroke="#555555" stroke-width="0.6" fill="none"/>
<path d="M 123.34,73.40 L 123.34,76.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 135.43,73.40 L 135.43,76.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 144.01,73.40 L 144.01,76.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 150.66,73.40 L 150.66,76.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 156.10,73.40 L 156.10,76.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 160.70,73.40 L 160.70,76.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 164.68,73.40 L 164.68,76.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 168.19,73.40 L 168.19,76.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 171.33,72.00 L 171.33,78.00" stroke="#555555" stroke-width="0.6" fill="none"/>
<path d="M 192.00,73.40 L 192.00,76.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 204.10,73.40 L 204.10,76.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 212.67,73.40 L 212.67,76.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 219.33,73.40 L 219.33,76.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 224.77,73.40 L 224.77,76.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 229.36,73.40 L 229.36,76.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 233.35,73.40 L 233.35,76.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 236.86,73.40 L 236.86,76.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 240.00,72.00 L 240.00,78.00" stroke="#555555" stroke-width="0.6" fill="none"/>
<text x="32.00" y="77.00" font-family="DejaVu Sans, Verdana, sans-serif" font-size="5.5" font-weight="600" fill="#8a8a8a" text-anchor="end">1 µs</text>
<path d="M 34.00,102.00 L 243.50,102.00" stroke="#555555" stroke-width="0.6" fill="none"/>
<path d="M 241.10,100.40 L 243.50,102.00 L 241.10,103.60" stroke="#555555" stroke-width="0.6" fill="none"/>
<path d="M 34.00,99.00 L 34.00,105.00" stroke="#555555" stroke-width="0.6" fill="none"/>
<path d="M 54.67,100.40 L 54.67,103.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 66.76,100.40 L 66.76,103.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 75.34,100.40 L 75.34,103.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 82.00,100.40 L 82.00,103.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 87.43,100.40 L 87.43,103.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 92.03,100.40 L 92.03,103.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 96.01,100.40 L 96.01,103.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 99.52,100.40 L 99.52,103.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 102.67,99.00 L 102.67,105.00" stroke="#555555" stroke-width="0.6" fill="none"/>
<path d="M 123.34,100.40 L 123.34,103.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 135.43,100.40 L 135.43,103.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 144.01,100.40 L 144.01,103.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 150.66,100.40 L 150.66,103.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 156.10,100.40 L 156.10,103.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 160.70,100.40 L 160.70,103.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 164.68,100.40 L 164.68,103.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 168.19,100.40 L 168.19,103.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 171.33,99.00 L 171.33,105.00" stroke="#555555" stroke-width="0.6" fill="none"/>
<path d="M 192.00,100.40 L 192.00,103.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 204.10,100.40 L 204.10,103.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 212.67,100.40 L 212.67,103.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 219.33,100.40 L 219.33,103.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 224.77,100.40 L 224.77,103.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 229.36,100.40 L 229.36,103.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 233.35,100.40 L 233.35,103.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 236.86,100.40 L 236.86,103.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 240.00,99.00 L 240.00,105.00" stroke="#555555" stroke-width="0.6" fill="none"/>
<text x="32.00" y="104.00" font-family="DejaVu Sans, Verdana, sans-serif" font-size="5.5" font-weight="600" fill="#8a8a8a" text-anchor="end">1 ms</text>
<path d="M 34.00,120.00 L 243.50,120.00" stroke="#555555" stroke-width="0.6" fill="none"/>
<path d="M 241.10,118.40 L 243.50,120.00 L 241.10,121.60" stroke="#555555" stroke-width="0.6" fill="none"/>
<path d="M 34.00,117.00 L 34.00,123.00" stroke="#555555" stroke-width="0.6" fill="none"/>
<path d="M 54.67,118.40 L 54.67,121.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 66.76,118.40 L 66.76,121.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 75.34,118.40 L 75.34,121.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 82.00,118.40 L 82.00,121.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 87.43,118.40 L 87.43,121.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 92.03,118.40 L 92.03,121.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 96.01,118.40 L 96.01,121.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 99.52,118.40 L 99.52,121.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 102.67,117.00 L 102.67,123.00" stroke="#555555" stroke-width="0.6" fill="none"/>
<path d="M 123.34,118.40 L 123.34,121.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 135.43,118.40 L 135.43,121.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 144.01,118.40 L 144.01,121.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 150.66,118.40 L 150.66,121.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 156.10,118.40 L 156.10,121.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 160.70,118.40 L 160.70,121.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 164.68,118.40 L 164.68,121.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 168.19,118.40 L 168.19,121.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 171.33,117.00 L 171.33,123.00" stroke="#555555" stroke-width="0.6" fill="none"/>
<path d="M 192.00,118.40 L 192.00,121.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 204.10,118.40 L 204.10,121.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 212.67,118.40 L 212.67,121.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 219.33,118.40 L 219.33,121.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 224.77,118.40 L 224.77,121.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 229.36,118.40 L 229.36,121.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 233.35,118.40 L 233.35,121.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 236.86,118.40 L 236.86,121.60" stroke="#b3b3b3" stroke-width="0.25" fill="none"/>
<path d="M 240.00,117.00 L 240.00,123.00" stroke="#555555" stroke-width="0.6" fill="none"/>
<text x="32.00" y="122.00" font-family="DejaVu Sans, Verdana, sans-serif" font-size="5.5" font-weight="600" fill="#8a8a8a" text-anchor="end">1 s</text>
<text x="10.00" y="137.00" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.0" font-weight="normal" fill="#8a8a8a" text-anchor="start">Order of magnitude only — contemporary desktop / laptop x86. Expect ± 2–3×; the ratios are the point.</text>
</g>
<g inkscape:groupmode="layer" id="layer-2-ns" inkscape:label="2 - ns band" style="display:inline">
<circle cx="34.00" cy="40.00" r="1.3" fill="#14507d"/>
<path d="M 34.00,38.00 L 34.00,35.00" stroke="#14507d" stroke-width="0.4" fill="none"/>
<text x="34.00" y="33.50" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.2" font-weight="normal" fill="#4a7ba6" text-anchor="middle">1 ns</text>
<text x="34.00" y="29.70" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.8" font-weight="600" fill="#14507d" text-anchor="middle">L1 cache</text>
<circle cx="75.34" cy="40.00" r="1.3" fill="#14507d"/>
<path d="M 75.34,38.00 L 75.34,35.00" stroke="#14507d" stroke-width="0.4" fill="none"/>
<text x="75.34" y="33.50" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.2" font-weight="normal" fill="#4a7ba6" text-anchor="middle">4 ns</text>
<text x="75.34" y="29.70" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.8" font-weight="600" fill="#14507d" text-anchor="middle">L2 cache</text>
<circle cx="102.67" cy="40.00" r="1.3" fill="#14507d"/>
<path d="M 102.67,38.00 L 102.67,27.00" stroke="#14507d" stroke-width="0.4" fill="none"/>
<text x="102.67" y="25.50" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.2" font-weight="normal" fill="#4a7ba6" text-anchor="middle">10 ns</text>
<text x="102.67" y="21.70" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.8" font-weight="600" fill="#14507d" text-anchor="middle">full memory barrier</text>
<circle cx="114.76" cy="40.00" r="1.3" fill="#14507d"/>
<path d="M 114.76,38.00 L 114.76,35.00" stroke="#14507d" stroke-width="0.4" fill="none"/>
<text x="114.76" y="33.50" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.2" font-weight="normal" fill="#4a7ba6" text-anchor="middle">15 ns</text>
<text x="114.76" y="29.70" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.8" font-weight="600" fill="#14507d" text-anchor="middle">L3 cache</text>
<circle cx="171.33" cy="40.00" r="1.3" fill="#14507d"/>
<path d="M 171.33,38.00 L 171.33,35.00" stroke="#14507d" stroke-width="0.4" fill="none"/>
<text x="171.33" y="33.50" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.2" font-weight="normal" fill="#4a7ba6" text-anchor="middle">100 ns</text>
<text x="171.33" y="29.70" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.8" font-weight="600" fill="#14507d" text-anchor="middle">DRAM access</text>
<path d="M 150.66,44.00 L 171.33,44.00" stroke="#14507d" stroke-width="0.7" fill="none"/>
<path d="M 150.66,44.00 L 150.66,40.00" stroke="#14507d" stroke-width="0.7" fill="none"/>
<path d="M 171.33,44.00 L 171.33,40.00" stroke="#14507d" stroke-width="0.7" fill="none"/>
<text x="161.00" y="47.50" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.2" font-weight="normal" fill="#4a7ba6" text-anchor="middle">50-100 ns</text>
<text x="161.00" y="51.20" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.8" font-weight="600" fill="#14507d" text-anchor="middle">contended cache line</text>
<path d="M 34.00,44.50 L 22.50,44.50" stroke="#14507d" stroke-width="0.5" fill="none" stroke-dasharray="1.6,1.2"/>
<path d="M 25.4,42.90 L 22.0,44.50 L 25.4,46.10" stroke="#14507d" stroke-width="0.6" fill="none"/>
<text x="22.00" y="47.50" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.2" font-weight="normal" fill="#4a7ba6" text-anchor="start">≈ 0.3 ns</text>
<text x="22.00" y="51.20" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.8" font-weight="600" fill="#14507d" text-anchor="start">1 clock cycle</text>
</g>
<g inkscape:groupmode="layer" id="layer-3-us" inkscape:label="3 - us band" style="display:inline">
<circle cx="34.00" cy="75.00" r="1.3" fill="#14507d"/>
<path d="M 34.00,73.00 L 34.00,70.00" stroke="#14507d" stroke-width="0.4" fill="none"/>
<text x="34.00" y="68.50" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.2" font-weight="normal" fill="#4a7ba6" text-anchor="middle">1 µs</text>
<text x="34.00" y="64.70" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.8" font-weight="600" fill="#14507d" text-anchor="middle">system call</text>
<circle cx="54.67" cy="75.00" r="1.3" fill="#14507d"/>
<path d="M 54.67,73.00 L 54.67,62.00" stroke="#14507d" stroke-width="0.4" fill="none"/>
<text x="54.67" y="60.50" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.2" font-weight="normal" fill="#4a7ba6" text-anchor="middle">2 µs</text>
<text x="54.67" y="56.70" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.8" font-weight="600" fill="#14507d" text-anchor="middle">context switch</text>
<circle cx="150.66" cy="75.00" r="1.3" fill="#14507d"/>
<path d="M 150.66,73.00 L 150.66,70.00" stroke="#14507d" stroke-width="0.4" fill="none"/>
<text x="150.66" y="68.50" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.2" font-weight="normal" fill="#4a7ba6" text-anchor="middle">50 µs</text>
<text x="150.66" y="64.70" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.8" font-weight="600" fill="#14507d" text-anchor="middle">OS timer slack</text>
<path d="M 102.67,79.00 L 135.43,79.00" stroke="#14507d" stroke-width="0.7" fill="none"/>
<path d="M 102.67,79.00 L 102.67,75.00" stroke="#14507d" stroke-width="0.7" fill="none"/>
<path d="M 135.43,79.00 L 135.43,75.00" stroke="#14507d" stroke-width="0.7" fill="none"/>
<text x="119.05" y="82.50" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.2" font-weight="normal" fill="#4a7ba6" text-anchor="middle">10-30 µs</text>
<text x="119.05" y="86.20" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.8" font-weight="600" fill="#14507d" text-anchor="middle">thread wake-up</text>
<path d="M 150.66,79.00 L 171.33,79.00" stroke="#14507d" stroke-width="0.7" fill="none"/>
<path d="M 150.66,79.00 L 150.66,75.00" stroke="#14507d" stroke-width="0.7" fill="none"/>
<path d="M 171.33,79.00 L 171.33,75.00" stroke="#14507d" stroke-width="0.7" fill="none"/>
<text x="161.00" y="82.50" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.2" font-weight="normal" fill="#4a7ba6" text-anchor="middle">50-100 µs</text>
<text x="161.00" y="86.20" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.8" font-weight="600" fill="#14507d" text-anchor="middle">SSD read</text>
</g>
<g inkscape:groupmode="layer" id="layer-4-ms-machine" inkscape:label="4 - ms band (machine)" style="display:inline">
<circle cx="102.67" cy="102.00" r="1.3" fill="#14507d"/>
<path d="M 102.67,100.00 L 102.67,97.00" stroke="#14507d" stroke-width="0.4" fill="none"/>
<text x="102.67" y="95.50" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.2" font-weight="normal" fill="#4a7ba6" text-anchor="middle">10 ms</text>
<text x="102.67" y="91.70" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.8" font-weight="600" fill="#14507d" text-anchor="middle">disk seek</text>
<circle cx="144.01" cy="102.00" r="1.3" fill="#14507d"/>
<path d="M 144.01,100.00 L 144.01,97.00" stroke="#14507d" stroke-width="0.4" fill="none"/>
<text x="144.01" y="95.50" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.2" font-weight="normal" fill="#4a7ba6" text-anchor="middle">40 ms</text>
<text x="144.01" y="91.70" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.8" font-weight="600" fill="#14507d" text-anchor="middle">one frame @ 25 fps</text>
</g>
<g inkscape:groupmode="layer" id="layer-5-ms-human" inkscape:label="5 - ms band (human)" style="display:inline">
<circle cx="102.67" cy="102.00" r="1.3" fill="#b4530a"/>
<path d="M 102.67,104.00 L 102.67,107.00" stroke="#b4530a" stroke-width="0.4" fill="none"/>
<text x="102.67" y="109.50" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.2" font-weight="normal" fill="#c98342" text-anchor="middle">10 ms</text>
<text x="102.67" y="113.20" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.8" font-weight="600" fill="#b4530a" text-anchor="middle">barely noticeable</text>
<circle cx="171.33" cy="102.00" r="1.3" fill="#b4530a"/>
<path d="M 171.33,104.00 L 171.33,107.00" stroke="#b4530a" stroke-width="0.4" fill="none"/>
<text x="171.33" y="109.50" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.2" font-weight="normal" fill="#c98342" text-anchor="middle">100 ms</text>
<text x="171.33" y="113.20" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.8" font-weight="600" fill="#b4530a" text-anchor="middle">UI feels sluggish</text>
</g>
<g inkscape:groupmode="layer" id="layer-6-s" inkscape:label="6 - s band (human)" style="display:inline">
<circle cx="34.00" cy="120.00" r="1.3" fill="#b4530a"/>
<path d="M 34.00,122.00 L 34.00,125.00" stroke="#b4530a" stroke-width="0.4" fill="none"/>
<text x="34.00" y="127.50" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.2" font-weight="normal" fill="#c98342" text-anchor="middle">1 s</text>
<text x="34.00" y="131.20" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.8" font-weight="600" fill="#b4530a" text-anchor="middle">“it’s thinking…”</text>
<circle cx="188.86" cy="120.00" r="1.3" fill="#b4530a"/>
<path d="M 188.86,122.00 L 188.86,125.00" stroke="#b4530a" stroke-width="0.4" fill="none"/>
<text x="188.86" y="127.50" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.2" font-weight="normal" fill="#c98342" text-anchor="middle">3 min</text>
<text x="188.86" y="131.20" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.8" font-weight="600" fill="#b4530a" text-anchor="middle">another coffee</text>
<circle cx="236.86" cy="120.00" r="1.3" fill="#b4530a"/>
<path d="M 236.86,122.00 L 236.86,125.00" stroke="#b4530a" stroke-width="0.4" fill="none"/>
<text x="250.00" y="127.50" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.2" font-weight="normal" fill="#c98342" text-anchor="end">≈ 15 min</text>
<text x="250.00" y="131.20" font-family="DejaVu Sans, Verdana, sans-serif" font-size="3.8" font-weight="600" fill="#b4530a" text-anchor="end">go do something else</text>
</g>
<g inkscape:groupmode="layer" id="layer-7-dim" inkscape:label="7 - dim ns+us (optional)" style="display:none">
<rect x="0" y="22" width="254.0" height="67" fill="#ffffff" fill-opacity="0.74"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 23 KiB

View file

@ -0,0 +1,264 @@
Time Scales
===========
:Author: Lumiera Project
:toc:
Anyone reasoning about I/O, threading or real-time media processing eventually
needs a feeling for _how long things take_. The trouble is that the relevant
durations span more than twelve orders of magnitude -- from a single CPU clock
cycle up to the point where a human gives up waiting. Numbers on that scale
resist intuition, and so we tend to reason about ``fast'' and ``slow'' without
noticing that the two words can be separated by a factor of a million.
The following table collects the figures that matter in this context. It is
meant as a rough orientation, not as a benchmark result.
NOTE: All values are order-of-magnitude figures for contemporary desktop and
laptop x86 hardware. Expect a deviation of 2--3{nbsp}x depending on
microarchitecture, memory configuration, kernel version and load. The
_ratios_ are the point, not the digits.
The last column rescales everything by a single constant factor, chosen so
that one CPU clock cycle (≙ 0.3{nbsp}ns at 3.3{nbsp}GHz) becomes one second
of human time. This is the most effective way we know to convey what a cache
miss or a disk seek actually costs from the machine's point of view.
Overview Table
--------------
[width="100%",cols="12%,26%,40%,22%",options="header"]
|=========================================================================
| Figure | Item | Remarks | Felt as (1{nbsp}cycle{nbsp}≙{nbsp}1{nbsp}s)
4+^s| Nanoseconds -- inside the CPU
| 0.3{nbsp}ns | one clock cycle | at 3.3{nbsp}GHz; the machine's own time quantum | *1 second*
| ~1{nbsp}ns | L1 cache hit | about 4 cycles, fully pipelined | ≈{nbsp}3 seconds
| ~1.5{nbsp}ns | `sfence` | store fence, ≈{nbsp}5 cycles; rarely needed on x86 | ≈{nbsp}5 seconds
| ~2.7{nbsp}ns | `lfence` | ≈{nbsp}9 cycles; no ordering semantics on x86 beyond blocking speculation | ≈{nbsp}9 seconds
| ~4{nbsp}ns | L2 cache hit | ≈{nbsp}12--15 cycles | ≈{nbsp}13 seconds
| ~5{nbsp}ns | `lock add $0,(%rsp)` | the actual `smp_mb()` on Linux; ≈{nbsp}half the cost of `mfence` footnoteref:[lkml,LKML thread "[PATCH 3/4] x86,asm: Re-work smp_store_mb()", Nov 2015. Davidlohr Bueso measures XCHG against MFENCE on Ivy Bridge; Linus Torvalds reports that on Haswell "lock addq" costs pretty much exactly half of MFENCE, and explains that locked operations were the ones CPU designers had reason to optimise -- https://lkml.iu.edu/hypermail/linux/kernel/1511.0/01052.html] | ≈{nbsp}17 seconds
| ~5{nbsp}ns | branch misprediction | pipeline refill | ≈{nbsp}17 seconds
| ~10{nbsp}ns | `mfence` / full barrier | ≈{nbsp}23--33 cycles; comparable to a `lock`-prefixed RMW footnoteref:[downs,Travis Downs, "A Concurrency Cost Hierarchy", 6 July 2020, see the discussion of "vanilla" versus "non-vanilla" instructions: on x86 plain accesses and non-atomic read-modify-writes are cheap while LOCK-prefixed RMWs are an order of magnitude slower, with MFENCE comparable to a locked instruction -- https://travisdowns.github.io/blog/2020/07/06/concurrency-costs.html] | ≈{nbsp}33 seconds
| ~15{nbsp}ns | L3 cache hit | shared, hence variable | ≈{nbsp}50 seconds
| ~13{nbsp}ns | DRAM, open row | a plain load that happens to hit an already-activated row footnoteref:[cxl,Measured values in Table III of: Performance Characterizations and Usage Guidelines of Samsung CXL Memory Module Hybrid Prototype, arXiv:2503.22017. Local DDR5 median latency 122.9 ns under Intel MLC pointer chasing versus 13.1 ns for a plain load; methodology (rdtsc, 10 000 repetitions, median) described in section III-C -- https://arxiv.org/pdf/2503.22017] | ≈{nbsp}43 seconds
| ~14{nbsp}ns | DRAM, first access | device-level figure; essentially unchanged from DDR4 to DDR5 footnote:[Measurement-oriented analysis of DDR5 sub-channel architecture; the relevant passage is in section "Latency Implications", noting that both DDR4 and DDR5 sit at approximately 14 ns first-access latency at their respective standard speeds. arXiv:2605.08725 -- https://arxiv.org/pdf/2605.08725]
| ≈{nbsp}47 seconds
| 40--100{nbsp}ns | contended cache line | core-to-core transfer; strongly topology dependent (same CCX / cross CCX / cross socket) | 2--5 minutes
| ~100{nbsp}ns | DRAM random access | full miss through the whole hierarchy; 122.9{nbsp}ns measured by pointer chasing on DDR5 footnoteref:[cxl] | *≈{nbsp}5 minutes*
4+^s| Nanoseconds to microseconds -- entering the kernel
| ~25{nbsp}ns | syscall via vDSO | not a real trap; almost as cheap as a function call footnoteref:[arkanis,"Measurements of system call performance and overhead", Arkanis Development, 5 Jan 2017 -- compares a plain function call against getpid() issued via the syscall instruction and via the vDSO -- https://arkanis.de/weblog/2017-01-05-measurements-of-system-call-performance-and-overhead/] | ≈{nbsp}1.5 minutes
| 70--100{nbsp}ns | syscall trap, bare | `getpid()` on a mitigation-free host footnoteref:[gms,Georg Sauthoff, "On the Costs of Syscalls", 30 Aug 2021. Median of 100 repetitions across a heterogeneous set of hosts; the conclusion is that user-kernel mode switches cost on the order of a few hundred nanoseconds on all hosts, with the fastest host switching modes in under 100 ns, and that the higher figures are explained by CPU-bug mitigations being enabled by default -- https://gms.tf/on-the-costs-of-syscalls.html] | 4--6 minutes
| 200--600{nbsp}ns | syscall trap, mitigated | ``a few hundred nanoseconds'' on typical hosts with CPU-bug mitigations enabled footnoteref:[gms] | 11--33 minutes
| ~1{nbsp}µs | syscall doing real work | the round figure we use for the diagram | *≈{nbsp}1 hour*
| ~2{nbsp}µs | context switch | scheduler plus cold cache on the new task | ≈{nbsp}2 hours
4+^s| Microseconds -- the operating system
| 10--30{nbsp}µs | thread wake-up | IRQ handling, `try_to_wake_up()`, scheduling onto a CPU footnoteref:[lkmlwake,Michal Schmidt, reply in the thread "nanosleep() accuracy", linux-kernel mailing list, 17 Aug 2007: the point being that a process always carries non-zero wake-up latency covering interrupt processing, waking and scheduling, and that 10-30 us is not unreasonable -- https://lkml.iu.edu/hypermail/linux/kernel/0708.2/0423.html] | 9{nbsp}hours -- 1{nbsp}day
| ~31--57{nbsp}µs | scheduling latency, avg | `cyclictest` at `SCHED_FIFO` 95 with memory locked footnoteref:[rhel,Sample cyclictest output and interpretation in: Finding Realtime Linux Kernel Latencies, Clark Williams (Red Hat), section "Using cyclictest" -- https://people.redhat.com/williams/latency-howto/rt-latency-howto.txt] | 1--2 days
| ~59--90{nbsp}µs | scheduling latency, max | same run; the tail is what hurts footnoteref:[rhel] | 2--3.5 days
| *50{nbsp}µs* | *OS timer slack, default* | the kernel may delay a timed wake-up by this much, deliberately, to coalesce wakeups footnoteref:[slack,Primary documentation: `man 7 time`, section "High-resolution timers", and `man 2 prctl`, description of PR_SET_TIMERSLACK. The 50 us default for non-realtime threads is stated explicitly in: Metronome -- adaptive and precise intermittent packet retrieval in DPDK, section II (discussion of nanosleep limits), arXiv:2103.13263 -- https://arxiv.org/pdf/2103.13263 ; a practical encounter with the same default, including the /proc interface, is documented in OpenJDK issue JDK-8307766 -- https://bugs.openjdk.org/browse/JDK-8307766] | *≈{nbsp}2 days*
| 1{nbsp}ns | OS timer slack, minimum | via `prctl(PR_SET_TIMERSLACK)` or `/proc/PID/timerslack_ns` footnoteref:[slack] | ≈{nbsp}3 seconds
| ~9{nbsp}µs | SSD read, best case | Optane / Z-NAND class at QD1 footnoteref:[anand,AnandTech, review of the Samsung 983 ZET (Z-NAND), page 3, "QD1 Random Read Performance" -- explains why queue depth 1 exposes best-case access latency, and reports an average of just under 9 us for an Optane drive against 16 us for the Z-NAND based 983 ZET -- https://at-web1.www.anandtech.com/show/13951/the-samsung-983-zet-znand-ssd-review/3] | ≈{nbsp}8 hours
| 20--70{nbsp}µs | NVMe SSD 4K random read | typical contemporary drive footnoteref:[simply,Summary of the storage latency hierarchy: a modern NVMe SSD serves a random 4K read in roughly 20-70 us, a SATA SSD in 100-200 us, and a spinning disk in 5-10 ms. simplyblock glossary entry "NVMe Latency" -- https://simplyblock.io/glossary/nvme-latency/] | 1--3 days
| 50--100{nbsp}µs | NVMe SSD, interrupt path | what you see without polling | *2--4 days*
| 100--200{nbsp}µs | SATA SSD 4K random read | the interface costs, not just the medium footnoteref:[simply] | 4--8 days
| 100{nbsp}µs -- 1{nbsp}ms | deep C-state exit | wake from C6 and below; worse when many cores wake at once | 4{nbsp}days -- 5{nbsp}weeks
4+^s| Milliseconds -- where machine and human meet
| 5--10{nbsp}ms | rotating disk, seek + rotation | 7200{nbsp}rpm; the number that has not improved in decades footnoteref:[simply] | *6{nbsp}months -- 1{nbsp}year*
| ~10{nbsp}ms | barely noticeable latency | the threshold below which a human stops perceiving delay | ≈{nbsp}1 year
| 20{nbsp}ms | one frame @{nbsp}50{nbsp}fps | | ≈{nbsp}2 years
| 40{nbsp}ms | one frame @{nbsp}25{nbsp}fps | our actual deadline | *≈{nbsp}4 years*
| ~100{nbsp}ms | UI starts to feel sluggish | | ≈{nbsp}11 years
4+^s| Seconds and beyond -- the human
| ~1{nbsp}s | ``the software takes time to respond'' | acceptable, but noticed | *≈{nbsp}1 century*
| ~3{nbsp}min | ``time for another cup of coffee'' | | ≈{nbsp}19 000 years
| ~15{nbsp}min | ``can't wait, I'll do something else'' | ≙{nbsp}1000{nbsp}s, which conveniently closes the fourth decade block | ≈{nbsp}95 000 years
|=========================================================================
The Memory Wall
---------------
For roughly two decades, CPU clock rates have hovered around 3{nbsp}GHz while
DRAM access latency has stayed near 60--100{nbsp}ns. The consequence is
arithmetic: a cache miss that cost a handful of cycles on early-1990s hardware
now costs several hundred. The processor did not merely get faster than
memory -- it got faster than memory _by a growing margin_, and the entire
cache hierarchy exists to paper over that gap. Note in the table above that
the raw device-level latency of DDR5 is barely distinguishable from DDR4;
the generational improvements went into bandwidth, not into latency.
Once several cores share one memory subsystem, a single uniform path to memory
becomes the bottleneck. The answer was to move the memory controller onto the
CPU die and attach memory per socket -- and, with chiplet designs, effectively
per die group. This is what NUMA (non-uniform memory access) describes: memory
that ``belongs'' to the core you are running on is reached quickly, while
memory belonging to a neighbour costs noticeably more. The practical upshot is
that thread placement and memory placement are no longer independent concerns,
and that a scheduler which migrates a worker between cores may silently convert
local accesses into remote ones.
Cores also keep private caches, and a store performed on one core does not
become visible to another core at the moment the instruction retires: it first
lands in that core's store buffer. Cache coherence guarantees that the cores
eventually agree on the content of a cache line, but it says nothing about
_when_, nor about the order in which several independent writes become visible.
This is what memory fences are for -- they constrain the order, and thereby
allow one core to reason about what an adjacent core has already done. The cost
shows up in the table twice: an uncontended fence is roughly the price of an
L3 hit, whereas a cache line actually bouncing between two cores costs about as
much as going to DRAM. It is worth internalising that the expensive part is
usually not the synchronisation primitive itself, but the coherence traffic it
provokes.
x86 implements a comparatively strong model, _Total Store Order_ (TSO): of the
four possible reorderings, only StoreLoad is permitted. Acquire and release
semantics are therefore free on x86 -- they compile to plain `MOV` -- and only
sequential consistency requires an actual instruction. ARM and POWER are
weakly ordered and consequently offer a more differentiated set of barriers:
ARM distinguishes `DMB` from the heavier `DSB`, and provides graded load
acquire flavours such as `LDAR` and `LDAPR`; POWER separates the heavyweight
`sync` from the cheaper `lwsync`. The trap for the unwary is that C++
`memory_order` distinctions which cost nothing on x86 have a real price on
ARM -- and, worse, that under-synchronised code can appear perfectly correct
on x86 and fail on ARM.
Crossing into the Kernel
------------------------
A system call is not a function call: it is a controlled trap. The `syscall`
instruction switches the processor to privilege level 0, and the kernel entry
path then swaps to the kernel `GS` base, switches stacks, and saves the user
register state. Since the Meltdown/Spectre family of vulnerabilities, that
sequence also includes a page-table switch (KPTI writes `CR3`, with the
attendant TLB consequences) and one or more branch-prediction barriers --
retpolines, IBPB or IBRS depending on hardware and configuration. On return,
the whole dance runs in reverse.
The direct cost is measurable: a minimal syscall such as `getpid()` amounts to
little more than two mode switches, and lands in the region of a hundred
nanoseconds on a fast host without mitigations, rising to several hundred once
mitigations are enabled.footnoteref:[gms] The indirect cost is harder to see
and frequently larger. Kernel entry code and the syscall handler evict
user-space data from L1 and L2, and disturb the branch predictors; the
application resumes with a cold working set and pays for it in misses that no
syscall benchmark will attribute to the syscall. This is precisely why the
interesting question is rarely ``how expensive is one syscall'' but rather
``how many syscalls does this design require per unit of work'' -- which is
the question `io_uring` was built to answer.
Operating System Slack
----------------------
A thread that asks to sleep for a specific duration will not, in general, wake
at that instant. Part of this is unavoidable: an interrupt must be handled, the
sleeping task must be moved to the run queue, and the scheduler must actually
place it on a CPU, which together account for something in the range of
10--30{nbsp}µs.footnoteref:[lkmlwake]
The larger part, however, is deliberate. Linux maintains a per-thread _timer
slack_ value, and the kernel is free to delay a timed wake-up by up to that
amount in order to coalesce several wake-ups into one and thereby let the CPU
stay in a low-power state longer. For every thread not running under a
real-time scheduling policy, the default is 50{nbsp}µs.footnoteref:[slack]
This is not imprecision; it is a power-versus-precision trade that the kernel
makes on the application's behalf, and it can be adjusted -- down to
1{nbsp}ns -- via `prctl(PR_SET_TIMERSLACK)` or the per-process
`/proc/PID/timerslack_ns` interface.
The distinction worth carrying away is between _resolution_ and _accuracy_.
Since Linux 2.6.21 the high-resolution timer subsystem reports nanosecond
resolution, and `clock_getres()` will happily confirm it. The accuracy with
which a `nanosleep()` actually returns is a different quantity altogether, two
to three orders of magnitude coarser, and dominated by slack, wake-up latency
and -- if the core had gone idle -- C-state exit time. Any design that assumes
it can steer a worker thread with microsecond-scale sleeps should be checked
against these figures first.
// ----------------------------------------------------------------------
// Footnote definitions are attached at first use above via footnoteref.
// Repeated references reuse the id; see AsciiDoc footnoteref macro.
// ----------------------------------------------------------------------
Sources
-------
The footnotes above carry the precise locations. Collected here as a reading
list, roughly in order of usefulness for further work.
- Travis Downs, *A Concurrency Cost Hierarchy* --
https://travisdowns.github.io/blog/2020/07/06/concurrency-costs.html +
The best single entry point on why synchronisation costs what it costs.
- LKML thread, *[PATCH 3/4] x86,asm: Re-work smp_store_mb()* (Nov 2015) --
https://lkml.iu.edu/hypermail/linux/kernel/1511.0/01052.html +
Kernel developers measuring `MFENCE` against `XCHG` and `lock addq`.
- Georg Sauthoff, *On the Costs of Syscalls* (Aug 2021) --
https://gms.tf/on-the-costs-of-syscalls.html +
Careful cross-host microbenchmark of the mode-switch cost.
- *Measurements of system call performance and overhead* (Jan 2017) --
https://arkanis.de/weblog/2017-01-05-measurements-of-system-call-performance-and-overhead/ +
Function call versus `syscall` instruction versus vDSO.
- Brendan Gregg, *KPTI/KAISER Meltdown Initial Performance Regressions* --
https://www.brendangregg.com/blog/2018-02-09/kpti-kaiser-meltdown-performance.html +
Relates mitigation overhead to syscall rate, context switch rate and working
set size -- the systemic view rather than the per-call view.
- Clark Williams (Red Hat), *Finding Realtime Linux Kernel Latencies* --
https://people.redhat.com/williams/latency-howto/rt-latency-howto.txt +
Walks through a `cyclictest` run and how to read its output.
- `man 7 time` and `man 2 prctl` (PR_SET_TIMERSLACK) +
Primary documentation for timer slack. The 50{nbsp}µs default is stated in
https://arxiv.org/pdf/2103.13263 (Metronome, DPDK packet retrieval) and
encountered in practice in https://bugs.openjdk.org/browse/JDK-8307766
- *Performance Characterizations ... Samsung CXL Memory Module Hybrid
Prototype*, arXiv:2503.22017 -- https://arxiv.org/pdf/2503.22017 +
Table III contains a well-documented measurement of plain local DDR5.
- *Single 32-bit Sub-Channel DDR5 DIMMs*, arXiv:2605.08725 --
https://arxiv.org/pdf/2605.08725 +
Useful for the point that DDR5 did not improve absolute latency over DDR4.
- simplyblock glossary, *NVMe Latency* --
https://simplyblock.io/glossary/nvme-latency/ +
Compact summary of the NVMe / SATA SSD / rotating disk hierarchy.
- AnandTech, *Samsung 983 ZET (Z-NAND) review*, page 3 --
https://at-web1.www.anandtech.com/show/13951/the-samsung-983-zet-znand-ssd-review/3 +
Explains why queue depth 1 exposes best-case access latency.
A Note on Microbenchmarks
-------------------------
One caveat deserves to be stated separately, because it explains why published
figures for synchronisation primitives vary so wildly. A hot microbenchmark
keeps every thread runnable and every core out of idle by construction. Under
those conditions an uncontended atomic operation or a mutex acquisition costs
single-digit to low-three-digit nanoseconds, and a benchmark will faithfully
report that. What such a benchmark structurally cannot observe is the cost of
_blocking_ -- of a thread actually parking and later being woken -- because
pressing the benchmark is precisely what prevents that from happening.
The instruction-level cost of a synchronisation primitive is what a hot
microbenchmark measures. Its systemic cost -- coherence traffic under
contention, and blocking and wake-up latency when it is not contended -- is
what production tail latency measures. The two can differ by two to four orders
of magnitude, and the table above spans both regimes for exactly that reason.

View file

@ -0,0 +1,244 @@
/*
* core2core.cpp — measure core-to-core latency by cache-line ping-pong.
*
* Code generated 8/2026 by Claude -- prompted by Ichthyostega
* Context: FrOSCon-26 talk on IO_URING
*
* Two threads are pinned to two specific logical CPUs and share a single
* cache line holding two atomic counters. Thread A stores i, then spins
* until thread B has answered with i; thread B does the mirror image.
* Each round trip therefore forces the line to migrate twice: A -> B and
* back. The one-way latency reported is half the measured round trip.
*
* What this actually measures is the cost of a *contended* cache line —
* the coherence traffic, not the atomic instruction. An uncontended
* atomic on a line already held in M state costs a couple of nanoseconds;
* the numbers here are one to two orders of magnitude larger, and that
* difference is the whole point.
*
* build: g++ -O2 -std=c++17 -pthread -o core2core core2core.cpp
* run: ./core2core # full matrix over all allowed CPUs
* ./core2core 3 9 # just the pair (3,9)
*
* Linux only (uses pthread_setaffinity_np / sched_getaffinity).
*
* Reading the results
* -------------------
* * SMT siblings of one physical core: lowest of all, often 5–20 ns.
* They share L1/L2, so almost nothing migrates. Cross-check which
* CPUs are siblings with lscpu -e or lscpu -p=CPU,CORE,SOCKET.
* * Cores on the same die / CCX / cluster: typically 25–50 ns.
* * Across an AMD CCX or CCD boundary, or across sockets: 70–150 ns
* and upwards. On a multi-socket machine the cross-socket figure
* can exceed local DRAM latency.
*
* Getting numbers you can trust
* -----------------------------
* * Set the CPU governor to performance; on an idle laptop the cores
* will otherwise be clocked down and the figures inflate.
* sudo cpupower frequency-set -g performance
* * Run on an otherwise idle machine. The tool reports the minimum
* over several repetitions, which filters most interference, but it
* cannot filter a busy system.
* * Do not read more than two significant digits into the result.
*/
#include <atomic>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <limits>
#include <string>
#include <thread>
#include <vector>
#include <pthread.h>
#include <sched.h>
namespace {
constexpr std::size_t CACHELINE = 64; // deliberately hard-coded; see note below
/** the two counters live in one cache line, so a store by either side
* invalidates the other side's copy. `alignas` keeps neighbouring data
* out of that line, so we measure our own ping-pong and nothing else. */
struct alignas(CACHELINE) PingPong
{
std::atomic<std::uint64_t> a{0};
std::atomic<std::uint64_t> b{0};
char pad[CACHELINE - 2 * sizeof(std::atomic<std::uint64_t>)]{};
};
static_assert (sizeof(PingPong) == CACHELINE, "PingPong must occupy exactly one line");
bool pin_to (int cpu)
{
cpu_set_t set;
CPU_ZERO (&set);
CPU_SET (cpu, &set);
return 0 == pthread_setaffinity_np (pthread_self(), sizeof(set), &set);
}
std::vector<int> available_cpus()
{
std::vector<int> cpus;
cpu_set_t set;
CPU_ZERO (&set);
if (0 == sched_getaffinity (0, sizeof(set), &set))
for (int i = 0; i < CPU_SETSIZE; ++i)
if (CPU_ISSET (i, &set))
cpus.push_back (i);
return cpus;
}
/** one ping-pong run between two pinned threads.
* @return round-trip time per iteration in nanoseconds, or -1 on failure */
double one_run (PingPong& pp, int cpuA, int cpuB,
std::uint64_t warmup, std::uint64_t iters)
{
using Clock = std::chrono::steady_clock;
pp.a.store (0, std::memory_order_relaxed);
pp.b.store (0, std::memory_order_relaxed);
std::atomic<int> ready {0};
std::atomic<bool> go {false};
std::atomic<bool> ok {true};
const std::uint64_t total = warmup + iters;
std::thread responder{[&]
{
if (not pin_to (cpuB)) { ok.store (false); }
ready.fetch_add (1, std::memory_order_release);
while (not go.load (std::memory_order_acquire))
;
for (std::uint64_t i = 1; i <= total; ++i)
{
while (pp.a.load (std::memory_order_acquire) != i)
;
pp.b.store (i, std::memory_order_release);
}
}};
Clock::time_point t0, t1;
std::thread initiator{[&]
{
if (not pin_to (cpuA)) { ok.store (false); }
ready.fetch_add (1, std::memory_order_release);
while (not go.load (std::memory_order_acquire))
;
for (std::uint64_t i = 1; i <= total; ++i)
{
if (i == warmup + 1) // caches and predictors are warm now
t0 = Clock::now();
pp.a.store (i, std::memory_order_release);
while (pp.b.load (std::memory_order_acquire) != i)
;
}
t1 = Clock::now();
}};
while (ready.load (std::memory_order_acquire) < 2)
;
go.store (true, std::memory_order_release);
initiator.join();
responder.join();
if (not ok.load())
return -1.0;
auto elapsed = std::chrono::duration_cast<std::chrono::nanoseconds> (t1 - t0);
return double(elapsed.count()) / double(iters);
}
/** @return best (minimum) one-way latency in ns over @p reps repetitions */
double measure_pair (int cpuA, int cpuB, int reps,
std::uint64_t warmup, std::uint64_t iters)
{
PingPong pp;
double best = std::numeric_limits<double>::infinity();
for (int r = 0; r < reps; ++r)
{
double rtt = one_run (pp, cpuA, cpuB, warmup, iters);
if (rtt < 0) return -1.0;
if (rtt < best) best = rtt;
}
return best / 2.0; // round trip covers two line migrations
}
} // namespace (anon)
int main (int argc, char** argv)
{
constexpr int REPS = 5;
constexpr std::uint64_t WARMUP = 2000;
constexpr std::uint64_t ITERS = 20000;
if (argc == 3)
{ // single pair
int a = std::atoi (argv[1]);
int b = std::atoi (argv[2]);
if (a == b)
{
std::fprintf (stderr, "the two CPUs must differ "
"(both threads spin, they cannot share one core)\n");
return 1;
}
double ns = measure_pair (a, b, REPS, WARMUP, ITERS);
if (ns < 0)
{
std::fprintf (stderr, "could not pin to CPU %d / %d\n", a, b);
return 1;
}
std::printf ("CPU %d <-> CPU %d : %.1f ns one-way (%.1f ns round trip)\n",
a, b, ns, 2 * ns);
return 0;
}
auto cpus = available_cpus();
if (cpus.size() < 2)
{
std::fprintf (stderr,
"need at least two available CPUs, found %zu.\n"
"(inside a restricted container, check the cpuset)\n",
cpus.size());
return 1;
}
std::printf ("core-to-core latency, one-way, nanoseconds\n");
std::printf ("minimum of %d runs x %llu iterations; %zu CPUs available\n\n",
REPS, (unsigned long long)ITERS, cpus.size());
// header
std::printf (" ");
for (std::size_t j = 0; j + 1 < cpus.size(); ++j)
std::printf ("%6d", cpus[j]);
std::printf ("\n");
for (std::size_t i = 1; i < cpus.size(); ++i)
{
std::printf ("%5d ", cpus[i]);
for (std::size_t j = 0; j < i; ++j)
{
double ns = measure_pair (cpus[i], cpus[j], REPS, WARMUP, ITERS);
if (ns < 0) std::printf (" -");
else std::printf ("%6.0f", ns);
std::fflush (stdout);
}
std::printf ("\n");
}
std::printf ("\nlowest values are usually SMT siblings; check with lscpu -e\n");
return 0;
}