Recent error messages, deduped
Requirement
A log store returns entries newest first. Show the three most
recent distinct error messages: keep only ERROR
entries, drop repeats of a message already shown, and stop after three.
The data is in the code below; both versions must print the lines shown
under Expected output.
Expected output
09:41 payment gateway timeout 09:31 inventory service 503 09:17 invalid session token
Side by side
Native Dart
FxDart
Why they differ
Dart has no "distinct by key" — deduping by message means managing a
Set yourself, so the native version becomes a loop with
three concerns braided together: the level check, the
seen.add trick, and a counted break. Each is
fine alone; together they force you to read the whole loop to see what
it keeps. FxDart states the three rules as three chain steps —
filter, uniqBy, take — and
because the chain is lazy, it also stops scanning the log the moment the
third distinct error is found, exactly like the hand-written
break.
Two FxDart spellings
The benchmark on this page carries a third bar, which no
other comparison does. The chain above is the one to write: three
independent rules, read top to bottom, and lazy — it stops scanning at the
third distinct error, exactly like the hand-written break.
What it cannot do is inline its own callbacks. A lazy stage keeps its
closure in an iterator field, and the AOT compiler cannot see through a
field, so filter and uniqBy each cost a real
indirect call on every element — together, most of what separates this
pipeline from the native loop.
takeUniqBy, shown above main in the FxDart panel,
is the same pipeline written as one strict call. Its callback is a
parameter of a body small enough to inline into the caller, so the
compiler inlines the closure with it; one callback does both jobs, with a
null key meaning "skip this element". Over 1,000,000 log lines
that is the difference between the second and third bars — and the native
loop is what the first bar shows.
Write the chain by default. Reach for takeUniqBy when the
pipeline is hot and a profile says these callbacks are the cost.
Benchmark
N = 100
Time Tie
Peak memory Tie
N = 10,000
Time Tie
Peak memory Tie
N = 1,000,000
Time Native wins
Peak memory Tie
Bars are medians of repeated timed iterations in fresh processes per side (small N is batched for timer resolution). Sides within 5% of each other — or within 0.6 ms, a difference no person can perceive — count as a tie; close relative races are re-measured up to 5 runs. In an app, anything under a few milliseconds is invisible to the user regardless of which bar is shorter. Memory is peak process RSS. The Dart VM and the dataset are identical on both sides, so the difference between the two bars is what the pipeline itself holds onto.