It’s partly by design—the word senate shares its root with the word senex “old man” (whence also senile, etc.) because it’s literally a council of elders. That’s what the Roman one was and what the US constitution (among others) was attempting to emulate.
Partly, though, it’s a bit more interesting than that[1]. Wikipedia has an interesting chart[2] titled “Lifespan of each United States president”, which shows that in modern-ish times, the generation from which US presidents come is sticky: you get three presidents born ca. 1855, then one born ca. 1865, two ca. 1875, three from the late 1880s, four(!) from the 1910s (starting with “young” JFK), Carter/Reagan/Bush Sr. oscillate between the 1910s and 1920s, then again four from the 1940s (starting with “young” Clinton) only interrupted by “young” Obama (born 1961). (I could start my recounting earlier and it would still work.) Trump and Biden appear abnormal if you look at their ages, but if you look at their birth years instead they are simply a backslide, after Obama, to the generation of Clinton and Bush Jr.
What does appear true is that generations hold onto their power for longer and longer before the snap transition to the next eligible generation happens. Longer life expectancy due to better medical care seems like a perfectly satisfactory explanation for that.
[1] I first read about this idea a few years ago and I don’t remember where, so I’ll need to attempt my own explanation. Sorry.
An ASCII-friendly way to spell ℞, an R with a crossed right leg, shorthand for the Latin singular imperative recipe! “take (the following)!”, which is the conventional start of a prescription; by extension, something necessitating a prescription to buy.
Depending on the effort you’re willing to put in, you don’t have to: gzip has a specific provision for concatenated files and tar is a flat array of (file) records each followed by file data, terminated by a sentinel record, so what you need to do is to trim the sentinel off the first tar.gz and then append the second one as is. I don’t know of a ready-made tool that would be able to chop off a gzip file’s last 512 uncompressed bytes without recompressing it in full, but it ought to be possible.
> Rust's critical invention was "what if we modeled heap ownership and borrowing with linear types".
Rust did not invent this (and I don’t believe Rust’s creators claim it did), see e.g. “Linear regions are all you need”[1] (ESOP 2006) and in general the work around Cyclone[2] in the 2000s. There was also Mezzo[3], which did very similar things but described them in a different style (effects instead of affine types), and a fair bit of work that went beyond what Rust can currently express (e.g. fractional capabilities). What Rust did is succeed at bringing a conservative subset of this academic work to production—which is not a small deal, mind you; others (including the Cyclone team) tried and failed.
None of the sequelae of that have really improved since then, at least as far as the first world is concerned. The foundations of universal surveillance were being laid down, but it wasn’t yet feasible. Police (transport security, border patrol, potato, pot-ah-to) ruffling through your belongings had not yet been normalized. You could transact with cash. People largely still sort of believed that talking to your political opponents would lead to better outcomes than making them comply under threat of government violence or unemployment.
(Plenty of things sucked, don’t get me wrong, it’s just that a lot of the fallout from the “war on terror” was still in the upper atmosphere.)
Judging by the lowercase “m” it’s the 8x16 variation[1] and not the 9x16 one you linked, but otherwise yes, looks like the plain old VGA font? (Incorrectly displayed, as the site points out: the VGA didn’t have square pixels in either of those modes.) The site uses (AVIF) images in place of text, but it’s like 15 minutes to write a program that typesets an ASCII file in a given monospace bitmap font and writes out the result as say a PPM (and I’m sure ImageMagick can do it if you’d rather dig through docs than program), so this doesn’t seem to require any further explanation?
> I/O is inherently very slow compared to computation.
Not anymore, no. Your SSD, before any caching, does gigabytes per second of sequential reads. For any bytewise processing, except the most trivial of tasks, you’ll struggle to get above a few hundred megabytes per second with scalar (native) code. To actually keep up with a modern SSD, you’ll virtually always have to hand-write SIMD loops, minimize the number of syscalls with tools like io_uring, or possibly be smart about distributing tasks across cores without ruining the access pattern.
For instance, simdjson is famously fast but I don’t believe it can keep up with say a high-end PCIe Gen 4 SSD like a Samsung 990 PRO, let alone the latest-and-greatest (and, literally, hottest) Gen 5 stuff. And I know of no Unicode normalizer that would be able to do a gigabyte per second on general inputs (not ASCII, not Latin-1) simply because the latency for dependent lookup table accesses is absolute murder.
Your analysis is correct if and only if the data is on the same machine as the calculations. If the data comes from another machine, it comes at network speed. If it comes from the internet, it comes at non-local network speed. That's very different from SSD speed.
No, you're comparing apples and oranges. All an SSD sequential read is doing is copying data from one place to another. So you should be comparing SSD bandwidth to memory bandwidth, not SSD bandwidth to (time it takes to execute some arbitrary algorithm). Or you should be comparing SSD bandwidth when performing millions of tiny random non-sequential reads and writes, to the algorithm time.
What your comment demonstrates is that it is possible in some cases for I/O to be fast enough to not be a performance bottleneck for certain kinds of programs. But not that I/O is not slow.
Muratori et al. like comparing speeds to (single-core) memory bandwidth (dozens of GB/s) and that’s a reasonable upper bound, but generally it seems to me that, unless you operate on huge elements and don’t do very much with them, you won’t get within an order of magnitude of it. Even if you think about RAM exclusively, the headline numbers are for sequential reads and things will slow down dramatically if you actually perform random accesses (IIRC, DDR5 is about as slow as DDR4 there in terms of physical time units, so much slower in terms of bus cycles). Meanwhile, in a real situation, you’re going to be bound by compute long before that.
And I think you’re being unfair labelling my couple of examples “some arbitrary algorithm[s]”: my choice was indeed arbitrary, but it’s also immaterial. The general setup would be that you’re processing elements in a loop and that your iterations are serialized (as they usually more or less are before you get around to optimization). A loop body of even three lines of C is likely to have a latency of 5–10 cycles or so, and you’re running on a core clocked somewhere from 5 GHz (desktop) to half that (server). So the best you should expect is ~500 MB/s if your elements are bytes, ~2 GB/s if they’re 32-bit integers, etc. For very simple tasks (that are also somehow not susceptible to vectorization), it is possible to not lose this order of magnitude and get down to almost 1 cycle/element in scalar code, but that requires heroic effort[1].
> Don’t think TeX ever did that, that was a “simple” Pascal program.
Not exactly the entire process image, no, but essentially all of its data in a single chunk. It’s a peculiar Pascal program because it bypasses basically all of Pascal’s typing, records, etc., and instead builds its own from a set of (WEB) macros on top of a giant untyped array. Quoth Knuth in TeX: The Program §115:
> The dynamic storage requirements of TeX are handled by providing a large array mem in which consecutive blocks of words are used as nodes by the TeX routines. Pointer variables are indices into this array [...].
There are a few more areas designated for specific purposes, but at the end of the day (§1302) it works out about the way you’d expect:
procedure store_fmt_file;
[...] begin ⟨ If dumping is not allowed, abort 1304 ⟩
⟨ Create the format ident, open the format file, and inform the user that dumping has begun 1328 ⟩;
⟨ Dump constants for consistency check 1307 ⟩;
⟨ Dump the string pool 1309 ⟩;
⟨ Dump the dynamic memory 1311 ⟩;
⟨ Dump the table of equivalents 1313 ⟩;
⟨ Dump the font information 1320 ⟩;
⟨ Dump the hyphenation tables 1324 ⟩;
⟨ Dump a couple more things and the closing check word 1326 ⟩;
⟨ Close the format file 1329 ⟩;
end;
Partly, though, it’s a bit more interesting than that[1]. Wikipedia has an interesting chart[2] titled “Lifespan of each United States president”, which shows that in modern-ish times, the generation from which US presidents come is sticky: you get three presidents born ca. 1855, then one born ca. 1865, two ca. 1875, three from the late 1880s, four(!) from the 1910s (starting with “young” JFK), Carter/Reagan/Bush Sr. oscillate between the 1910s and 1920s, then again four from the 1940s (starting with “young” Clinton) only interrupted by “young” Obama (born 1961). (I could start my recounting earlier and it would still work.) Trump and Biden appear abnormal if you look at their ages, but if you look at their birth years instead they are simply a backslide, after Obama, to the generation of Clinton and Bush Jr.
What does appear true is that generations hold onto their power for longer and longer before the snap transition to the next eligible generation happens. Longer life expectancy due to better medical care seems like a perfectly satisfactory explanation for that.
[1] I first read about this idea a few years ago and I don’t remember where, so I’ll need to attempt my own explanation. Sorry.
[2] https://en.wikipedia.org/wiki/List_of_presidents_of_the_Unit...
reply