Skip to content

[clang-tidy] Speed up readability-container-contains#175121

Merged
localspook merged 2 commits intollvm:mainfrom
localspook:container-contains-optimization
Jan 26, 2026
Merged

[clang-tidy] Speed up readability-container-contains#175121
localspook merged 2 commits intollvm:mainfrom
localspook:container-contains-optimization

Conversation

@localspook
Copy link
Contributor

@localspook localspook commented Jan 9, 2026

This is currently one of our most expensive checks according to --enable-check-profile. I measured using the usual setup:

hyperfine \
    --shell=none \
    --prepare='cmake --build build/release --target clang-tidy' \
    './build/release/bin/clang-tidy --checks=-*,readability-container-contains all_headers.cpp -header-filter=.* -system-headers -- -std=c++23 -fno-delayed-template-parsing'

First, the status quo:

  Time (mean ± σ):      5.243 s ±  0.158 s    [User: 4.964 s, System: 0.248 s]
  Range (min … max):    5.133 s …  5.612 s    10 runs

This PR improves that in two independent commits. The first commit changes the default traversal mode from TK_AsIs to TK_IgnoreUnlessSpelledInSource. Result:

  Time (mean ± σ):      4.554 s ±  0.100 s    [User: 4.273 s, System: 0.248 s]
  Range (min … max):    4.442 s …  4.785 s    10 runs

The second commit merges all 12 of the check's BinaryOperation matchers into one, because I found that each additional registered BinaryOperation matcher has pretty extreme overhead. Result:

  Time (mean ± σ):      3.480 s ±  0.121 s    [User: 3.264 s, System: 0.186 s]
  Range (min … max):    3.400 s …  3.798 s    10 runs

@localspook localspook marked this pull request as ready for review January 9, 2026 06:05
@localspook localspook requested review from vbvictor, zeyi2 and zwuis January 9, 2026 06:05
@llvmbot
Copy link
Member

llvmbot commented Jan 9, 2026

@llvm/pr-subscribers-clang-tools-extra

Author: Victor Chernyakin (localspook)

Changes

This is currently our most expensive check as measured by --enable-check-profile, tied with altera-id-dependent-backward-branch. I measured using the usual setup:

hyperfine \
    --shell=none \
    --prepare='cmake --build build/release --target clang-tidy' \
    './build/release/bin/clang-tidy --checks=-*,readability-container-contains all_headers.cpp -header-filter=.* -system-headers -- -std=c++23 -fno-delayed-template-parsing'

First, the status quo:

  Time (mean ± σ):      5.243 s ±  0.158 s    [User: 4.964 s, System: 0.248 s]
  Range (min … max):    5.133 s …  5.612 s    10 runs

This PR improves that in two independent commits. The first commit changes the default traversal mode from TK_AsIs to TK_IgnoreUnlessSpelledInSource. Result:

  Time (mean ± σ):      4.554 s ±  0.100 s    [User: 4.273 s, System: 0.248 s]
  Range (min … max):    4.442 s …  4.785 s    10 runs

The second commit merges all 12 of the check's BinaryOperation matchers into one, because I found that simply registering a BinaryOperation matcher has pretty extreme overhead. Result:

  Time (mean ± σ):      3.480 s ±  0.121 s    [User: 3.264 s, System: 0.186 s]
  Range (min … max):    3.400 s …  3.798 s    10 runs

Full diff: https://github.com/llvm/llvm-project/pull/175121.diff

2 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.cpp (+31-51)
  • (modified) clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.h (+1-1)
diff --git a/clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.cpp b/clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.cpp
index efcf13d63b4ff..3b4113c4e2ed0 100644
--- a/clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.cpp
@@ -14,6 +14,7 @@
 using namespace clang::ast_matchers;
 
 namespace clang::tidy::readability {
+
 void ContainerContainsCheck::registerMatchers(MatchFinder *Finder) {
   const auto Literal0 = integerLiteral(equals(0));
   const auto Literal1 = integerLiteral(equals(1));
@@ -47,57 +48,36 @@ void ContainerContainsCheck::registerMatchers(MatchFinder *Finder) {
   const auto StringNpos = anyOf(declRefExpr(to(varDecl(hasName("npos")))),
                                 memberExpr(member(hasName("npos"))));
 
-  auto AddSimpleMatcher = [&](const auto &Matcher) {
-    Finder->addMatcher(traverse(TK_IgnoreUnlessSpelledInSource, Matcher), this);
-  };
-
-  // Find membership tests which use `count()`.
-  Finder->addMatcher(implicitCastExpr(hasImplicitDestinationType(booleanType()),
-                                      hasSourceExpression(CountCall))
-                         .bind("positiveComparison"),
-                     this);
-  AddSimpleMatcher(
-      binaryOperation(hasOperatorName("!="), hasOperands(CountCall, Literal0))
-          .bind("positiveComparison"));
-  AddSimpleMatcher(
-      binaryOperation(hasLHS(CountCall), hasOperatorName(">"), hasRHS(Literal0))
-          .bind("positiveComparison"));
-  AddSimpleMatcher(
-      binaryOperation(hasLHS(Literal0), hasOperatorName("<"), hasRHS(CountCall))
-          .bind("positiveComparison"));
-  AddSimpleMatcher(binaryOperation(hasLHS(CountCall), hasOperatorName(">="),
-                                   hasRHS(Literal1))
-                       .bind("positiveComparison"));
-  AddSimpleMatcher(binaryOperation(hasLHS(Literal1), hasOperatorName("<="),
-                                   hasRHS(CountCall))
-                       .bind("positiveComparison"));
-
-  // Find inverted membership tests which use `count()`.
-  AddSimpleMatcher(
-      binaryOperation(hasOperatorName("=="), hasOperands(CountCall, Literal0))
-          .bind("negativeComparison"));
-  AddSimpleMatcher(binaryOperation(hasLHS(CountCall), hasOperatorName("<="),
-                                   hasRHS(Literal0))
-                       .bind("negativeComparison"));
-  AddSimpleMatcher(binaryOperation(hasLHS(Literal0), hasOperatorName(">="),
-                                   hasRHS(CountCall))
-                       .bind("negativeComparison"));
-  AddSimpleMatcher(
-      binaryOperation(hasLHS(CountCall), hasOperatorName("<"), hasRHS(Literal1))
-          .bind("negativeComparison"));
-  AddSimpleMatcher(
-      binaryOperation(hasLHS(Literal1), hasOperatorName(">"), hasRHS(CountCall))
-          .bind("negativeComparison"));
-
-  // Find membership tests based on `find() == end()` or `find() == npos`.
-  AddSimpleMatcher(
-      binaryOperation(hasOperatorName("!="),
-                      hasOperands(FindCall, anyOf(EndCall, StringNpos)))
-          .bind("positiveComparison"));
-  AddSimpleMatcher(
-      binaryOperation(hasOperatorName("=="),
-                      hasOperands(FindCall, anyOf(EndCall, StringNpos)))
-          .bind("negativeComparison"));
+  Finder->addMatcher(
+      traverse(TK_AsIs,
+               implicitCastExpr(hasImplicitDestinationType(booleanType()),
+                                hasSourceExpression(CountCall))
+                   .bind("positiveComparison")),
+      this);
+
+  const auto PositiveComparison =
+      anyOf(allOf(hasOperatorName("!="), hasOperands(CountCall, Literal0)),
+            allOf(hasLHS(CountCall), hasOperatorName(">"), hasRHS(Literal0)),
+            allOf(hasLHS(Literal0), hasOperatorName("<"), hasRHS(CountCall)),
+            allOf(hasLHS(CountCall), hasOperatorName(">="), hasRHS(Literal1)),
+            allOf(hasLHS(Literal1), hasOperatorName("<="), hasRHS(CountCall)),
+            allOf(hasOperatorName("!="),
+                  hasOperands(FindCall, anyOf(EndCall, StringNpos))));
+
+  const auto NegativeComparison =
+      anyOf(allOf(hasOperatorName("=="), hasOperands(CountCall, Literal0)),
+            allOf(hasLHS(CountCall), hasOperatorName("<="), hasRHS(Literal0)),
+            allOf(hasLHS(Literal0), hasOperatorName(">="), hasRHS(CountCall)),
+            allOf(hasLHS(CountCall), hasOperatorName("<"), hasRHS(Literal1)),
+            allOf(hasLHS(Literal1), hasOperatorName(">"), hasRHS(CountCall)),
+            allOf(hasOperatorName("=="),
+                  hasOperands(FindCall, anyOf(EndCall, StringNpos))));
+
+  Finder->addMatcher(
+      binaryOperation(
+          anyOf(allOf(PositiveComparison, expr().bind("positiveComparison")),
+                allOf(NegativeComparison, expr().bind("negativeComparison")))),
+      this);
 }
 
 void ContainerContainsCheck::check(const MatchFinder::MatchResult &Result) {
diff --git a/clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.h b/clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.h
index 8e058f20427fd..8d043910fc2ba 100644
--- a/clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.h
+++ b/clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.h
@@ -29,7 +29,7 @@ class ContainerContainsCheck : public ClangTidyCheck {
     return LO.CPlusPlus;
   }
   std::optional<TraversalKind> getCheckTraversalKind() const override {
-    return TK_AsIs;
+    return TK_IgnoreUnlessSpelledInSource;
   }
 };
 

@llvmbot
Copy link
Member

llvmbot commented Jan 9, 2026

@llvm/pr-subscribers-clang-tidy

Author: Victor Chernyakin (localspook)

Changes

This is currently our most expensive check as measured by --enable-check-profile, tied with altera-id-dependent-backward-branch. I measured using the usual setup:

hyperfine \
    --shell=none \
    --prepare='cmake --build build/release --target clang-tidy' \
    './build/release/bin/clang-tidy --checks=-*,readability-container-contains all_headers.cpp -header-filter=.* -system-headers -- -std=c++23 -fno-delayed-template-parsing'

First, the status quo:

  Time (mean ± σ):      5.243 s ±  0.158 s    [User: 4.964 s, System: 0.248 s]
  Range (min … max):    5.133 s …  5.612 s    10 runs

This PR improves that in two independent commits. The first commit changes the default traversal mode from TK_AsIs to TK_IgnoreUnlessSpelledInSource. Result:

  Time (mean ± σ):      4.554 s ±  0.100 s    [User: 4.273 s, System: 0.248 s]
  Range (min … max):    4.442 s …  4.785 s    10 runs

The second commit merges all 12 of the check's BinaryOperation matchers into one, because I found that simply registering a BinaryOperation matcher has pretty extreme overhead. Result:

  Time (mean ± σ):      3.480 s ±  0.121 s    [User: 3.264 s, System: 0.186 s]
  Range (min … max):    3.400 s …  3.798 s    10 runs

Full diff: https://github.com/llvm/llvm-project/pull/175121.diff

2 Files Affected:

  • (modified) clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.cpp (+31-51)
  • (modified) clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.h (+1-1)
diff --git a/clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.cpp b/clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.cpp
index efcf13d63b4ff..3b4113c4e2ed0 100644
--- a/clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.cpp
@@ -14,6 +14,7 @@
 using namespace clang::ast_matchers;
 
 namespace clang::tidy::readability {
+
 void ContainerContainsCheck::registerMatchers(MatchFinder *Finder) {
   const auto Literal0 = integerLiteral(equals(0));
   const auto Literal1 = integerLiteral(equals(1));
@@ -47,57 +48,36 @@ void ContainerContainsCheck::registerMatchers(MatchFinder *Finder) {
   const auto StringNpos = anyOf(declRefExpr(to(varDecl(hasName("npos")))),
                                 memberExpr(member(hasName("npos"))));
 
-  auto AddSimpleMatcher = [&](const auto &Matcher) {
-    Finder->addMatcher(traverse(TK_IgnoreUnlessSpelledInSource, Matcher), this);
-  };
-
-  // Find membership tests which use `count()`.
-  Finder->addMatcher(implicitCastExpr(hasImplicitDestinationType(booleanType()),
-                                      hasSourceExpression(CountCall))
-                         .bind("positiveComparison"),
-                     this);
-  AddSimpleMatcher(
-      binaryOperation(hasOperatorName("!="), hasOperands(CountCall, Literal0))
-          .bind("positiveComparison"));
-  AddSimpleMatcher(
-      binaryOperation(hasLHS(CountCall), hasOperatorName(">"), hasRHS(Literal0))
-          .bind("positiveComparison"));
-  AddSimpleMatcher(
-      binaryOperation(hasLHS(Literal0), hasOperatorName("<"), hasRHS(CountCall))
-          .bind("positiveComparison"));
-  AddSimpleMatcher(binaryOperation(hasLHS(CountCall), hasOperatorName(">="),
-                                   hasRHS(Literal1))
-                       .bind("positiveComparison"));
-  AddSimpleMatcher(binaryOperation(hasLHS(Literal1), hasOperatorName("<="),
-                                   hasRHS(CountCall))
-                       .bind("positiveComparison"));
-
-  // Find inverted membership tests which use `count()`.
-  AddSimpleMatcher(
-      binaryOperation(hasOperatorName("=="), hasOperands(CountCall, Literal0))
-          .bind("negativeComparison"));
-  AddSimpleMatcher(binaryOperation(hasLHS(CountCall), hasOperatorName("<="),
-                                   hasRHS(Literal0))
-                       .bind("negativeComparison"));
-  AddSimpleMatcher(binaryOperation(hasLHS(Literal0), hasOperatorName(">="),
-                                   hasRHS(CountCall))
-                       .bind("negativeComparison"));
-  AddSimpleMatcher(
-      binaryOperation(hasLHS(CountCall), hasOperatorName("<"), hasRHS(Literal1))
-          .bind("negativeComparison"));
-  AddSimpleMatcher(
-      binaryOperation(hasLHS(Literal1), hasOperatorName(">"), hasRHS(CountCall))
-          .bind("negativeComparison"));
-
-  // Find membership tests based on `find() == end()` or `find() == npos`.
-  AddSimpleMatcher(
-      binaryOperation(hasOperatorName("!="),
-                      hasOperands(FindCall, anyOf(EndCall, StringNpos)))
-          .bind("positiveComparison"));
-  AddSimpleMatcher(
-      binaryOperation(hasOperatorName("=="),
-                      hasOperands(FindCall, anyOf(EndCall, StringNpos)))
-          .bind("negativeComparison"));
+  Finder->addMatcher(
+      traverse(TK_AsIs,
+               implicitCastExpr(hasImplicitDestinationType(booleanType()),
+                                hasSourceExpression(CountCall))
+                   .bind("positiveComparison")),
+      this);
+
+  const auto PositiveComparison =
+      anyOf(allOf(hasOperatorName("!="), hasOperands(CountCall, Literal0)),
+            allOf(hasLHS(CountCall), hasOperatorName(">"), hasRHS(Literal0)),
+            allOf(hasLHS(Literal0), hasOperatorName("<"), hasRHS(CountCall)),
+            allOf(hasLHS(CountCall), hasOperatorName(">="), hasRHS(Literal1)),
+            allOf(hasLHS(Literal1), hasOperatorName("<="), hasRHS(CountCall)),
+            allOf(hasOperatorName("!="),
+                  hasOperands(FindCall, anyOf(EndCall, StringNpos))));
+
+  const auto NegativeComparison =
+      anyOf(allOf(hasOperatorName("=="), hasOperands(CountCall, Literal0)),
+            allOf(hasLHS(CountCall), hasOperatorName("<="), hasRHS(Literal0)),
+            allOf(hasLHS(Literal0), hasOperatorName(">="), hasRHS(CountCall)),
+            allOf(hasLHS(CountCall), hasOperatorName("<"), hasRHS(Literal1)),
+            allOf(hasLHS(Literal1), hasOperatorName(">"), hasRHS(CountCall)),
+            allOf(hasOperatorName("=="),
+                  hasOperands(FindCall, anyOf(EndCall, StringNpos))));
+
+  Finder->addMatcher(
+      binaryOperation(
+          anyOf(allOf(PositiveComparison, expr().bind("positiveComparison")),
+                allOf(NegativeComparison, expr().bind("negativeComparison")))),
+      this);
 }
 
 void ContainerContainsCheck::check(const MatchFinder::MatchResult &Result) {
diff --git a/clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.h b/clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.h
index 8e058f20427fd..8d043910fc2ba 100644
--- a/clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.h
+++ b/clang-tools-extra/clang-tidy/readability/ContainerContainsCheck.h
@@ -29,7 +29,7 @@ class ContainerContainsCheck : public ClangTidyCheck {
     return LO.CPlusPlus;
   }
   std::optional<TraversalKind> getCheckTraversalKind() const override {
-    return TK_AsIs;
+    return TK_IgnoreUnlessSpelledInSource;
   }
 };
 

@vbvictor
Copy link
Contributor

vbvictor commented Jan 9, 2026

To give better perspective, what is speedup according to --enable-check-profile?

If I understand correctly, running hyperfine clang-tidy will include both c++ frontend and clang-tidy parts. Usually frontend takes a lot, so having dedicated check time would be very useful

@localspook
Copy link
Contributor Author

Before:

   ---User Time---   --System Time--   --User+System--   ---Wall Time---  --- Name ---
   2.1875 (100.0%)   0.0938 (100.0%)   2.2812 (100.0%)   2.2609 (100.0%)  readability-container-contains

After:

   ---User Time---   --System Time--   --User+System--   ---Wall Time---  --- Name ---
   0.2188 (100.0%)   0.0781 (100.0%)   0.2969 (100.0%)   0.3131 (100.0%)  readability-container-contains

@EugeneZelenko
Copy link
Contributor

Should pull request be marked as NFC?

@localspook
Copy link
Contributor Author

localspook commented Jan 9, 2026

This feels like a gray area: is a performance change a "functional" change? LLVM docs define NFC as "pure refactoring/cleanup", so it seems this isn't NFC, but it would be nice if it was more explicit. More discussion here: https://groups.google.com/g/llvm-dev/c/AXCgjDwcWnQ

Update: Discord discussion yields conflicting opinions!
изображение

Copy link
Member

@zeyi2 zeyi2 left a comment

Choose a reason for hiding this comment

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

The code part LGTM, thank you!

Regarding the commit message: personally I don't think this is a NFC change, so I'm fine with the current PR title/message :)

@vbvictor
Copy link
Contributor

vbvictor commented Jan 11, 2026

We changed AS-IS to IgnoreUnless.. but there are no tests with template functions:

template <typename T, U>
void foo(map<T, U> m) {
  m.size() = ...
}

Can we add them? (I think we should add them before your change and then see if everything still pass)


The speedup is impressive in terms of check speed! I think we should look at --enable-check-profile data rather then full clang-tidy time

@localspook
Copy link
Contributor Author

The check currently doesn't work with templated containers: https://godbolt.org/z/9Ts9d9EcG
Supporting them would require a heuristic like what bugprone-standalone-empty uses:

auto Candidates = HeuristicResolver(Context).lookupDependentName(
MemberCall->getRecordDecl(), Name, [](const NamedDecl *ND) {
return isa<CXXMethodDecl>(ND) &&
llvm::cast<CXXMethodDecl>(ND)->getMinRequiredArguments() ==
0 &&
!llvm::cast<CXXMethodDecl>(ND)->isConst();
});
const bool HasClear = !Candidates.empty();

@vbvictor
Copy link
Contributor

The tests currently seems broken; take a look at https://godbolt.org/z/6bKG3Y8Y3.

The last warning is line 453, and in actual tests we should have 487 line

@vbvictor
Copy link
Contributor

vbvictor commented Jan 11, 2026

And the check doesn't work if we use actual map instead of fake one:
real map - https://godbolt.org/z/WcfqazWeG
fake map - https://godbolt.org/z/xxWdzGs9d

And If you instantiate the function, it works with the template.

@localspook
Copy link
Contributor Author

And the check doesn't work if we use actual map instead of fake one:

This seems to be because of the unfortunate fact that std::map only got a contains function in C++20:
https://en.cppreference.com/w/cpp/container/map/contains.html
https://godbolt.org/z/T7K4cY4ar

@localspook
Copy link
Contributor Author

localspook commented Jan 11, 2026

The tests currently seems broken; take a look at https://godbolt.org/z/6bKG3Y8Y3.

The last warning is line 453, and in actual tests we should have 487 line

And std::string and std::string_view only in C++23 (!!):
https://en.cppreference.com/w/cpp/string/basic_string/contains.html
https://godbolt.org/z/T7TrqT363

@vbvictor
Copy link
Contributor

Yes, really. I should postpone all reviews till the end of vacation...

But, with instantiation templates still works https://godbolt.org/z/hMP6E85q7.
Maybe we can change traversal kind later?

@localspook
Copy link
Contributor Author

localspook commented Jan 11, 2026

And If you instantiate the function, it works with the template.

This PR breaks that case, true. But I hope we can agree that the current behaviour is still not ideal, right? We would like the check to work whether you instantiate the template or not. That would require another PR implementing heuristic name resolution (and I would be happy to write it!).

Maybe we can change traversal kind later?

I think it would be less work in the end to leave this PR as-is, go implement the heuristic, then come back here: does that sound like a plan?

@vbvictor
Copy link
Contributor

vbvictor commented Jan 11, 2026

This PR breaks that case, true. But I hope we can agree that the current behaviour is still not ideal, right? We would like the check to work whether you instantiate the template or not. That would require another PR implementing heuristic name resolution (and I would be happy to write it!).

I think it's not perfectly ideal, but good enough for 99.9..% cases. I think we have dozens of other checks that operate with same logic involving templates. (To find exact number need grep instantiate() in test dir).
So if in this case we get a lot of perf benefit - worth doing IgnoreUnless.., otherwise I personally wouldn't bother much about imperfections.

I think it would be less work in the end to leave this PR as-is, go implement the heuristic.

This sounds good to me.


I don't like merging with just IgnoreUnless because it may brong regression to library template code that is usually instantiated to concrete types in the end e.g. in tests.

Copy link
Contributor

@zwuis zwuis left a comment

Choose a reason for hiding this comment

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

LGTM.

I'm neutral about the commit title (with or without NFC).

And If you instantiate the function, it works with the template.

This PR breaks that case, true. ...

I don't get it. How the case is broken? Changing traversal mode? Did I miss anything?

We changed AS-IS to IgnoreUnless.. ...

See lambda AddSimpleMatcher deleted by this PR, which uses TK_IgnoreUnlessSpelledInSource. Traversal mode of each matcher doesn't change.

@vbvictor
Copy link
Contributor

See lambda AddSimpleMatcher deleted by this PR, which uses TK_IgnoreUnlessSpelledInSource. Traversal mode of each matcher doesn't change.

Oh, actually I missed that we already had IgnoreUnless in AddSimpleMatcher. So this could be an NFC change.

I don't get it. How the case is broken? Changing traversal mode? Did I miss anything?

As I understand, we didn't break anything with current patch. However, the check had FN before: e.g. https://godbolt.org/z/459M3EaTe: even if returnContains is instantiated inside main function, it doesn't give warning there. If the matcher used AsIs mode, then we would should get the warning.

Copy link
Contributor

@vbvictor vbvictor left a comment

Choose a reason for hiding this comment

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

LGTM, since this patch doesn't break anything - we already have FN on trunk

@localspook
Copy link
Contributor Author

localspook commented Jan 26, 2026

Oh yeah, looks like I was confused: I think I saw that the check flags the “conversion to bool” pattern even in templates, and assumed that it works 100% even in templates, but that’s not the case. I agree now, this change preserves behavior.

@localspook localspook merged commit 51f4c5e into llvm:main Jan 26, 2026
16 checks passed
@localspook localspook deleted the container-contains-optimization branch January 26, 2026 19:11
stomfaig pushed a commit to stomfaig/llvm-project that referenced this pull request Jan 28, 2026
This is currently one of our most expensive checks according to
`--enable-check-profile`. I measured using [the usual
setup](llvm#174357 (comment)):
```sh
hyperfine \
    --shell=none \
    --prepare='cmake --build build/release --target clang-tidy' \
    './build/release/bin/clang-tidy --checks=-*,readability-container-contains all_headers.cpp -header-filter=.* -system-headers -- -std=c++23 -fno-delayed-template-parsing'
```

First, the status quo:
```txt
  Time (mean ± σ):      5.243 s ±  0.158 s    [User: 4.964 s, System: 0.248 s]
  Range (min … max):    5.133 s …  5.612 s    10 runs
```
This PR improves that in two independent commits. The first commit
changes the default traversal mode from `TK_AsIs` to
`TK_IgnoreUnlessSpelledInSource`. Result:
```txt
  Time (mean ± σ):      4.554 s ±  0.100 s    [User: 4.273 s, System: 0.248 s]
  Range (min … max):    4.442 s …  4.785 s    10 runs
```

The second commit merges all 12 of the check's `BinaryOperation`
matchers into one, because I found that each additional registered
`BinaryOperation` matcher has pretty extreme overhead. Result:
```txt
  Time (mean ± σ):      3.480 s ±  0.121 s    [User: 3.264 s, System: 0.186 s]
  Range (min … max):    3.400 s …  3.798 s    10 runs
```
Icohedron pushed a commit to Icohedron/llvm-project that referenced this pull request Jan 29, 2026
This is currently one of our most expensive checks according to
`--enable-check-profile`. I measured using [the usual
setup](llvm#174357 (comment)):
```sh
hyperfine \
    --shell=none \
    --prepare='cmake --build build/release --target clang-tidy' \
    './build/release/bin/clang-tidy --checks=-*,readability-container-contains all_headers.cpp -header-filter=.* -system-headers -- -std=c++23 -fno-delayed-template-parsing'
```

First, the status quo:
```txt
  Time (mean ± σ):      5.243 s ±  0.158 s    [User: 4.964 s, System: 0.248 s]
  Range (min … max):    5.133 s …  5.612 s    10 runs
```
This PR improves that in two independent commits. The first commit
changes the default traversal mode from `TK_AsIs` to
`TK_IgnoreUnlessSpelledInSource`. Result:
```txt
  Time (mean ± σ):      4.554 s ±  0.100 s    [User: 4.273 s, System: 0.248 s]
  Range (min … max):    4.442 s …  4.785 s    10 runs
```

The second commit merges all 12 of the check's `BinaryOperation`
matchers into one, because I found that each additional registered
`BinaryOperation` matcher has pretty extreme overhead. Result:
```txt
  Time (mean ± σ):      3.480 s ±  0.121 s    [User: 3.264 s, System: 0.186 s]
  Range (min … max):    3.400 s …  3.798 s    10 runs
```
sshrestha-aa pushed a commit to sshrestha-aa/llvm-project that referenced this pull request Feb 4, 2026
This is currently one of our most expensive checks according to
`--enable-check-profile`. I measured using [the usual
setup](llvm#174357 (comment)):
```sh
hyperfine \
    --shell=none \
    --prepare='cmake --build build/release --target clang-tidy' \
    './build/release/bin/clang-tidy --checks=-*,readability-container-contains all_headers.cpp -header-filter=.* -system-headers -- -std=c++23 -fno-delayed-template-parsing'
```

First, the status quo:
```txt
  Time (mean ± σ):      5.243 s ±  0.158 s    [User: 4.964 s, System: 0.248 s]
  Range (min … max):    5.133 s …  5.612 s    10 runs
```
This PR improves that in two independent commits. The first commit
changes the default traversal mode from `TK_AsIs` to
`TK_IgnoreUnlessSpelledInSource`. Result:
```txt
  Time (mean ± σ):      4.554 s ±  0.100 s    [User: 4.273 s, System: 0.248 s]
  Range (min … max):    4.442 s …  4.785 s    10 runs
```

The second commit merges all 12 of the check's `BinaryOperation`
matchers into one, because I found that each additional registered
`BinaryOperation` matcher has pretty extreme overhead. Result:
```txt
  Time (mean ± σ):      3.480 s ±  0.121 s    [User: 3.264 s, System: 0.186 s]
  Range (min … max):    3.400 s …  3.798 s    10 runs
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants