Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ZeroTrie: Add cursor type for manual iteration and use it in BlobSchemaV2 #4383

Merged
merged 23 commits into from
Dec 5, 2023

Conversation

sffc
Copy link
Member

@sffc sffc commented Nov 30, 2023

Fixes #4249
Fixes #4379

Depends on #4381
Depends on #4382

@sffc sffc requested review from Manishearth and a team as code owners November 30, 2023 05:57
@sffc
Copy link
Member Author

sffc commented Nov 30, 2023

11% improvement on BlobDataProvider read performance:

provider/construct/v1   time:   [50.531 ns 50.638 ns 50.778 ns]
                        change: [+0.0986% +0.5478% +1.0605%] (p = 0.03 < 0.05)
                        Change within noise threshold.
Found 13 outliers among 100 measurements (13.00%)
  3 (3.00%) high mild
  10 (10.00%) high severe

provider/construct/v2   time:   [50.346 ns 50.397 ns 50.451 ns]
                        change: [-0.3096% -0.0285% +0.2176%] (p = 0.83 > 0.05)
                        No change in performance detected.
Found 7 outliers among 100 measurements (7.00%)
  5 (5.00%) high mild
  2 (2.00%) high severe

provider/read/v1        time:   [1.1265 µs 1.1279 µs 1.1297 µs]
                        change: [-0.5655% -0.3793% -0.1707%] (p = 0.00 < 0.05)
                        Change within noise threshold.
Found 10 outliers among 100 measurements (10.00%)
  4 (4.00%) high mild
  6 (6.00%) high severe

provider/read/v2        time:   [688.62 ns 689.91 ns 691.28 ns]
                        change: [-11.527% -11.363% -11.201%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 9 outliers among 100 measurements (9.00%)
  7 (7.00%) high mild
  2 (2.00%) high severe

@sffc sffc requested a review from younies November 30, 2023 06:04
@sffc
Copy link
Member Author

sffc commented Nov 30, 2023

@younies This provides the core functionality we need to implement strip_prefix but I'll do that in the next PR.

trie: ZeroTrieSimpleAscii<&'a [u8]>,
}

impl<'a> fmt::Write for ZeroTrieStepWrite<'a> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you make this a general fmt::Write instead of something more locale-specific, it should handle UTF-8 input correctly. write_str currently forwards non-ASCII bytes, and write_char panics (debug) or forwards (release).

You can use the Err case to signal that non-ASCII is encountered. Then you can allow the unwrap on the grounds that locale.write_to produces ASCII only.

You should probably call this ZeroTrieSimpleAsciiStepWrite.

experimental/zerotrie/src/zerotrie.rs Outdated Show resolved Hide resolved
/// assert_eq!(it.head_value(), None); // "abcdxy"
/// ```
#[inline]
pub fn step(&mut self, byte: u8) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are you keeping the lookup state in the trie itself? The API I'd expect would be:

  • call lookup_write(&self) to obtain a ZeroTrieSimpleAsciiLookup<'a> type that has a non-exclusive ref to the trie
  • that type can implement fmt::Write, or expose step
  • that type has a current() getter

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ZeroTrieSimpleAscii is capable of stepping itself without the need to introduce any new types. This is an API that we can add regardless of whether we expose a more general ZeroTrieLookup type that has an API similar to the one you describe.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that your proposed API is more intuitive and self-explanatory. If you're okay with that then I can push to this PR to implement it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please do

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PTAL

@younies younies mentioned this pull request Nov 30, 2023
@sffc
Copy link
Member Author

sffc commented Dec 1, 2023

No appreciable difference in performance. Ready for re-review with the new API.

FYI @younies I added an example for how to query for the longest prefix:
http://icu4x-pr-artifacts.storage.googleapis.com/gha/d5e9e2e86fcd6f11a355e73caafcc0a992be1551/docs/zerotrie/struct.ZeroTrieSimpleAscii.html#method.cursor

step_bsearch_only(&mut self.trie.store, byte)
}

/// Takes the value at the current position and moves the cursor.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's unclear to me where it moves the cursor, I don't see a default place where it could be moved

Copy link
Member Author

@sffc sffc Dec 1, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I deleted peek_value which means I no longer need that clause to distinguish the behavior of value from peek_value.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, but why does this "take" and use a &mut? This should not mutate.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Values are stored in trie nodes. When we read a value, we can step over that trie node so that we don't need to do that again next time we call step with the next character.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is value() idempotent?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pub fn value(self) -> Result<usize, Self>?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could make a moving pub fn value(self) along with a borrowing pub fn has_value(&self) which is mostly as efficient as pub fn take_value(&mut self) but I still prefer the mutating value function.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pub fn value(self) -> Result<usize, Self>?

That signature doesn't allow checking the presence of a value and then continuing, as we need to do in the longest-prefix example.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually my has_value suggestion does not work well in the longest prefix example if you care about retaining the value, which we probably do most of the time.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the function name to take_value


impl<'a> ZeroTrieSimpleAsciiCursor<'a> {
/// Steps the cursor one byte into the trie.
///
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how does this handle non-ASCII?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

provider/blob/src/blob_schema.rs Outdated Show resolved Hide resolved
sffc and others added 2 commits December 1, 2023 13:24
Co-authored-by: Robert Bastian <4706271+robertbastian@users.noreply.github.com>
@sffc
Copy link
Member Author

sffc commented Dec 1, 2023

Also can someone review the diffbase PR #4382?

@sffc
Copy link
Member Author

sffc commented Dec 1, 2023

@younies I marked this PR as fixing #4379 because it makes it easy to do the prefix match in userland, as the example demonstrates.

Copy link

dpulls bot commented Dec 1, 2023

🎉 All dependencies have been resolved !

younies
younies previously approved these changes Dec 4, 2023
Manishearth
Manishearth previously approved these changes Dec 4, 2023
@@ -504,6 +504,80 @@ pub fn get_phf_extended(mut trie: &[u8], mut ascii: &[u8]) -> Option<usize> {
}
}

pub(crate) fn step_bsearch_only(trie: &mut &[u8], c: u8) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: docs

@sffc sffc dismissed stale reviews from Manishearth and younies via 09b56c0 December 4, 2023 21:59
@sffc sffc changed the title ZeroTrie: Add step function for manual iteration and use it in BlobSchemaV2 ZeroTrie: Add cursor type for manual iteration and use it in BlobSchemaV2 Dec 4, 2023
@robertbastian robertbastian merged commit f50e87f into unicode-org:main Dec 5, 2023
29 checks passed
@sffc sffc deleted the zerotrie-step branch December 5, 2023 17:18
@robertbastian
Copy link
Member

changelog pls

sffc added a commit that referenced this pull request Dec 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
4 participants