We Ran the Same Test Suite on macOS, Windows and Linux, and Windows Found Us Losing — Wide Field Part 13: 2.49x / 3.42x / 1.88x Overall With Zero Mismatches, but xz, zst and lz4 on Windows at 1/1.5–1/2.1 of ripgrep

Technical Guide

Almost every number in this series came from one Mac.

UwView ships for Windows, macOS and Linux. Is “on par with ripgrep or better”, measured on a Mac, true on the other two?

We ran the same test suite on three machines.

The short answer: all 145 correctness tests passed on all three machines, and the 96 speed comparisons had zero mismatched results. Overall, UwView (uvf/uvp) was faster on all three: 2.49× on macOS, 3.42× on Windows and 1.88× on Linux. On one large file the gap was widest on Windows (uvf 2.47× over seven searches on 50 GB). But Windows turned up a loss the Mac never showed: on compressed xz, zst and lz4, uvf was 1.5–2.1× slower than ripgrep. We have not yet confirmed why

uvf and uvp are the search commands of UwView (v1.7.3.6.9 “Wide Field”, 4 October 2026), which I build. uvf is in the free edition, uvp in the paid UwView Pro. Windows and Linux were measured with 1.7.3.6.8 (1.7.3.6.9 only changes searching many small files).


The three machines, and what we measured

macOS Windows Linux
Machine Apple M4, 10 logical, 32 GB, external USB SSD x64, 8 logical, 15.6 GB aarch64, 2 logical, 7.0 GB
Version 1.7.3.6.9 1.7.3.6.8 1.7.3.6.8
Threads uvf 8, uvp 8 (default) uvf 4 (set in config), uvp 8 uvf 2, uvp 2 (default)
ripgrep 15.2.0 15.2.0 15.1.0
Full run 2 h 20 min 4 h 9 min 3 h 29 min

The machines are completely different, so seconds are never compared across systems. All we line up is, on each machine, “ripgrep’s time ÷ UwView’s time” (above 1 means UwView is faster; under 1.5× is “on par”).

uvf ran with 4 threads on Windows because the test machine’s config file said so — not the default (up to the logical processor count). That matters later.

Correctness: all three pass

macOS Windows Linux
Input types (encodings, 7 compression formats, empty files, …) 66 pass 66 pass 66 pass
Multiple files 36 pass 34 pass, 2 skipped 36 pass
Exclusion rules (.gitignore and friends) 43 pass 43 pass 43 pass
Output checks during the speed runs 0 mismatches 0 mismatches 0 mismatches

The two skipped on Windows were “no way to create a zip” (no Python on the test machine), not a product problem.

Every speed comparison checks the output against ripgrep’s (or sed, sort and uniq’s). Fast with a different answer means nothing, so that comes first.

One large file: the gap is widest on Windows

Seven searches (plain, -i, -E, anchored, -E -i, -v, -E -v) on OpenStreetMap XML of 3 GB, 10 GB and 50 GB, first run (right after dropping the cache) plus second run.

vs ripgrep macOS uvf Windows uvf Linux uvf macOS uvp Windows uvp Linux uvp
3 GB 1.00 1.48 1.31 2.39 2.22 1.62
10 GB 1.16 1.90 1.13 3.28 2.81 2.45
50 GB 1.13 2.47 1.33 3.74 3.44 1.99
Overall, 96 items (uvf + uvp) 2.49 3.42 1.88
  • On Windows, ripgrep’s first run was slow, taking 1.65–2.53× as long as uvf. Even on the second 50 GB run uvf was 2.42× faster. uvf won all 21 combinations of 7 patterns × 3 sizes.
  • On the Mac, uvf is on par with ripgrep (1.00–1.16) — exactly what this series has been saying.
  • uvp was 1.6–3.7× faster on all three, even including the first run that builds the index (.uwvz). From the second search on it only reads the index.

Look only at the Mac and the story ends at “uvf is on par with ripgrep”. On Windows the same uvf is 1.5–2.5× faster. Both are true; write only one and it will be wrong for some readers.

Many files: uvf ahead on Windows and Linux

vs ripgrep (first + second run) macOS Windows Linux
5 plain files 1.02 1.86 2.22
7 gz files 1.31 1.54 1.67
12 plain + gz files 1.29 1.58 1.67

On Linux with 2 logical cores, rg -z calling an external gzip for every gz file costs a lot. uvf decompresses in-process.

The loss: compressed files on Windows

Compressed 1 GB and 3 GB files searched with rg -z and uvf (first + second run).

vs ripgrep mac 1G mac 3G Win 1G Win 3G Linux 1G Linux 3G
plain 1.06 1.07 1.15 1.53 1.04 1.07
gz 1.26 1.30 1.30 1.37 2.00 2.08
bz2 1.04 1.04 1/1.28 1/1.30 1.08 1.10
xz 1/1.11 1/1.09 1/1.43 1/1.69 1.11 1.06
lzma 1.22 1.22 1/1.02 1.08 1.30 1.23
zst 1.08 1.07 1/1.61 1/1.22 1/1.01 1.01
lz4 1.11 1.12 1/2.09 1/1.51 1.53 1.38
br 1.25 1.25 1.01 1.05 1.57 1.63
mixed (plain + compressed) 1.04 1.03 1/1.43 1/1.41 1.24 1.24
mixed (compressed only) 1.04 1.06 1/1.36 1/1.23 1/1.25 1/1.04

We lost by more than 1.5× in four places, all on Windows: xz (3G), zst (1G) and lz4 (1G and 3G). bz2 and the mixed sets are 1/1.2–1/1.4 — on par, but only Windows leans toward losing. On macOS and Linux the same formats were on par or better.

The Windows breakdown (3 GB, seconds — same machine, so seconds are fine here):

3 GB rg -z first / second uvf first / second
plain 4.03 / 1.35 2.53 / 0.99
bz2 39.38 / 36.73 48.22 / 50.80
xz 2.74 / 2.63 4.79 / 4.27
zst 2.49 / 2.18 2.98 / 2.71
lz4 1.43 / 1.14 2.08 / 1.81

One more number bothers me. The second run on 1 GB of plain text takes 0.57 s with uvf on Windows (0.12 s on the Mac). We don’t compare seconds across machines, but it makes me suspect a fixed cost of about 0.4–0.7 s on every run, even when there’s almost nothing to decompress. The faster a format decompresses (zst, lz4), the worse the ratio — which is what a fixed cost would do.

Suspects (not yet confirmed)

# Suspect Evidence
W1 The Windows uvf and uvp are not built with NativeAOT. Every start pays for starting .NET and compiling code on the spot A fixed 0.4–0.7 s on plain-text second runs and on uvp second runs; the faster the format decompresses, the worse the ratio
W2 uvf set to 4 threads (8 logical). It loses to external tools on parallel xz decompression and bz2 xz 3G: uvf 4.27 s, rg -z 2.63 s
W3 Antivirus (Defender) scanning Not checked

The macOS and Linux builds are NativeAOT (compiled to machine code ahead of time). The Windows build is a single-file executable that has to warm up on every start. NativeAOT can only be built on the target OS, and the Windows build was being made from a Mac.

We know how to check. W2: delete the 4-thread setting on the test machine and rerun only the compression comparison. W1: build NativeAOT on a Windows machine and compare. As of this article, neither has been done.

uvp on Linux (2 logical cores)

uvp builds an index (.uwvz) on the first run, and writing it compressed costs CPU.

On a 2-core Linux machine, for fast-to-decompress zst and lz4 and for plain text, that first run is heavy: two runs combined come to 1/1.2–1/1.4 of ripgrep (gz, bz2 and lzma are 1.7–2.1× faster). From the third search on uvp only reads the index and pulls ahead. On a machine with few cores, if you search only once, uvf or ripgrep is enough.

Caveats

  • We have not confirmed the cause of the Windows compression losses. W1–W3 above are suspects. When they are fixed, we’ll write it up with re-measured numbers.
  • uvf on Windows ran with 4 threads from a config setting. Measured at the default, results may change (we expect better, but won’t say so until measured).
  • Windows and Linux used 1.7.3.6.8, the Mac 1.7.3.6.9. The only difference is searching many small files, which barely touches the items in these tables.
  • The three machines differ in CPU, memory and disk. We do not compare seconds across systems. Each ratio is a ratio on that machine. Read it not as “Windows favors UwView” but as “on this Windows machine, ripgrep’s first run was slow”.
  • 50 GB doesn’t fit in memory on any of the machines, so the second run reads from disk too.
  • I’m writing about my own product. Please read with that in mind.

Takeaways: don’t call it fast on one machine

(1) Losses show up on other machines. Formats that were on par or better on the Mac were 1/2.1 on Windows. Measuring on one machine, we could never have written about this loss.

(2) Across machines, line up ratios only. Seconds from machines with different CPUs and disks don’t tell you what you’re comparing. We took “theirs ÷ ours” on each machine and lined those up.

(3) Check the answers match on every machine before the speed. Every speed comparison checks the output. Zero mismatches on all three.

(4) Write suspects and confirmations separately. “It’s slow because it isn’t NativeAOT” sounds right — and last time (Part 12) the mmap story that sounded right was wrong. Until it’s checked, it’s written as a suspect.

The tool

UwView is a viewer for huge text files and huge logs (free UwView / paid UwView Pro).

uvf app.log.zst ERROR                # search compressed files as they are (7 formats, no external commands)
uvf 'logs/*' ERROR                   # plain and compressed together, in one run
uvf --tune                           # measure the thread count that suits this machine (--apply saves it)

Exit codes follow grep: 0 = found, 1 = not found, 2 = error or incomplete result.

The free edition is on GitHub Releases. On a Mac, brew install --cask amru195704/uwview/uwview; on Windows, Scoop (scoop bucket add uwview https://github.com/amru195704/scoop-uwview, then scoop install uwview).

  • Part 12: the newline count I said would never show up in seconds was 40% of the second search: https://uvp.y42u.net/en/blog/uwview-wf12-newline-hot-en/
  • Part 11: --json and --tune (measuring the thread count): https://uvp.y42u.net/en/blog/uwview-wf11-json-and-tune-en/
  • Part 1: 12 plain and gz files in one command: https://uvp.y42u.net/en/blog/uwview-wf01-multi-file-one-command-en/
  • Choosing a compression format for logs (7 formats measured): https://uvp.y42u.net/en/blog/log-compression-format-choice-en/
  • If you use rg on Windows (--no-mmap): https://uvp.y42u.net/en/blog/uvp-rg-no-mmap-50gb-en/
  • Where we stopped calling things a difference: https://uvp.y42u.net/en/blog/uwview-fl09-difference-threshold-en/
  • v1.7.3.6.9 release notes: https://github.com/amru195704/UwView/blob/main/2-doc/release-body-v1.7.3.6.9.md#uwview-v17369–wide-field-english
  • Every measurement in one place: https://uvp.y42u.net/en/benchmarks-en/
  • Source code (GitHub): https://github.com/amru195704/UwView

And if you are storing huge logs that eat your disk and want to search them compressed, take a look at UwView Pro — a persistent index, search inside the compressed cache, about 1/9 of the storage, and a faster reopen as well (all operating systems; one-time purchase or monthly plan).


From the developer: my apps, Kindle books and public projects are listed at GitHub: amru195704.


Copied title and URL