Llvm

3 posts

github3 min readCurated summary

Don’t stop early: Case-folding source code at memory speed

Case folding converts text into a canonical, case-insensitive form for comparisons, making it essential to GitHub’s large-scale code search. GitHub optimized this operation by removing an apparent optimization: instead of stopping at the first non-ASCII byte, it scans the entire buffer branchlessly, enabling SIMD vectorization. The resulting Rust `casefold` crate processes ASCII at over 45 GiB/s—close to memory-bandwidth limits. ## Case Folding Is Not Lowercasing - Lowercasing is intended for display and can depend on locale or context. - Greek sigma may become `ς` or `σ`. - Turkish `I` has locale-specific behavior. - Case folding is intended for comparison and must be locale-independent and symmetric. - Unicode provides explicit rules in `CaseFolding.txt`. - The crate supports simple one-to-one folds (statuses C and S), but not: - Full folds such as `ß → ss` - Turkic-specific folds such as dotted `İ` - This restriction matches tools such as ripgrep and helps maintain consistent matching behavior. ## Why Case-Folding Performance Matters - GitHub’s Blackbird search engine indexes more than: - 180 million repositories - 480 TB of source code - Source bytes are case-folded before n-gram extraction and indexing. - Folding is also needed when evaluating potential query matches. - Since most source code is ASCII, optimizing the ASCII path provides the largest benefit. ## Removing the Early Exit - A conventional implementation scans until it finds a non-ASCII byte, then switches to Unicode processing. - On an Apple M4, this branch-heavy approach reached only about 3.1 GiB/s. - The optimized loop: - ORs every byte into an accumulator to detect non-ASCII data once. - Uses `b.wrapping_sub(b'A') < 26` as a branchless uppercase test. - Sets bit 5 with `| (is_upper << 5)` to lowercase uppercase ASCII letters. - The loop always processes and writes the entire buffer, then checks whether Unicode processing is necessary. ## Vectorization Beats Early Termination - Removing the data-dependent `break` allows LLVM to vectorize the loop with 16-byte NEON instructions. - Performance progression on a 5.7 KB ASCII buffer: - Naive branchy loop: 3.1 GiB/s - Branchless body with early exit: 2.6 GiB/s - Early exit removed: 7.6 GiB/s - Fully branchless loop: over 45 GiB/s - The early exit prevents vectorization even when the loop body is otherwise branch-free. - Branchless arithmetic then eliminates compare-and-blend overhead and enables full memory-speed performance. ## Why Branchless Code Can Be Slower - In scalar code, the branchless version writes every byte, even when no change is needed. - The branchy version skips stores for the majority of lowercase letters, digits, spaces, and other unchanged bytes. - Its conditional branch is highly predictable, so it is inexpensive. - Branchless writes become beneficial only after vectorization, where the processor handles a whole vector at once. The practical lesson is to avoid data-dependent loop exits when they block vectorization. For predominantly ASCII workloads, a complete branchless scan can outperform “stop as soon as possible” logic by a wide margin, while an accumulated high-bit check efficiently identifies inputs requiring Unicode handling.

Read original(opens in new tab)
datadog2 min readCurated summary

Unraveling a Postgres segfault that uncovered an Arm64 JIT compiler bug

Postgres was crashing with segmentation faults when executing certain expensive queries on an Arm64 Kubernetes cluster. Investigators reduced the failure to a simple table scan and discovered that disabling JIT compilation prevented the crash. Assembly-level debugging ultimately traced the problem to a bug in LLVM’s Arm64 JIT support. ## Isolating the Crash - The failures occurred across multiple EC2 nodes, ruling out faulty hardware. - Query logs showed that the crashes consistently followed a small number of query patterns. - The simplest reproducer was: ```sql SELECT repo_id FROM repository; ``` - Core dumps had badly corrupted stacks, but surviving frames pointed to `ExecRunCompiledExpr`, suggesting a failure during JIT execution. - The unusually short backtraces reinforced the suspicion that the stack itself had been corrupted. ## How PostgreSQL JIT Works - PostgreSQL normally evaluates SQL expressions through a general-purpose interpreter. - JIT compilation converts expressions such as `1+1` into native machine code, reducing interpreter overhead for large workloads. - JIT can also optimize tuple deforming by converting disk tuples into in-memory values more efficiently. - PostgreSQL uses LLVM to generate the compiled code. - Because compilation adds overhead and compiled functions are not reused between queries, PostgreSQL enables JIT primarily for expensive queries based on cost thresholds. ## The Query of Death - The affected query scanned a partitioned `repository` table with: - 64 partitions - More than 1.6 million rows - 128 JIT-generated functions - Its query plan enabled expression compilation and tuple deforming: ```text JIT: Functions: 128 Expressions: true Deforming: true Inlining: false Optimization: false ``` - Running the query with: ```sql SET jit = off; ``` completed successfully. - Disabling JIT cluster-wide immediately stopped the crashes without noticeable query-latency effects. ## Root Cause Direction - The release build of PostgreSQL offered limited debugging flexibility, so the team planned to reproduce the failure in a dedicated test environment. - Further investigation eventually isolated the defect to JIT compilation on Arm64 systems. - The underlying issue was identified as an LLVM bug rather than a PostgreSQL query or hardware problem. - The investigation continued down to generated assembly and resulted in an upstream fix. The immediate mitigation was to disable PostgreSQL JIT, while the durable solution was to adopt the LLVM fix addressing the Arm64 code-generation bug.

Read original(opens in new tab)
figma3 min readCurated summary

Figma is powered by WebAssembly | Figma Blog

WebAssembly reduced Figma’s load time by more than three times after replacing its asm.js-based C++ runtime. The improvement came primarily from faster parsing, native-code compilation, and caching—not from a major reduction in compressed download size. Figma’s experience demonstrated that WebAssembly could deliver substantially faster, desktop-quality web applications, though browser support and implementation differences remained limitations. ## What WebAssembly Changes - WebAssembly is a compact binary format for machine code designed specifically for browsers. - Figma’s C++ code was a strong candidate because C++ can be compiled directly to WebAssembly. - Before WebAssembly, Figma used asm.js, a restricted JavaScript subset that represents memory as a large numeric array. - WebAssembly preserves asm.js’s limitations: - It primarily loads and stores numbers. - It must call JavaScript for browser APIs such as the DOM and networking. - It remains subject to the browser sandbox. ## Why WebAssembly Is Faster Than asm.js - **Smaller and faster to parse:** WebAssembly’s binary format transfers efficiently and parses around 20 times faster than asm.js. - **Ahead-of-time optimization:** LLVM optimizes the C++ code before compilation, allowing browsers to translate it directly to native code. - **Effective caching:** Browsers can cache the translated native code, making subsequent loads nearly free. - **Native 64-bit integer support:** WebAssembly avoids the slower emulation required by JavaScript’s 53-bit integer limitation. - **Less runtime optimization work:** Unlike JavaScript, WebAssembly does not require extensive browser optimization passes for code that was already compiled and optimized. ## Figma’s Load-Time Results - Figma measured load time from application initialization through downloading and rendering an entire design for the first time. - Switching from asm.js to WebAssembly improved load time by more than three times across document sizes. - The gain was especially meaningful because Figma users often work with large documents and switch between them frequently. - Subsequent loads could benefit further from cached WebAssembly-to-native translations. - The compressed download size improved only slightly because compressed asm.js was already close in size to compressed WebAssembly. ## Browser Support Limitations - At the time of publication, WebAssembly was enabled by default in Firefox and Chrome. - Edge and Safari were still developing their implementations. - Figma enabled WebAssembly only in Firefox because Chrome’s implementation had blocking issues, including the lack of caching for translated WebAssembly code. - These browser-specific differences affected whether the performance benefits could be consistently delivered. Figma’s results suggested that teams with substantial C or C++ code should seriously consider WebAssembly for performance-sensitive web applications. The largest benefits were faster startup and reusable native-code caching, while download-size improvements were comparatively modest.

Read original(opens in new tab)