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

Recursive call stack makes release build 4x slower #73336

Open
JohelEGP opened this issue Nov 24, 2023 · 3 comments
Open

Recursive call stack makes release build 4x slower #73336

JohelEGP opened this issue Nov 24, 2023 · 3 comments

Comments

@JohelEGP
Copy link

In analyzing cppfront with Clang Build Analyzer, I noticed a very odd item:

**** Functions that took longest to compile:
  8037 ms: cpp2::primary_expression_node::is_fold_expression() const (/home/johel/Documents/C++/Forks/hsutter/waarudo/source/cppfront.cpp)
   417 ms: void cpp2::iteration_statement_node::visit<cpp2::sema>(cpp2::sema&, ... (/home/johel/Documents/C++/Forks/hsutter/waarudo/source/cppfront.cpp)

These are only the first two items.
primary_expression_node::is_fold_expression is exorbitantly expensive to compile.

I have a matrix of compilers (Clang, GCC) × configurations (Debug, Release).
Only Clang Release is affected, its compilation time increasing by 145 s.

Normally:

Configuration Clang 18 GCC 14
Debug 25 s 47 s
Release 199 s 95 s

With the patch below:

Configuration Clang 18 GCC 14
Debug 25 s 47 s
Release 54 s 95 s

This git patch removes the offending call that makes it expensive to optimize.

diff --git a/source/parse.h b/source/parse.h
index 103f9ce..bae5e7f 100644
--- a/source/parse.h
+++ b/source/parse.h
@@ -1556,7 +1556,7 @@ auto primary_expression_node::is_fold_expression() const
     break;case identifier:
         return *std::get<identifier>(expr) == "...";
     break;case expression_list:
-        return std::get<expression_list>(expr)->is_fold_expression();
+        return false; // std::get<expression_list>(expr)->is_fold_expression();
     break;case id_expression:
         return std::get<id_expression>(expr)->is_fold_expression();
     break;default: ; // the others can't contain folds

Analyzing with this patch drops the offending function from the default output.

I suspect this is due to the recursive nature of primary_expression_node::is_fold_expression.
This is the stack when first breaking at the offending call while debugging regression-tests/pure2-print.cpp2.
1700837777
From level 18 to 1, those are all calls to is_fold_expression members.
And if primary_expression_node contains an expression_list_node, that can repeat, ad infinitum.

@JohelEGP
Copy link
Author

This is a flame graph of the build with a Release configuration of Clang.
Unfortunately, I was unable to build a Debug configuration of Clang.
1700918842

@hsutter
Copy link

hsutter commented Dec 10, 2023

Thanks @JohelEGP
That's strange... FWIW, on my laptop, a full compile of cppfront using GCC 10 and Clang 12 takes 17 seconds (each) with default optimizations, 25 and 33 seconds (respectively) with -O2.
Update: And MSVC 2022 takes 6 seconds in debug and 16 seconds with -O2.

@JohelEGP
Copy link
Author

Maybe it's a regression by a more recent combination of LLVM version and C++ standard version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants