I haven't benchmarked it myself. It would be interesting to see how the most common cores today do on this...
1,502 karma · joined August 27, 2012
Currently: Senior Architect at F5, working on Cranelift/Wasmtime in the Office of the CTO.
Previously: Principal software engineer at Fastly, working on Cranelift/Wasmtime for the Wasm-based Compute@Edge project, 2020-2024. Mozilla, working on the SpiderMonkey and Cranelift JIT compilers in Firefox, 2019-2020; PhD student (ECE) and then postdoc (CS) at Carnegie Mellon working on compilers, static and dynamic analysis (2009-2013, 2015-2019).
More previously: Google (2014-2015), and Intel (2013-2014).
Web: https://cfallin.org/
[ my public key: https://keybase.io/cfallin; my proof: https://keybase.io/cfallin/sigs/ZJj2pg69eB6Hs0cl9j_babYkyADojbWrfe4uZp11LMo ]
I haven't benchmarked it myself. It would be interesting to see how the most common cores today do on this...
... are actually preferred over a hand-written vectorized loop on Ivy Bridge and up (see [1] section 3.7.7, "Enhanced REP MOVSB and STOSB operation (ERMSB)"). It's indicated by a CPUID feature flag bit (edit: grep for "erms" in /proc/cpuinfo to see this).
The reason is that microcode knows more about the dcache microachitecture, load/store units, special features (weak ordering with fence at end? [2]), etc., than you do, and can be specially optimized for the particular design. There's a slight cost to transitioning to microcode and back (to ordinary hardware-decoded instructions), of course, so for small operations it might not be a win.
The section cited above shows ERMSB as ~break-even vs. 128 bit AVX on Ivy Bridge from 128 bytes up to 2KB, and about 2% faster above that.
[1] Intel 64 and IA-32 Architectures Optimization Reference Manual (order #248966-026), April 2012. http://www.intel.com/content/dam/doc/manual/64-ia-32-archite...
[2] http://stackoverflow.com/questions/33480999/how-can-the-rep-...
But that's just a guess and I can't seem to find anything in a quick search... anyone know more about this?
Also, some states treat graduate stipends specially. In Pennsylvania, the stipend is exempt from state and local income tax.
Core language features: both have algebraic datatypes like ML ("case classes" in Scala, enums in Rust), and a 'match' statement that makes working with them easy. Both have pervasive destructuring that works with match arms, 'let' bindings, etc.
Data structures and mutation: both encourage a pragmatic version of immutability -- use values and provide functional interfaces primarily, but support mutation where needed. In Rust there are 'Cell' and 'RefCell' types, just like ML's cell-based interior mutability.
Library idioms: all three have the standard set of functional-style higher-order transformation functions ('map', 'filter', etc. -- Rust in particular has a very rich Iterator trait API), and it's idiomatic to use these.
FWIW, Rust's first/bootstrap compiler was written in OCaml and there's a lot of obvious influence.
Anyway, I really appreciate the simplicity of rc.conf and the well-curated config/boot infrastructure in general; pkg(ng) just works; ZFS is insanely cool; LLVM-as-system-compiler is nice... I sometimes wonder where we'd be if the BSD lawsuit hadn't held things back 20-ish years ago.
Pittsburgh's bus system is definitely not the most timely or reliable of bus systems I've used, but I have the opposite experience here: parking is a huge pain, drivers can be insane, and I'd much rather sit on the bus reading Hacker News on my phone (or whatever). Maybe not for the occasional Costco trip but definitely anywhere in the East End.
Granted, (i) I also have an unlimited bus pass via CMU (sunk costs work both ways!), and (ii) I'm close to several frequent routes (in Squirrel Hill). If I had to pay each time and buses were less frequent I'd probably be thinking like you are.
In any case, OP's two-hour number seems pretty off-putting. Agreed that time is an important consideration at that scale :-)
There are also practical issues with increasing the architectural register set -- you'd have to define a new encoding for instructions, and this (i) adds significant cost (area, power, timing) to the instruction decoding logic, and (ii) imposes a giant cost on the software ecosystem -- new binaries, operating system support for context-switching, etc.
Not even that! It loads the first 512-byte sector of your boot disk and jumps to it in 16-bit mode. Usually that bit of code loads in a few more sectors (using BIOS disk IO calls), enough to load a real bootloader's image (like GRUB), which subsequently understands your filesystem and loads the real kernel.
It's kind of a hack but I completely agree, much better to leave it in the hands of the systems developer -- firmware that tries to do too much often just gets in the way.
The two are actually orthogonal -- generators are a control-flow structure where control keeps re-entering an existing function context and returning from it (basically, a coroutine), whereas lazy evaluation is a language-semantics choice where every value starts out as a thunk (bit of code) with enough information to compute the value, until resolved to the result.
Generators in the Python or JavaScript sense aren't really doing "lazy evaluation" in the Haskell sense, IMHO, though I can see how one sees the parallels (infinite sequences, etc). You can have the same "infinite sequence" by just defining an iterator implementation that always returns a next value, without the use of generators/coroutines.
Likewise, there are other uses for lazy evaluation than generator-like infinite sequences. For example, lazy evaluation lets you build a (finite) table of values in a memoized dynamic-programming problem, where each value is an expression that refers to some of the other values, and the lazy evaluation semantics ensure that (i) only the needed values are computed and (ii) once a value is computed, it's memoized (the thunk resolves to its result). You get demand-based computation and memoization "for free" from the language runtime.
E.g. -- a "differential discrimination" test of sorts: altering any one variable of a "protected class" such as race, gender, religion, etc. does not change the answer. You would maybe want to pick a set of canonical test profiles among real people who differ only on one axis (as closely as possible), rather than just take a test point and alter one axis directly, because you'd want all the relevant correlations (ZIP code vs wealth, etc) to remain authentic. The end result would be a set of "equivalence classes": sets of human profiles who must be considered equivalent on all relevant life-altering judgments.
Or perhaps a "unit test"-like approach: similar to how one creates a unit test for each bug one fixes, create a "criminal justice ML algorithm test suite" with canonical profiles and their results: you must judge this person to likely not re-offend, you must judge that person as a high risk, you must judge this person worthy of a home loan for $X, etc. Sort of like a body of case law. I guess the risk is overfitting -- so maybe this data set is held in trust by some regulatory agency and not revealed.
People have probably thought about this and I haven't read your links -- is building a test data set and building regulations around it something that's considered?
In other words, the general optimization is to convert
bool A = condition();
if (!A) { f(A); }
to: bool A = condition();
if (!A) { f(false); }
i.e., that we can assume an if-condition is true within the body of that if-statement. The problem is that the check-widening reuses the body of the if-statement: the code bool A = condition();
if (!A) { f(A); }
bool B = condition();
if (!B) { f(A); }
is converted by check-widening to the first snippet above, which then becomes the second. Sanjoy's observation is that guard-widening is almost correct (we still want to pursue this route) because `f` (the deoptimization escape) is really what we want to call in both cases, but we just need to get the value of `A` right by somehow letting the optimizer know that we're reusing the if-body and that it can't assume anything about the condition that got us there.Also, this is really really clever and I enjoyed the post, OP!
It seems that Gluon has taken inspiration from Haskell right down to the monadic solution to mutable state: https://github.com/Marwes/gluon/blob/master/std/state.glu
I wasn't able to find any 'cell' type or mutable records like in OCaml, so this does seem to be a pure language -- is that right? I wonder how that works in practice for small embedded scripts/logic -- my intuition is that the discipline enforced by monadic types and purity is really great at large scale but can be frustrating when you just want to hack something together.
Anyway, it would be interesting to see typical use-cases for Gluon!
If instead of two brothers marrying two sisters, you have two brothers marrying two first cousins, like so...
b1 and b2 are brothers
c1 and c2 are first cousins
b1 and c1 marry and have child x
b2 and c2 marry and have child y
... then x and y are both first and second cousins (and their children are second + third cousins, etc).(All that said, seeing a real chip at the end of a project must be a heck of an exhilarating feeling of success...)
This shortcut is possible because of a common technique of splitting functional and timing details apart: the functional emulator simply runs the instructions in a big interpreter loop and tracks the machine state as, say, QEMU or Bochs would, while the timing model is just cycle accounting given the instruction stream. In contrast, when you build a model in RTL, you're actually doing all the work that industry microarchitects do: you need to get right all the details of (say) speculative execution, or cache tag matching, or whatever, because your microarchitecture is implementing the code execution directly. That's a lot harder to do!
People do sometimes write RTL for their proposed microarchitectures, but that's usually done for power or timing (clock speed / critical path) results. And they usually model just whatever new thing (prediction table, synchronization widget, cache eviction logic) they propose, rather than the whole chip.
This is fairly standard for computer architecture research in academia: the results are based on a cycle-accurate model that simulates what the machine would do when running particular code. The field has a standard set of benchmarks (e.g. SPEC CPU2006 for serial code, and a bunch of different ones for parallel code) that are run on these simulators and are well-understood. The same is true for architecture teams in industry: they start with simulators and (a lot of) benchmarks before any silicon ever exists.
The reason for all of that is that actually fabbing a chip is prohibitively expensive and insanely work-intensive. A real chip has probably millions of lines of Verilog RTL, and a lot of custom layout to get anything reasonably performant (then > $1M for a mask set, multiple weeks or months to get the chips back from the fab, etc). In contrast, a good simulation model, worthy of publishable results, of an SoC with out-of-order cores, a cache hierarchy, and DRAM, is somewhere between 10K and 100K LoC of C++; the model for a new proposed feature is maybe a few KLoC of code on top of that. Once the simulator exists, a grad student can try out new features fairly easily. It's also much more analyzable and instrumentable: a chip is mostly a black box, modulo whatever debugging features you build in, while a simulator can easily dump a "pipe trace" of the pipeline state every cycle. The field has invented lots of different tools and ways of visualizing data to get a sense of what goes on inside the machine and what bottlenecks exist.
So basically, it's a software model, and the software model is much more informative, and easier to tweak and iterate on, than silicon while being "good enough" for trustworthy results.
IMHO a Rust-style type `Option<HashMap<String, Vec<u32>>>` is a little noiser than the Haskell-style type `Maybe (Map String [Int])`, but not fatally so. Maybe there are much worse cases though?
https://github.com/dspinellis/unix-history-make (a repo with the scripts to build the final repo)
https://github.com/dspinellis/unix-history-repo (the final repo -- check out all of the different branches/tags)
I'm also curious -- but I wonder, would this allow for imaging anything that a scanning electron microscope can't?