SIMD kernels (AVX-512/AVX-2/SSE2, runtime-dispatched) counting a single pass over mmap'd files, split across cores past 8 MiB, with exact GNU oracle parity (NBSP included, glibc's decoder fixed, the whole -m path mirrored so counts agree at every boundary). Result: 1ms vs 2ms at 1M lines, 8-9ms vs 22-24ms at 10M. Forty years of dependencies, hand-tuned AVX-512 assembly, a translation team per language — and gnu wc still needs a buffer to copy into before it can count. We mapped the file and just counted. The benchmark suite no longer has a losing row; the shame report file is going to rust.
97 lines
4.0 KiB
Markdown
97 lines
4.0 KiB
Markdown
# fastwc Style Guide
|
|
|
|
The style is the law. GNU wc can afford to look like a committee
|
|
designed it — it was. We're one file, one purpose, one opinion.
|
|
|
|
## The language
|
|
|
|
- **C99 with POSIX.** `_POSIX_C_SOURCE 200809L`. We target the
|
|
standard, not a compiler vendor's mood.
|
|
- **Zero warnings** with `-Wall -Wextra`. A warning is a bug you
|
|
haven't fixed yet. Fix it.
|
|
- **No compiler-specific extensions** unless they're guarded and the
|
|
benchmark gets faster because of it. `__builtin_popcountll` is the
|
|
one exception — it's the price of admission on every compiler that
|
|
matters, and the benchmark proves it.
|
|
|
|
## Format
|
|
|
|
- **4 spaces.** Not tabs, not 8 spaces. GNU uses tabs because GNU was
|
|
written in 1985 by people who had strong opinions about typewriters.
|
|
- **80 columns.** If your line is longer, your idea is longer than it
|
|
needs to be.
|
|
- **Braces on the next line** (Allman). Everywhere. Functions, blocks,
|
|
switch cases. Consistency is the only religion here, and we're devout.
|
|
- **Snake case for everything.** `count_stream`, `ws_tab`, `nread`.
|
|
No CamelCase — that's for languages that need help knowing what a
|
|
word is.
|
|
|
|
## Structure
|
|
|
|
- **One file: `src/main.c`.** See CONTRIBUTING.md rule 6. This is not a
|
|
suggestion.
|
|
- **Everything is `static`.** The binary exports nothing. We're not a
|
|
library, we're a verdict.
|
|
- **Types end in `_t`.** `counts_t`, and not much else. GNU has a type
|
|
for everything and a committee to name it. We have counts, and we
|
|
count.
|
|
- **No globals except what's truly process-wide.** `flags` and `ws_tab`
|
|
live at file scope because they're the program's identity. Everything
|
|
else gets passed around like it's hot.
|
|
|
|
## The hot path
|
|
|
|
`count_stream()`, `count_newlines()`, `count_words()`, and the SIMD
|
|
kernels (`count_lw_avx512`, `count_lw_avx2`, `count_lw_sse2`) are the
|
|
product. The rest of the file is just the packaging.
|
|
|
|
- **Fixed stride.** No per-token function calls. No `isspace()` in a
|
|
loop — that's what the lookup table is for.
|
|
- **No allocation, no locks, no syscalls in the counting loop.** The
|
|
kernels are pure; parallel slices need no locks. The `fread` (or the
|
|
single `mmap`) is the only syscall, and it's not yours to add to.
|
|
- **Branchless where it costs nothing.** A predictable branch is fine;
|
|
a mispredicted one is a lie you told the CPU.
|
|
- **Regular files are mapped, not streamed.** The kernel hands us the
|
|
pages; we count them, split across cores for anything past 8 MiB.
|
|
Stdin and odd files fall back to the `static`, 128 KiB buffer that
|
|
never grows.
|
|
|
|
## Counts
|
|
|
|
- **`long long` for everything that counts.** Files can be bigger than
|
|
your opinions. Signed, because overflow should scream, not wrap.
|
|
- **Lines are `'\n'`s.** Not "lines of text," not "records." A file
|
|
without a trailing newline has fewer newlines than it has lines, and
|
|
that's correct. GNU agrees with us on this one, so it must be right.
|
|
- **Words are whitespace-delimited runs.** The lookup table decides
|
|
what whitespace is; the popcount decides how many words; you don't
|
|
get an opinion.
|
|
|
|
## Errors
|
|
|
|
- **`fastwc: <file>: <message>` on stderr**, and keep going. GNU wc
|
|
fails gracefully across multiple files, and we'll credit them that
|
|
one thing. Then we'll be faster at it.
|
|
- **Exit 1 if anything failed**, 0 otherwise. No drama, no
|
|
`EXIT_FAILURE` poetry.
|
|
|
|
## Comments
|
|
|
|
- **Only when the code can't speak for itself.** The SWAR newline
|
|
counter in `count_newlines()` gets a comment because
|
|
`(x ^ nl) & cl` is a sentence in a language you don't speak yet.
|
|
`i++` does not get a comment.
|
|
- **No commented-out code.** The dead don't get to live in the file.
|
|
- **No TODO without a date.** A TODO without a date is a promise you
|
|
plan to break.
|
|
|
|
## What the style is not
|
|
|
|
- It is not GNU's style. GNU's style was written by a committee,
|
|
ratified by a foundation, and translated into fourteen languages.
|
|
- It is not "whatever your editor defaults to." Your editor has
|
|
opinions. So do we. Ours are right.
|
|
- It is not negotiable in review. If a PR violates the style, the PR
|
|
is a rewrite request with extra steps.
|