Skip to content

dictBuilder: break COVER sort ties by position, not by address - #1

Closed
jaypatrickhoward wants to merge 1 commit into
devfrom
cover-cmp-fix
Closed

dictBuilder: break COVER sort ties by position, not by address#1
jaypatrickhoward wants to merge 1 commit into
devfrom
cover-cmp-fix

Conversation

@jaypatrickhoward

Copy link
Copy Markdown
Owner

CI dry run on the fork, per CONTRIBUTING.md:

Before sharing anything to the community, create a pull request in your own fork against the dev branch and make sure that all GitHub Actions CI tests pass.

The full description will go on the upstream PR against facebook:dev once this is green.

Local checks already passing: make check, make test, make staticAnalyze (16 findings, identical to unmodified dev), C90 -Wall -Wextra -Werror -pedantic across four DEBUGLEVEL/NDEBUG combinations.

stableSort() is documented to leave each dmer group ordered by position in
the input, and COVER_group() depends on it: it counts how many samples a dmer
occurs in using a forward-only cursor, so a group whose positions are not
ascending has occurrences silently dropped.
COVER_strict_cmp() tried to provide that ordering by breaking ties on
`lp < rp`. But lp and rp are the addresses of the elements being compared,
not the positions they hold. Those coincide only until qsort() performs its
first swap; afterwards the tie-break orders by where an element currently
sits rather than by what it contains.
The result is a comparator that is not a function of the values it compares,
with three consequences:
- Output is not reproducible. When qsort() compares an element against a
temporary (a pivot copy, which is common), one operand is not in the array
at all, so the comparison is stack-vs-heap and ASLR decides it. The same
binary on the same input produced two different dictionaries in six
consecutive runs.
- Output depends on the C library. glibc happens to satisfy the invariant;
Apple libc and MSVC do not, so they produce different dictionaries from the
same input.
- An inconsistent comparator breaks quicksort's partitioning assumptions.
On MSVC, training on a 16 MB corpus at d=6 takes 465s; with this fix, 8s.
Comparing the stored positions instead makes the key (dmer, position).
Positions are unique, so no two elements compare equal, the order is total,
and every conforming qsort() must produce the same arrangement.
Verified: on glibc the output is unchanged (byte-identical across 215
configurations spanning 20 corpora), and on Apple libc and MSVC the output
now matches what glibc produces.
@jaypatrickhoward

Copy link
Copy Markdown
OwnerAuthor

CI dry run complete: 103/103 checks passed. Superseded by the upstream PR: facebook#4765

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@jaypatrickhoward