Skip to content

Commit d09b052

Browse files
committed
Add some simple timer profiling support
1 parent 4b1b255 commit d09b052

File tree

2 files changed

+169
-9
lines changed

2 files changed

+169
-9
lines changed

source/common.h

Lines changed: 148 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,18 @@
3030
#ifndef CPP2_COMMON_H
3131
#define CPP2_COMMON_H
3232

33-
#include <string>
34-
#include <string_view>
35-
#include <vector>
36-
#include <cstdint>
37-
#include <cctype>
33+
#include <algorithm>
3834
#include <cassert>
39-
#include <iomanip>
35+
#include <cctype>
36+
#include <chrono>
4037
#include <compare>
41-
#include <algorithm>
38+
#include <cstdint>
39+
#include <iomanip>
40+
#include <map>
41+
#include <string>
42+
#include <string_view>
4243
#include <unordered_map>
44+
#include <vector>
4345

4446
namespace cpp2 {
4547

@@ -971,6 +973,14 @@ struct error_entry
971973
//-----------------------------------------------------------------------
972974
//
973975

976+
//-----------------------------------------------------------------------
977+
//
978+
// stackinstr: builds debug information to find causes of large stacks
979+
//
980+
// Useful if we need to optimize deep recursion to use less stack
981+
//
982+
//-----------------------------------------------------------------------
983+
//
974984
class stackinstr
975985
{
976986
struct entry
@@ -1045,6 +1055,137 @@ std::vector<stackinstr::entry> stackinstr::largest;
10451055
#define STACKINSTR stackinstr::guard _s_guard{ __func__, __FILE__, __LINE__, reinterpret_cast<char*>(&_s_guard) };
10461056

10471057

1058+
//-----------------------------------------------------------------------
1059+
//
1060+
// timer: a little profiling timer to measure time spent in
1061+
// specific sections of code
1062+
//
1063+
// This is a tool for cppfront developers to measure the run-time of
1064+
// specific parts of cppfront code.
1065+
//
1066+
// Do still use a profiler too. But once you know where to measure,
1067+
// this has some advantages:
1068+
//
1069+
// - Precise: It's easy to accurately measure specific parts of
1070+
// code across the run of the program, not relying on sampling to
1071+
// representatively hit those regions
1072+
//
1073+
// - Flexible: You can have as many timers as you want, each with
1074+
// a clearly readable name, including nested timers (see example
1075+
// below)
1076+
//
1077+
// - Portable: It's part of cppfront itself, no separate tool needed
1078+
//
1079+
// - Integrated: Reports are printed automatically by -verbose
1080+
//
1081+
// Usage guide
1082+
// -----------
1083+
//
1084+
// To measure the total time spent in a specific section of code, create
1085+
// a scope_timer guard object in that scope and give it a readable
1086+
// string name that will remind you what it's measuring.
1087+
//
1088+
// Example: Today I wanted to measure how long get_declaration_of is
1089+
// taking. So add this line at the start of that function's body:
1090+
//
1091+
// auto guard = scope_timer("get_declaration_of");
1092+
//
1093+
// Recompile cppfront, then run with -verbose:
1094+
//
1095+
// cppfront pure2-last-use.cpp2 -p -verb
1096+
//
1097+
// Sample output:
1098+
//
1099+
// pure2-last-use.cpp2... ok (all Cpp2, passes safety checks)
1100+
// Cpp1 0 lines
1101+
// Cpp2 1,050 lines (100%)
1102+
// Time 1,629 ms
1103+
// 1,120 ms in get_declaration_of
1104+
//
1105+
// The first "Time:" is the total run time of the compiland. It is then
1106+
// followed by any active scope_timer names, in alphabetical order
1107+
// (you can choose names so that related timers appear together).
1108+
//
1109+
// Here, this one function is taking most of the total runtime. It
1110+
// consists of two major loops, one run before the other, so we can
1111+
// measure the cost of each part, using names that will be
1112+
// reported together and in understandable order with -verbose:
1113+
//
1114+
// - The function's first loop is to find a starting point, so
1115+
// enclose that in { } with a scope_timer:
1116+
//
1117+
// {
1118+
// auto guard = scope_timer("get_declaration_of step 1, initial find loop");
1119+
// /* the code I want to measure */
1120+
// }
1121+
//
1122+
// - Immediately after that, install a second timer to measure the
1123+
// second loop which covers the entire rest of the function body:
1124+
//
1125+
// auto guard2 = scope_timer("get_declaration_of step 2, rest of lookup");
1126+
// /* followed by the rest of the function's body */
1127+
//
1128+
// - And, since it's easy, throw in a third timer to measure one
1129+
// sub-part of the step 2 loop... right before the 'move this'
1130+
// branch we can add this to measure the time spent in oly that
1131+
// trailing 1/3 of each loop iteration (starting and stopping the
1132+
// timer at those points in each loop iteration to measure just
1133+
// the sum of all those loop fragments):
1134+
//
1135+
// auto guard = scope_timer("get_declaration_of step 2b, 'move this' branch");
1136+
//
1137+
// Recompile cppfront and run again with -verbose... sample output:
1138+
//
1139+
// pure2-last-use.cpp2... ok (all Cpp2, passes safety checks)
1140+
// Cpp1 0 lines
1141+
// Cpp2 1,050 lines (100%)
1142+
// Time 1,471 ms
1143+
// 1,139 ms in get_declaration_of
1144+
// 540 ms in get_declaration_of step 1, initial find loop
1145+
// 567 ms in get_declaration_of step 2, rest of lookup
1146+
// 148 ms in get_declaration_of step 2b, 'move this' branch
1147+
//
1148+
//-----------------------------------------------------------------------
1149+
//
1150+
class timer
1151+
{
1152+
using clock = std::chrono::high_resolution_clock;
1153+
1154+
bool running = false;
1155+
std::chrono::time_point<clock> start_tm = clock::now();
1156+
std::chrono::duration<double> duration = {};
1157+
1158+
public:
1159+
auto start() {
1160+
testing.enforce( !running );
1161+
running = true;
1162+
start_tm = clock::now();
1163+
}
1164+
1165+
auto stop() {
1166+
testing.enforce( running );
1167+
running = false;
1168+
duration += clock::now() - start_tm;
1169+
}
1170+
1171+
void reset() {
1172+
duration = {};
1173+
}
1174+
1175+
auto elapsed() const {
1176+
testing.enforce( !running );
1177+
return std::chrono::duration_cast<std::chrono::milliseconds>(duration);
1178+
}
1179+
};
1180+
1181+
static std::map<std::string, timer> timers; // global named timers
1182+
1183+
auto scope_timer(std::string const& name) {
1184+
timers[name].start();
1185+
return finally( [=]{ timers[name].stop(); } );
1186+
}
1187+
1188+
10481189
}
10491190

10501191
#endif

source/cppfront.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ auto main(
5757
int exit_status = EXIT_SUCCESS;
5858
for (auto const& arg : cmdline.arguments())
5959
{
60+
cpp2::timer t;
61+
t.start();
62+
6063
auto& out = flag_cpp1_filename != "stdout" ? std::cout : std::cerr;
6164

6265
if (!flag_quiet) {
@@ -85,9 +88,14 @@ auto main(
8588
}
8689

8790
if (flag_verbose) {
88-
out << " Cpp1: " << print_with_thousands(count.cpp1_lines) << " line" << (count.cpp1_lines != 1 ? "s" : "");
89-
out << "\n Cpp2: " << print_with_thousands(count.cpp2_lines) << " line" << (count.cpp2_lines != 1 ? "s" : "");
9091
auto total = count.cpp1_lines + count.cpp2_lines;
92+
auto total_lines = print_with_thousands(total);
93+
out << " Cpp1 "
94+
<< std::right << std::setw(total_lines.size())
95+
<< print_with_thousands(count.cpp1_lines) << " line" << (count.cpp1_lines != 1 ? "s" : "");
96+
out << "\n Cpp2 "
97+
<< std::right << std::setw(total_lines.size())
98+
<< print_with_thousands(count.cpp2_lines) << " line" << (count.cpp2_lines != 1 ? "s" : "");
9199
if (total > 0) {
92100
out << " (";
93101
if (count.cpp1_lines == 0) {
@@ -102,6 +110,17 @@ auto main(
102110
}
103111
out << "%)";
104112
}
113+
114+
t.stop();
115+
auto total_time = print_with_thousands(t.elapsed().count());
116+
std::cout << "\n Time " << total_time << " ms";
117+
118+
for (auto [name, t] : timers) {
119+
std::cout
120+
<< "\n "
121+
<< std::right << std::setw(total_time.size())
122+
<< print_with_thousands(t.elapsed().count()) << " ms" << " in " << name;
123+
}
105124
}
106125

107126
out << "\n";

0 commit comments

Comments
 (0)