HNHacker News
TopNewBestAskShowJobs

cfallin

1,502 karma · joined August 27, 2012

Compiler nerd, computer scientist, software engineer. Living and working in Sunnyvale, California.

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 ]

submissionscomments
cfallin··on Does a compiler use all x86 instructions? (2010)
I was just quoting the cited PDF ("the section cited above shows..."), which the author(s) did on Ivy Bridge.

I haven't benchmarked it myself. It would be interesting to see how the most common cores today do on this...

cfallin··on Does a compiler use all x86 instructions? (2010)
> the rep prefixed instructions for string operations

... 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-...

cfallin··on 4 a.m. Is the Most Productive Hour
I wonder if there's some control-theory reason why one's unsynchronized circadian rhythm is on average slightly longer than a day? Intuitively it seems that one might actually have more consistent synchronization with the solar cycle if upward pressure from a natural setpoint is balanced by downward pressure from end-of-day environmental signals (light levels, etc). Or in other words, the body has evolved a slight margin to allow for adjustability "in the field" via feedback loops.

But that's just a guess and I can't seem to find anything in a quick search... anyone know more about this?

cfallin··on Why Do We Judge Parents for Putting Kids at Perceived But Unreal Risk?
Either that, or lead solder fumes (ah, memories), have to explain at least some fraction of EE/computer folks' quirks...
cfallin··on NLRB rules graduate students are employees
At least on the NSF GRFP, and at least in my department at CMU, I still received W-2s, and federal deductions happened normally as per my W-4. (The fellowship funding went through the dept and I was paid by CMU.) Maybe my department is an outlier, though shrug

Also, some states treat graduate stipends specially. In Pennsylvania, the stipend is exempt from state and local income tax.

cfallin··on Writing a JPEG Decoder in Rust – Part 2: Implementation I
Not at all -- there's a lot of commonality in the language designs.

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.

cfallin··on Uber’s First Self-Driving Fleet Arrives in Pittsburgh This Month
Not just topology, but visibility too... IMHO at least, Pittsburgh streets in the denser neighborhoods (east end mainly) have a real problem with blind intersections and driveways, especially because of street parking. If the car's sensor system handles this well then that's really impressive.
cfallin··on FreeBSD 11.0-RC1 Now Available
It's good to see the DRM (graphics) stuff more up-to-date in 11.0. FreeBSD is frustratingly close to natively-just-works on my 2014 Asus Haswell ultrabook (support for scrolling with the touchpad, of all things, is the main thing missing; also volume/screen hotkeys). I hope the laptop hardware support keeps improving!

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.

cfallin··on A Denver Suburb Experiments with Free Lyft Rides to Light Rail
> I can get a bus to downtown Pittsburgh a block away from my house, but I'll almost always drive

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 :-)

cfallin··on Shape of errors to come
I've heard that technique described as "type tetris". I find myself trusting the type system to guide me a lot in Rust too -- it's very nice!
cfallin··on Shape of errors to come
I really appreciate the user-friendliness of Rust's error messages -- I can't remember seeing a compiler tell me "maybe try this instead?" before (perhaps something from Clang, but never with the specificity of, e.g., a suggested lifetime annotation). And from a parsing / compiler-hacking perspective, it seems really hard to get the heuristics good enough to produce the "right" root cause. Kudos to the Rust team for this continued focus!
cfallin··on Why do CPUs have multiple cache levels?
To expand on another answer here -- there are more values in the PRF than there are names because "old" instructions in flight can refer to outdated versions of architectural registers (the names). But a new instruction can only see the latest versions of the architectural registers, so only those 16 actually matter w.r.t. future execution: when we switch back to the task, its new instructions will ony need the 16 live values.
cfallin··on Why do CPUs have multiple cache levels?
Yep, the long bitlines are a large factor in timing for register files. The timing is something like O(n) for n storage lines because it grows linearly with capacitance.
cfallin··on Why do CPUs have multiple cache levels?
It's a quantitative tradeoff question -- for many programs it may be true at first that increasing the register count (say, from 16 to 32) allows more live values in-register and reduces memory traffic, but at a certain point there are diminishing returns, because the extra access time eventually becomes another cycle of latency and this hits all programs. Notable as well is that for many programs (and especially modern ones with large data sets), most cache misses occur because of accesses to large heap data structures, not because there were slightly too few registers and a few locals spilled to the stack.

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.

cfallin··on A x64 OS #1: UEFI
> The bios loads your kernel

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.

cfallin··on Functional Programming Jargon
> Lazy evaluation should be called a generator if we are doing JS specific naming conventions.

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.

cfallin··on The bicycle is making a comeback in US cities
Anecdotally, I hear hills cited pretty often as a reason not to bike-commute. I live in Pittsburgh and the city is taking biking more and more seriously, but there's only so much one can do about topography. An electric boost could make a big difference for some. (Then again, if you live on the hill and work in the valley, the morning commute is pretty fun!)
cfallin··on Training Computers to Find Future Criminals
Given that ML is likely going to be used more and more -- even if not for criminal justice applications, at least in the private sector (credit scores/loan applications, insurance, etc) -- I wonder if there's a way to develop standardized "anti-discrimination standards" and then apply some regulation to any ML algorithm that makes a "life-altering decision" by some definition?

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?

cfallin··on Check Widening in LLVM
The optimizer recognizes that the variable `condition` and the condition with the guard's if-statement are the same (edit: inverses actually), so within the if-statement's body, it can assume that `condition` is false. But when other guard cases are merged in, that's no longer the case.

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!

cfallin··on Gluon: A static, type inferred and embeddable language written in Rust
Ah hah -- I had missed your `Ref` and channel types. Thanks for the detailed explanation!
cfallin··on Gluon: A static, type inferred and embeddable language written in Rust
This is an impressive effort!

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!

cfallin··on Google and LinkedIn announce massive land swap
The muddy marsh/salt ponds surprised me too! But there are a few spots where the actual Bay (open water) is visible... e.g. Baylands Nature Preserve in Palo Alto. Also nearby hills have views -- I used to take walks to the top of the hill behind Google's Crittenden buildings, from where you can see a good bit of the southern Bay and up the peninsula northward. (Those hills also used to be MTV's garbage dump, so it could be worse!)
cfallin··on Friends are as genetically similar as fourth cousins
It can get even more fun!

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).
cfallin··on MIT Takes Multicore in a Different Direction
Fair point -- I forgot about MOSIS. Agreed, pretty reasonable for small test runs. I guess I've mostly heard of tapeouts when proving out a full design though, e.g. TRIPS from UT-Austin (and they had a team of ~20 students, so that was worth a number of dissertations). I can't imagine doing a tapeout while working on caching or branch prediction or other core comparch subfields like that -- the deliverable is an algorithm and/or a new idea, not a physical proof of concept, and the usual effort (a grad-student-year or two, at most) isn't high enough.

(All that said, seeing a real chip at the end of a project must be a heck of an exhilarating feeling of success...)

cfallin··on MIT Takes Multicore in a Different Direction
You definitely wouldn't want to use Verilog to iterate quickly on a simulation model. It's a lot more work: in a software model, you can (for example) say the equivalent of "this instruction is a divide and takes 32 cycles", then mark the divider busy for the next 32 cycles in a reservation table and increment the "total cycles taken by this benchmark" counter. In the RTL you'd actually have to build a divider and integrate it into the pipeline.

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.

cfallin··on MIT Takes Multicore in a Different Direction
From their MICRO'15 paper (section 5): "We use an in-house microarchitectural, event-driven, sequential simulator based on Pin".

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.

cfallin··on Haskell ArgumentDo Proposal
I'm curious, what sorts of issues arise with complex types that Haskell-like syntax handles better? (Genuinely curious here -- I've written a little Haskell but am by no means an expert...)

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?

cfallin··on Haskell ArgumentDo Proposal
I wonder if anyone is working on a full rework of Haskell's syntax, a la Facebook's Reason for OCaml? I'm a big fan of the semantics but not as much the syntax of Haskell, too, and it would be interesting to see what a more... current-day-mainstream... syntax (Rust-like braces-and-semicolons-expression-based-language style, perhaps) would do for it.
cfallin··on US Declaration of Independence first and final drafts as GitHub diffs
That's how the Unix History Repository was built, at least (a really cool project that reconstructed a synthetic git commit history for Unix from the initial Bell Labs source onward):

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)

cfallin··on What are “actual pictures” of atoms actually pictures of?
Ah, yes, sorry, I missed your "of a single non-repeating atom" :-)

I'm also curious -- but I wonder, would this allow for imaging anything that a scanning electron microscope can't?

← PreviousPage 5 of 10Next →