Skip to content

Commit

Permalink
kram-profile - collapsed template name support
Browse files Browse the repository at this point in the history
  • Loading branch information
alecazam committed Apr 1, 2024
1 parent 54774b4 commit 70d8323
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 36 deletions.
1 change: 0 additions & 1 deletion kram-profile/CBA/CBA.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#import "Foundation/Foundation.h"

// TODO: move to header
@interface CBA : NSObject

- (_Nonnull instancetype)init;
Expand Down
3 changes: 0 additions & 3 deletions kram-profile/CBA/CBA.mm
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,5 @@ - (NSString* _Nonnull)analyze:(NSArray<NSString*> * _Nonnull)filenames {
return [NSString stringWithUTF8String:out.c_str()];
}




@end

66 changes: 66 additions & 0 deletions kram-profile/Source/KramZipHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,72 @@ int32_t append_sprintf(string& str, const char* format, ...)
return len;
}

// This is extracted from CBA Analysis.cpp
extern "C" const char* _Nullable collapseFunctionName(const char* _Nonnull name_) {
// Adapted from code in Analysis. Really the only call needed from CBA.
// serialize to multiple threads
static mutex sMutex;
static unordered_map<string, string> sMap;
lock_guard<mutex> lock(sMutex);

string elt(name_);
auto it = sMap.find(elt);
if (it != sMap.end()) {
return it->second.c_str();
}

// Parsing op<, op<<, op>, and op>> seems hard. Just skip'm all
if (strstr(name_, "operator") != nullptr)
return nullptr;

std::string retval;
retval.reserve(elt.size());
auto b_range = elt.begin();
auto e_range = elt.begin();
while (b_range != elt.end())
{
e_range = std::find(b_range, elt.end(), '<');
if (e_range == elt.end())
break;
++e_range;
retval.append(b_range, e_range);
retval.append("$");
b_range = e_range;
int open_count = 1;
// find the matching close angle bracket
for (; b_range != elt.end(); ++b_range)
{
if (*b_range == '<')
{
++open_count;
continue;
}
if (*b_range == '>')
{
if (--open_count == 0)
{
break;
}
continue;
}
}
// b_range is now pointing at a close angle, or it is at the end of the string
}
if (b_range > e_range)
{
// we are in a wacky case where something like op> showed up in a mangled name.
// just bail.
// TODO: this still isn't correct, but it avoids crashes.
return nullptr;
}
// append the footer
retval.append(b_range, e_range);

// add it to the map
sMap[elt] = std::move(retval);

return sMap[elt].c_str();
}

extern "C" const char* _Nullable demangleSymbolName(const char* _Nonnull symbolName_) {
// serialize to multiple threads
Expand Down
4 changes: 4 additions & 0 deletions kram-profile/Source/KramZipHelperW.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,7 @@ typedef struct ZipEntryW {
// This is only needed for OptFunction and backend names
const char* _Nullable demangleSymbolName(const char* _Nonnull symbolName_);

// This is really the only call needed out of CBA
// Convert templated code to collapsed name so get more correspondence in map.
const char* _Nullable collapseFunctionName(const char* _Nonnull name_);

92 changes: 60 additions & 32 deletions kram-profile/kram-profile/kram_profileApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ import UniformTypeIdentifiers
// DONE: background process to compute buildTimings across all files
// DONE: add a total time, and show that in the nav panel, and % of total
// then know for a summary what the total time spend compiling is.
// TODO: parse instantiateFunction totals from build traces, what CBA is doing
// DONE: parse instantiateFunction totals from build traces, what CBA is doing
// avoid InstatiateClass since it's a child
// TODO: parse optFunction totals from build traces, what CBA is doing
// TODO: duration isn't updating properly when doing Reload on loose files, but thought this fixed
// DONE: parse optFunction totals from build traces, what CBA is doing
// TODO: duration may not updating properly when doing Reload on loose files, but thought this fixed
// TODO: add children of each archive, so those show in the list and can collapse

// Perf traces
Expand Down Expand Up @@ -816,11 +816,21 @@ class BuildFunctionTimings {
}
}

func combine(_ timings: BuildFunctionTimings) {
func combine(_ timings: BuildFunctionTimings, _ collapseNames: Bool = false) {
for pair in timings.optFunctions {
let detail = pair.key
var detail = pair.key
let timing = pair.value

// go out to CBA to collapse the names
if collapseNames {
// skip non-templates
if detail.firstIndex(of: "<") == nil { continue }

if let newDetail = collapseFunctionName(detail) {
detail = String(cString: newDetail)
}
}

if let f = optFunctions[detail] {
f.combine(timing)
}
Expand All @@ -831,9 +841,19 @@ class BuildFunctionTimings {
}
}
for pair in timings.instantiateFunctions {
let detail = pair.key
var detail = pair.key
let timing = pair.value

// go out to CBA to collapse the names
if collapseNames {
// skip non-templates
if detail.firstIndex(of: "<") == nil { continue }

if let newDetail = collapseFunctionName(detail) {
detail = String(cString: newDetail)
}
}

if let f = instantiateFunctions[detail] {
f.combine(timing)
}
Expand Down Expand Up @@ -950,7 +970,16 @@ func postBuildTimingsReport(files: [File]) -> String? {
buildFunctionTimings.combine(file.buildFunctionTimings)
}

let buildJsonBase64 = generateBuildReport(buildTimings: buildTimings, buildFunctionTimings: buildFunctionTimings, buildStats: buildStats)
// Compute more consolidation by collapsing names
let buildTemplateFunctionTimings = BuildFunctionTimings()
buildTemplateFunctionTimings.combine(buildFunctionTimings, true)

let buildJsonBase64 = generateBuildReport(
buildTimings: buildTimings,
buildFunctionTimings: buildFunctionTimings,
buildTemplateFunctionTimings: buildTemplateFunctionTimings,
buildStats: buildStats)

let buildJS = postLoadFileJS(fileContentBase64: buildJsonBase64, title: "BuildTimings")
return buildJS
}
Expand Down Expand Up @@ -996,7 +1025,11 @@ func mergeFileBuildTimings(files: [File]) -> [String:BuildTiming] {
return buildTimings
}

func generateBuildReport(buildTimings: [String:BuildTiming], buildFunctionTimings: BuildFunctionTimings, buildStats: BuildStats) -> String {
func generateBuildReport(buildTimings: [String:BuildTiming],
buildFunctionTimings: BuildFunctionTimings,
buildTemplateFunctionTimings: BuildFunctionTimings,
buildStats: BuildStats) -> String
{
// now convert those timings back into a perfetto displayable report
// So just need to build up the json above into events on tracks
var events: [PerfettoEvent] = []
Expand All @@ -1006,7 +1039,11 @@ func generateBuildReport(buildTimings: [String:BuildTiming], buildFunctionTiming

// add the thread names, only using 3 threads
if true {
let names = ["ParseTime", "ParseCount", "ParseSelf", "OptimizeTime", "InstFunc", "OptimizeFunc"]
let names = ["ParseTime", "ParseCount", "ParseSelf",
"OptimizeTime",
"InstFunc", "OptimizeFunc",
"InstTplFunc", "OptimizeTplFunc"
]
for i in 0..<names.count {
let event = PerfettoEvent(tid: i+1, threadName: names[i])
events.append(event)
Expand Down Expand Up @@ -1089,18 +1126,16 @@ func generateBuildReport(buildTimings: [String:BuildTiming], buildFunctionTiming
}
}

let doFunctionTimings = true
if doFunctionTimings {
func printTimings(_ functions: [String:BuildFunctionTiming], _ event: inout PerfettoEvent, _ events: inout [PerfettoEvent]) {
// compute inverse timings
var timing = 0
for time in buildFunctionTimings.instantiateFunctions.values {
for time in functions.values {
timing += time.duration
}
let timingInv = 1.0 / Double(timing)
event.tid = 5

// dump the highest ones
for tPair in buildFunctionTimings.instantiateFunctions {
// dump the highest duration
for tPair in functions{
let duration = tPair.value.duration
let percent = Double(duration) * timingInv
if percent < 0.01 { continue }
Expand All @@ -1112,27 +1147,20 @@ func generateBuildReport(buildTimings: [String:BuildTiming], buildFunctionTiming
}
}

// TODO: may also need to dump the highest counts
let doFunctionTimings = true
if doFunctionTimings {
// compute inverse timings
var timing = 0
for time in buildFunctionTimings.optFunctions.values {
timing += time.duration
}
let timingInv = 1.0 / Double(timing)
event.tid = 5
printTimings(buildFunctionTimings.instantiateFunctions, &event, &events)

event.tid = 6
printTimings(buildFunctionTimings.optFunctions, &event, &events)

// dump the highest ones
for tPair in buildFunctionTimings.optFunctions {
let duration = tPair.value.duration
let percent = Double(duration) * timingInv
if percent < 0.01 { continue }

let dur = Double(duration) * 1e-6
event.name = "\(tPair.key) \(double: dur, decimals:2, zero: false)s \(tPair.value.count)x"
event.dur = duration
events.append(event)
}
event.tid = 7
printTimings(buildTemplateFunctionTimings.instantiateFunctions, &event, &events)

event.tid = 8
printTimings(buildTemplateFunctionTimings.optFunctions, &event, &events)
}

events.sort {
Expand Down

0 comments on commit 70d8323

Please sign in to comment.