Optimize CoverageByXPathLevels data model and trie queries - #8333
Conversation
…lay names Redesign CoverageByXPathLevels into 4 category-specific ZeroTrie fields (language, territory, script, variant), extracting subtag attribute keys and integer coverage level discriminators. Hoist per-locale coverage level lookups outside subtag loops and eliminate string allocations during trie traversal. Co-authored-by: Gemini <176961590+gemini-code-assist[bot]@users.noreply.github.com>
|
At a94ab0d: At df937bf: That's a significant enough improvement that I think it's worth landing this. I don't intend to spend more time than I already have on code that will most likely be mostly deleted. |
Co-authored-by: Gemini <176961590+gemini-code-assist[bot]@users.noreply.github.com>
| // CLDR coverage files contain some non-ASCII XPaths, principally emoji annotations | ||
| // (e.g., //ldml/annotations/annotation[@cp="😀"]). |
There was a problem hiding this comment.
this comment is obsolete since now we allowlist the XPaths we want instead of ingesting all of them
| let coverage_cache = | ||
| crate::displaynames::coverage_experimental::coverage_cldr_cache(); | ||
| let locale_levels = coverage_cache.get_levels_for_locale(&locale, cldr)?; | ||
| let root_levels = coverage_cache.get_root_levels()?; |
There was a problem hiding this comment.
Pulling these out of the inner loop is a low-cost way to reduce hits on read_and_parse
Co-authored-by: Gemini <176961590+gemini-code-assist[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Redesigns the experimental CLDR coverage lookup used by display names providers to avoid XPath string construction/allocations and to speed up trie queries by indexing coverage tiers by display-name category + subtag (+ optional alt/menu). Adds an end-to-end export test exercising locale display name markers.
Changes:
- Refactors
CoverageByXPathLevelsinto four category-specificZeroTrieSimpleAsciitries and updates coverage tier lookup APIs accordingly. - Updates all display name providers/macros to use
DisplayNameCategory-based coverage lookups (and hoists per-locale coverage level fetches out of inner loops). - Adds an e2e export test and enables the
blob_exporterdev-dependency feature, plus anetworking-gated integration test target.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| provider/source/tests/e2e.rs | Adds an integration test exporting LocaleNames markers to a blob and asserting a minimum output size. |
| provider/source/src/displaynames/variant.rs | Switches coverage lookups from XPath prefix strings to DisplayNameCategory::Variant. |
| provider/source/src/displaynames/script.rs | Switches coverage lookups from XPath prefix strings to DisplayNameCategory::Script (all tiers/alt variants). |
| provider/source/src/displaynames/region.rs | Switches coverage lookups from XPath prefix strings to DisplayNameCategory::Territory. |
| provider/source/src/displaynames/language.rs | Switches coverage lookups from XPath prefix strings to DisplayNameCategory::Language (including menu providers). |
| provider/source/src/displaynames/mod.rs | Updates provider macros to use category-based coverage lookups and hoists per-locale coverage level retrieval in iterators. |
| provider/source/src/displaynames/coverage_experimental.rs | Implements the new category-specific trie model, cursor-based lookup, and new coverage APIs/tests. |
| provider/source/src/cldr_serde/displaynames/mod.rs | Adds as_str() helpers for Alt/Menu to support allocation-free cursor writes. |
| provider/source/Cargo.toml | Enables icu_provider_export’s blob_exporter feature and adds a networking-gated e2e test target. |
Suppressed comments (2)
provider/source/src/displaynames/mod.rs:185
- The macro signature now takes
$category, but the doc comment doesn’t list/document it.
/// - `$resource`: The CLDR serde resource type.
/// - `$file`: The JSON file name in CLDR.
/// - `$field`: The field name in `LocaleDisplayNames` containing the data.
/// - `$tier`: The target coverage tier.
macro_rules! impl_displaynames_menu_v1 {
($marker:ident, $subtag_ty:ty, $resource:path, $file:literal, $field:ident, $category:expr, $tier:pat,) => {
provider/source/src/displaynames/mod.rs:339
- The macro signature now takes
$category, but the doc comment doesn’t document this parameter.
/// - `$resource`: The CLDR serde resource type.
/// - `$file`: The JSON file name in CLDR.
/// - `$field`: The field name in `LocaleDisplayNames` containing the data.
/// - `$alt_variant`: The alt variant (e.g., `None`, `Some(Alt::Short)`).
/// - `$tier`: The target coverage tier.
macro_rules! impl_displaynames_iter_v1 {
($marker:ident, $subtag_ty:ty, $resource:path, $file:literal, $field:ident, $alt_variant:expr, $category:expr, $tier:pat) => {
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -95,7 +95,7 @@ where | |||
| /// - `$alt_variant`: The alt variant (e.g., `None`, `Some(Alt::Short)`). | |||
| /// - `$tier`: The target coverage tier. | |||
| macro_rules! impl_displaynames_v1 { | |||
| ($marker:ident, $subtag_ty:ty, $resource:path, $file:literal, $field:ident, $alt_variant:expr, $xpath_prefix:literal, $tier:pat,) => { | |||
| ($marker:ident, $subtag_ty:ty, $resource:path, $file:literal, $field:ident, $alt_variant:expr, $category:expr, $tier:pat,) => { | |||
| cldr: &CldrCache, | ||
| file_name: &str, | ||
| xpath_prefix: &str, | ||
| category: DisplayNameCategory, | ||
| mut extract_keys: impl FnMut(&Resource) -> &HashMap<WithAlt<T>, String>, |
| use icu_provider_source::SourceDataProvider; | ||
|
|
||
| #[test] | ||
| fn test_export_language_identifier_display_names() { |
There was a problem hiding this comment.
This test exists for performance benchmarking purposes. I could probably make it #[ignore]. I could maybe move it to a benches module.
There was a problem hiding this comment.
I don't see the value of this test. It was probably useful while you were working on performance improvements, but I don't think it should be checked in.
There was a problem hiding this comment.
also don't think it's useful as a benchmark
in any case, it should use our standard benchmark scaffolding
There was a problem hiding this comment.
I was intentional in making it a plain main fn. It is too slow for criterion, and I don't want to pull in other dependencies.
I think this is valuable as a benchmark because this covers a lot of datagen code paths, and improving the performance of this benchmark can improve the performance of datagen in general. Datagen in general is getting quite slow, so benchmarks seem warranted.
| let quote_idx = rest.find('"')?; | ||
| let val = &rest[..quote_idx]; | ||
| let remaining_attrs = &rest[quote_idx + 1..]; | ||
| let extra_attrs = remaining_attrs.strip_prefix(']')?; |
There was a problem hiding this comment.
this is split_once("\"]")
| Some(CoverageLevelForXPath::Modern) | ||
| } else { | ||
| None | ||
| impl TryFrom<RawCoverageByXPathLevels> for CoverageByXPathLevels { |
There was a problem hiding this comment.
suggestion: write a custom serde impl instead of using TryFrom. that way you don't have to collect into intermediate Vecs, and I also think it will make this code more readable
There was a problem hiding this comment.
ok, I think either structure is fine but I'll do the one you suggest
| impl CoverageByXPathLevels { | ||
| pub(super) fn level_for_category( | ||
| &self, | ||
| category: DisplayNameCategory, |
There was a problem hiding this comment.
instead of creating an enum to select the field on CoverageByXPathLevels, just let the caller select the field. you'll need a newtype around the trie for the remaining logic
There was a problem hiding this comment.
ok, I think either structure is fine but I'll do the one you suggest
Co-authored-by: Gemini <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: Gemini <176961590+gemini-code-assist[bot]@users.noreply.github.com>
…oryLevels newtype wrapper Co-authored-by: Gemini <176961590+gemini-code-assist[bot]@users.noreply.github.com>
…iminate intermediate Vec allocations Co-authored-by: Gemini <176961590+gemini-code-assist[bot]@users.noreply.github.com>
…nconditionally Co-authored-by: Gemini <176961590+gemini-code-assist[bot]@users.noreply.github.com>
…evels doc comment Co-authored-by: Gemini <176961590+gemini-code-assist[bot]@users.noreply.github.com>
|
I directed and monitored jetski to fix these issues. I'm conflicted because I want to land the 25% performance improvement on displaynames datagen, but I don't want to spend more time on code that is likely to be largely deleted once we get the new CLDR coverage levels. |
…g Option<&'static str> Co-authored-by: 176961590+gemini-code-assist[bot]@users.noreply.github.com
…remove Writeable Replace Co-authored-by: 176961590+gemini-code-assist[bot]@users.noreply.github.com
Co-authored-by: 176961590+gemini-code-assist[bot]@users.noreply.github.com
Co-authored-by: 176961590+gemini-code-assist[bot]@users.noreply.github.com
Co-authored-by: 176961590+gemini-code-assist[bot]@users.noreply.github.com
Co-authored-by: 176961590+gemini-code-assist[bot]@users.noreply.github.com
Co-authored-by: 176961590+gemini-code-assist[bot]@users.noreply.github.com
Co-authored-by: 176961590+gemini-code-assist[bot]@users.noreply.github.com
…er macros Co-authored-by: 176961590+gemini-code-assist[bot]@users.noreply.github.com
Co-authored-by: 176961590+gemini-code-assist[bot]@users.noreply.github.com
The only open conversation, I think, is the one about whether or not to land the benchmark. I don't consider this a blocking issue: the benchmark file can be easily deleted later without breaking anyone, and it does not slow down our tests or have other negative impact on our processes. (As stated above, I think it is useful to reproduce and measure this part of datagen, especially since we will be rewriting this code again come CLDR 48.) So I think this PR is landable now. @Manishearth can you take a look? |
Manishearth
left a comment
There was a problem hiding this comment.
Approving based on:
- Shane's assertion in chat that this is not intended to be code read deeply
- Understanding the PR body
- Quickly giving a once over to the code, which is rather complicated and not commented much
- Understanding the actual code has been through multiple review passes already
- Understanding that the open review issue on benchmarks will be resolved later
- Knowing all this code will go away in the future (please ensure the followup CLDR version cleans up as much of this as possible)
|
I want to delete basically this entire file once CLDR 49 is available. #8362 |
I didn't know we could do that! |
Redesigns
CoverageByXPathLevelsinto 4 category-specific ZeroTrie fields (language,territory,script,variant), extracting subtag attribute keys and integer coverage level discriminators.Stores only the short XPath suffixes in the trie and hoists per-locale coverage level lookups outside subtag loops.
Measured performance impact on
test_export_language_identifier_display_names:coverage_tiertrie lookup time reduced from 9.60 s to 2.62 s (3.66x speedup).🤖 This pull request was created by an AI agent working with @sffc.
Changelog
N/A