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

Introduce string map class and switch to it #4774

Merged
merged 15 commits into from
Jul 13, 2024
Merged

Introduce string map class and switch to it #4774

merged 15 commits into from
Jul 13, 2024

Conversation

asl
Copy link
Contributor

@asl asl commented Jul 2, 2024

The main change of this PR is a new string_map<V> class that does ordered_map<cstring, V> but in a proper way:

  • Key are stored as cstrings in the underlying abseil hash map.
  • Heterogenous lookup (with std::string_view keys) is supported. Special care is done not to create cstrings in case if they are not in the map already
  • For now map is ordered, values are stored in std::list, similar to ordered_map. Likely we'd need to allow std::vector / absl::inlined_vector as storage as well via dedicated policy, but I need to collect more statistics on this
  • Several ordered_map construction bugs were fixed as well

Give this class, I moved two main users of ordered_map<cstring, V> to it:

  • IDeclaration
  • JSonObject
    trying to refactor & modernize the API to allow & use heterogenous lookup as well.

While there I checked all instances of std::map<cstring, something in frontend / midend and replaced them with unordered maps where appropriate.

While it might look like a breaking change, it is not. Even more, the code eases adopting of downstream code to explicit const char* => cstring conversion, as heterogenous lookup makes these conversions unnecessary. I moved code in backends to this where appropriate.

Fixes #4763

@asl asl requested review from grg, vlstill and fruffy July 2, 2024 07:08
@asl
Copy link
Contributor Author

asl commented Jul 2, 2024

For the downstream testcase the compile time changes from:

Executed in   70.79 secs    fish           external
   usr time  107.65 secs    0.05 millis  107.65 secs
   sys time    3.89 secs    1.44 millis    3.89 secs

down to:

Executed in   63.48 secs    fish           external
   usr time   81.99 secs    0.09 millis   81.99 secs
   sys time    3.71 secs    5.54 millis    3.70 secs

So we are having nice 10% speedup. Two things contribute to this change:

  • Removal of strcmp in IDeclaration's ordered_map
  • Much less malloc traffic there.

@asl asl added run-validation Use this tag to trigger a Validation CI run. run-sanitizer Use this tag to run a Clang+Sanitzers CI run. core Topics concerning the core segments of the compiler (frontend, midend, parser) labels Jul 2, 2024
@asl asl force-pushed the cstring-map branch 2 times, most recently from f78ad80 to 099b9bf Compare July 2, 2024 07:22
lib/string_map.h Outdated Show resolved Hide resolved
@vlstill
Copy link
Contributor

vlstill commented Jul 2, 2024

For now map is ordered, values are stored in std::list, similar to ordered_map. Likely we'd need to allow std::vector / absl::inlined_vector as storage as well via dedicated policy, but I need to collect more statistics on this

Or we could just always use std::vector. I doubt std::list will ever be better. Alternatively, if the iteration is rare, some sort of intrusive (singly-) linked list might be usable (I hope noone is reverse iterating over these).

Copy link
Contributor

@vlstill vlstill left a comment

Choose a reason for hiding this comment

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

For now, I went through the data structure implementation. Thank you for your contribution!

lib/string_map.h Show resolved Hide resolved
lib/string_map.h Outdated Show resolved Hide resolved
lib/string_map.h Outdated Show resolved Hide resolved
lib/string_map.h Outdated Show resolved Hide resolved
lib/string_map.h Outdated Show resolved Hide resolved
lib/string_map.h Outdated Show resolved Hide resolved
lib/string_map.h Outdated Show resolved Hide resolved
@asl
Copy link
Contributor Author

asl commented Jul 2, 2024

Or we could just always use std::vector. I doubt std::list will ever be better. Alternatively, if the iteration is rare, some sort of intrusive (singly-) linked list might be usable (I hope noone is reverse iterating over these).

Yes. std::list definitely has much more malloc traffic overhead. And this case is certainly optimized for often erasures. For IDeclaration looks like an intrusive vector would be preferable. For JSonObject – likely the usual std::vector. Anyway, this is something I am planning to measure and play with :)

Copy link
Contributor

@vlstill vlstill left a comment

Choose a reason for hiding this comment

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

Few nitpicks in the map implementation.

lib/string_map.h Outdated Show resolved Hide resolved
lib/string_map.h Outdated Show resolved Hide resolved
lib/string_map.h Outdated Show resolved Hide resolved
lib/string_map.h Outdated Show resolved Hide resolved
lib/string_map.h Show resolved Hide resolved
Copy link
Contributor

@vlstill vlstill left a comment

Choose a reason for hiding this comment

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

A few comments for the rest of the code. Once we have C++20, we should try to revisit if we can replace the overloads that are the same for cstring and std::string_view with a single one taking a concept that is either. But I am not sure if GCC 9.2 has enough concepts support to allow that or if we would need to wait until we can drop it.

@@ -21,7 +21,7 @@ namespace P4 {
bool CheckNamedArgs::checkArguments(const IR::Vector<IR::Argument> *arguments) {
bool first = true;
bool hasName = false;
std::map<cstring, const IR::Argument *> found;
absl::flat_hash_map<cstring, const IR::Argument *> found;
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we use the hasher from Utils?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It will be used via our std::hash specialization. So, nothing special is needed. Though, abseil on top would salt the hash, so the iteration order will be randomized. I do not have preferences which approach is better.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After thinking about this a bit more, maybe we can adopt the following rule:

  • For local maps (within the method, etc) we can use Util::Hash directly. This way we won't have additional random salt on top of the existing hash, so the hashing latency will be reduced.
  • For members we'd stick with absel's randomized hashes. This way the iteration order will be different on each invocation. As a result, all possible accidentally added iterations could be noticed much earlier.

What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So, I implemented this approach for local maps. Also, found one "funny" case when we had map cstring -> ID which I replaces with set.

ir/v1.def Show resolved Hide resolved
lib/cstring.cpp Outdated Show resolved Hide resolved
lib/json.h Show resolved Hide resolved
test/gtest/cstring.cpp Outdated Show resolved Hide resolved
test/gtest/string_map.cpp Outdated Show resolved Hide resolved
@asl
Copy link
Contributor Author

asl commented Jul 3, 2024

@vlstill I factored out cstring bits into #4780 to decompose the changes

@ChrisDodd
Copy link
Contributor

For the downstream testcase the compile time changes from:

Executed in   70.79 secs    fish           external
   usr time  107.65 secs    0.05 millis  107.65 secs
   sys time    3.89 secs    1.44 millis    3.89 secs

down to:

Executed in   63.48 secs    fish           external
   usr time   81.99 secs    0.09 millis   81.99 secs
   sys time    3.71 secs    5.54 millis    3.70 secs

So we are having nice 10% speedup. Two things contribute to this change:

  • Removal of strcmp in IDeclaration's ordered_map
  • Much less malloc traffic there.

How does the performance compare to hvec_map?

@asl
Copy link
Contributor Author

asl commented Jul 3, 2024

How does the performance compare to hvec_map?

Its performance was lower in my initial tests. Checked once again to ensure we're comparing apples-to-apples replacing declaration map in IndexedVector with hvec_map<cstring, IDeclaration*>:

Executed in   65.31 secs    fish           external
   usr time  101.39 secs    0.10 millis  101.39 secs
   sys time    1.95 secs    2.28 millis    1.95 secs

@asl
Copy link
Contributor Author

asl commented Jul 5, 2024

Rebased after #4780 merged

@asl asl force-pushed the cstring-map branch 2 times, most recently from c0a2be1 to d3cd689 Compare July 9, 2024 21:32
@asl asl requested a review from vlstill July 9, 2024 22:08
asl added 4 commits July 12, 2024 12:13
…ing, ...>:

  - Underlying map is abseil's unordered map
  - Supports heterogenous lookups with std::string_view keys
  - Several ordered_map<cstring, ...> bugs are fixed

Signed-off-by: Anton Korobeynikov <[email protected]>
@asl
Copy link
Contributor Author

asl commented Jul 12, 2024

@vlstill @fruffy Anything else left to address?

@asl asl requested a review from fruffy July 12, 2024 19:14
Copy link
Collaborator

@fruffy fruffy left a comment

Choose a reason for hiding this comment

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

LGTM

@asl asl added this pull request to the merge queue Jul 12, 2024
Merged via the queue into p4lang:main with commit 0ccd7c6 Jul 13, 2024
17 checks passed
@asl asl deleted the cstring-map branch July 13, 2024 00:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
core Topics concerning the core segments of the compiler (frontend, midend, parser) run-sanitizer Use this tag to run a Clang+Sanitzers CI run. run-validation Use this tag to trigger a Validation CI run.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

ordered_map<cstring, ...> fiasko
4 participants