Skip to content

Avoid temporary serialization for shared-string lookups - #9410

Open
kunaldrafts25 wants to merge 2 commits into
google:masterfrom
kunaldrafts25:perf/shared-string-lookup-before-copy
Open

kunaldrafts25 wants to merge 2 commits into
google:masterfrom
kunaldrafts25:perf/shared-string-lookup-before-copy

Conversation

@kunaldrafts25

Copy link
Copy Markdown

CreateSharedString currently serializes every input before checking its offset pool. On a duplicate, it discards the newly written bytes, but any buffer growth has already happened. For example, a repeated 1 KiB string in a nearly full builder increases the buffer allocation count from one to three even though the returned offset and final buffer size are unchanged.

Use heterogeneous lookup when the standard library supports it (C++14 and newer), comparing the input bytes directly against stored strings. Return the existing offset on a hit, and reuse the lower_bound insertion hint on a miss. Keep the existing implementation for C++11 libraries. Preserve the nesting assertion, exact-length comparison, and empty-input handling.

Add an allocation regression test and benchmarks for duplicate and unique strings. The test also covers empty inputs, prefixes, buffer growth, and reuse after Clear; existing shared-string tests cover embedded NULs.

Validation:

  • Full flattests suites pass with GCC 12.2 in C++17 and C++11; flattests_cpp17 also passes.
  • A separate randomized binary-string round-trip passes ASan and UBSan in C++17, and passes in C++11. Serialized output is byte-identical to the baseline.
  • The standalone allocation reproducer fails against the baseline (one allocation becomes three) and passes with this change (stays at one).
  • Added benchmarks build and run. Timing was noisy in this Docker environment: an initial run showed regressions for the largest duplicate and unique-string cases, while a longer reversed-order run improved all measured cases. The benchmark library reports a DEBUG build warning. These results do not establish a reliable speedup; the allocation regression is deterministic.

@google-cla

google-cla Bot commented Oct 14, 2026

Copy link
Copy Markdown

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@github-actions github-actions Bot added the c++ label Oct 14, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Code Review

This pull request optimizes FlatBufferBuilder::CreateSharedString by utilizing heterogeneous lookup (__cpp_lib_generic_associative_lookup) when available, which prevents temporary serialization and unnecessary buffer growth for duplicate strings. It also includes new benchmarks and unit tests to validate the performance and correctness of this optimization. The feedback suggests using str->data() instead of str->c_str() in the transparent comparison operators to maintain consistency with the existing codebase.

Comment on lines +1397 to +1404
bool operator()(const Offset<String>& a, const StringLookup& b) const {
auto str = reinterpret_cast<const String*>(buf_->data_at(a.o));
return StringLessThan(str->c_str(), str->size(), b.data, b.size);
}
bool operator()(const StringLookup& a, const Offset<String>& b) const {
auto str = reinterpret_cast<const String*>(buf_->data_at(b.o));
return StringLessThan(a.data, a.size, str->c_str(), str->size());
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

For consistency with the existing operator()(const Offset<String>&, const Offset<String>&) which uses stra->data(), please use str->data() instead of str->c_str() in the transparent comparison operators.

    bool operator()(const Offset<String>& a, const StringLookup& b) const {
      auto str = reinterpret_cast<const String*>(buf_->data_at(a.o));
      return StringLessThan(str->data(), str->size(), b.data, b.size);
    }
    bool operator()(const StringLookup& a, const Offset<String>& b) const {
      auto str = reinterpret_cast<const String*>(buf_->data_at(b.o));
      return StringLessThan(a.data, a.size, str->data(), str->size());
    }

This branch has not been deployed

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant