Skip to content

Commit

Permalink
Add --hot-func-list to llvm-profdata show for sample profiles
Browse files Browse the repository at this point in the history
Summary: Add the --hot-func-list feature to llvm-profdata show for sample profiles. This feature prints a list of hot functions whose max sample count are above the 99% threshold, with their numbers of total samples, total samples percentage, max samples, entry samples, and their function names.

Reviewers: wmi, hoyFB, wenlei

Reviewed By: wmi

Subscribers: hoyFB, wenlei, llvm-commits, weihe

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D81800
  • Loading branch information
weihe authored and WenleiHe committed Jun 20, 2020
1 parent 7b201bf commit 7348b95
Show file tree
Hide file tree
Showing 4 changed files with 209 additions and 3 deletions.
15 changes: 15 additions & 0 deletions llvm/include/llvm/ProfileData/SampleProf.h
Expand Up @@ -498,6 +498,21 @@ class FunctionSamples {
return CallsiteSamples;
}

/// Return the maximum of sample counts in a function body including functions
/// inlined in it.
uint64_t getMaxCountInside() const {
uint64_t MaxCount = 0;
for (const auto &L : getBodySamples()) {
MaxCount = std::max(MaxCount, L.second.getSamples());
}
for (const auto &C : getCallsiteSamples()) {
for (const auto &F : C.second) {
MaxCount = std::max(MaxCount, F.second.getMaxCountInside());
}
}
return MaxCount;
}

/// Merge the samples in \p Other into this one.
/// Optionally scale samples by \p Weight.
sampleprof_error merge(const FunctionSamples &Other, uint64_t Weight = 1) {
Expand Down
41 changes: 41 additions & 0 deletions llvm/test/tools/llvm-profdata/Inputs/sample-hot-func-list.proftext
@@ -0,0 +1,41 @@
_Z3bari:20301:1437
1: 1437
_Z3fooi:7711:610
1: 610
main:184019:0
4: 534
4.2: 534
5: 1075
5.1: 1075
6: 2080
7: 534
9: 2300 _Z3bari:1471 _Z3fooi:631
10: inline1:1000
1: 1000
10: inline2:2000
1: 2000
_Z3bazi:20305:1000
1: 1000
Func1:1523:169
1: 169
7: 563
Func2:17043:1594
1: 1594
3: 1594
6: 2009 Func1:150 Func3:1789
13: 3105
17: 3105
19: 1594
Func3:97401:3035
1: 3035
5: 7344
9: 10640
11: 10640
15: 3035
Func4:465:210
1: 210
Func5:6948:470
1: 470
3: 3507
Func6:310:102
1: 102
12 changes: 12 additions & 0 deletions llvm/test/tools/llvm-profdata/sample-hot-func-list.test
@@ -0,0 +1,12 @@
; RUN: llvm-profdata show --sample --hot-func-list %S/Inputs/sample-hot-func-list.proftext | FileCheck %s
; CHECK: 8 out of 10 functions with profile (80.00%) are considered hot functions (max sample >= 470).
; CHECK-NEXT: 355251 out of 356026 profile counts (99.78%) are from hot functions.
; CHECK-NEXT: Total sample (%) Max sample Entry sample Function name
; CHECK-NEXT: 184019 (51.69%) 2300 534 main
; CHECK-NEXT: 97401 (27.36%) 10640 3035 Func3
; CHECK-NEXT: 20305 (5.70%) 1000 1000 _Z3bazi
; CHECK-NEXT: 20301 (5.70%) 1437 1437 _Z3bari
; CHECK-NEXT: 17043 (4.79%) 3105 1594 Func2
; CHECK-NEXT: 7711 (2.17%) 610 610 _Z3fooi
; CHECK-NEXT: 6948 (1.95%) 3507 470 Func5
; CHECK-NEXT: 1523 (0.43%) 563 169 Func1
144 changes: 141 additions & 3 deletions llvm/tools/llvm-profdata/llvm-profdata.cpp
Expand Up @@ -23,11 +23,12 @@
#include "llvm/Support/Errc.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/Threading.h"
#include "llvm/Support/ThreadPool.h"
#include "llvm/Support/Threading.h"
#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
Expand Down Expand Up @@ -1026,11 +1027,141 @@ static void showSectionInfo(sampleprof::SampleProfileReader *Reader,
}
}

struct HotFuncInfo {
StringRef FuncName;
uint64_t TotalSample;
double TotalSamplePercent;
uint64_t MaxSample;
uint64_t EntrySample;

HotFuncInfo()
: FuncName(), TotalSample(0), TotalSamplePercent(0.0f), MaxSample(0),
EntrySample(0) {}

HotFuncInfo(StringRef FN, uint64_t TS, double TSP, uint64_t MS, uint64_t ES)
: FuncName(FN), TotalSample(TS), TotalSamplePercent(TSP), MaxSample(MS),
EntrySample(ES) {}
};

// Print out detailed information about hot functions in PrinfValues vector.
// Users specify titles and offset of every columns through ColumnTitle and
// ColumnOffset. Note that ColumnOffset starts from the offset of the second
// column (because the first column always starts at offset 0). The size of
// ColumnTitle and ColumnOffset need to be at least ColNum and ColNum-1. In
// addition, users can optionally give a HotFuncMetric string to print out or
// let it be an empty string.
static void dumpHotFunctionList(
const uint64_t &ColNum, const std::vector<std::string> &ColumnTitle,
const std::vector<uint64_t> &ColumnOffset,
const std::vector<HotFuncInfo> &PrintValues, const uint64_t &HotFuncCount,
const uint64_t &TotalFuncCount, const uint64_t &HotProfCount,
const uint64_t &TotalProfCount, const std::string &HotFuncMetric,
raw_fd_ostream &OS) {
assert(ColumnOffset.size() >= ColNum - 1 && ColumnTitle.size() >= ColNum);

formatted_raw_ostream FOS(OS);
FOS << HotFuncCount << " out of " << TotalFuncCount
<< " functions with profile ("
<< format("%.2f%%", (((double)HotFuncCount) / TotalFuncCount * 100))
<< ") are considered hot functions";
if (!HotFuncMetric.empty())
FOS << " (" << HotFuncMetric << ")";
FOS << ".\n";
FOS << HotProfCount << " out of " << TotalProfCount << " profile counts ("
<< format("%.2f%%", (((double)HotProfCount) / TotalProfCount * 100))
<< ") are from hot functions.\n";

if (ColNum == 0)
return;

for (size_t I = 0; I < ColNum; ++I) {
FOS << ColumnTitle[I];
FOS.PadToColumn(ColumnOffset[I]);
}
FOS << "\n";

for (const auto &R : PrintValues) {
FOS << R.TotalSample << " (" << format("%.2f%%", R.TotalSamplePercent)
<< ")";
FOS.PadToColumn(ColumnOffset.size() > 0 ? ColumnOffset[0] : 0);
FOS << R.MaxSample;
FOS.PadToColumn(ColumnOffset.size() > 1 ? ColumnOffset[1] : 0);
FOS << R.EntrySample;
FOS.PadToColumn(ColumnOffset.size() > 2 ? ColumnOffset[2] : 0);
FOS << R.FuncName << "\n";
}
return;
}

static int
showHotFunctionList(const StringMap<sampleprof::FunctionSamples> &Profiles,
ProfileSummary &PS, raw_fd_ostream &OS) {
using namespace sampleprof;

const uint64_t HotFuncCutoff = 990000;
auto &SummaryVector = PS.getDetailedSummary();
uint64_t MinCountThreshold = 0;
for (const auto &SummaryEntry : SummaryVector) {
if (SummaryEntry.Cutoff == HotFuncCutoff) {
MinCountThreshold = SummaryEntry.MinCount;
break;
}
}
assert(MinCountThreshold != 0);

// Traverse all functions in the profile and keep only hot functions.
// The following loop also calculates the sum of total samples of all
// functions.
std::multimap<uint64_t, std::pair<const FunctionSamples *, const uint64_t>,
std::greater<uint64_t>>
HotFunc;
uint64_t ProfileTotalSample = 0;
uint64_t HotFuncSample = 0;
uint64_t HotFuncCount = 0;
uint64_t MaxCount = 0;
for (const auto &I : Profiles) {
const auto &FuncProf = I.second;
ProfileTotalSample += FuncProf.getTotalSamples();
MaxCount = FuncProf.getMaxCountInside();

// MinCountThreshold is a block/line threshold computed for a given cutoff.
// We intentionally compare the maximum sample count in a function with this
// threshold to get an approximate threshold for hot functions.
if (MaxCount >= MinCountThreshold) {
HotFunc.emplace(FuncProf.getTotalSamples(),
std::make_pair(&(I.second), MaxCount));
HotFuncSample += FuncProf.getTotalSamples();
HotFuncCount++;
}
}

const uint64_t ColNum = 4;
std::vector<std::string> ColumnTitle{"Total sample (%)", "Max sample",
"Entry sample", "Function name"};
std::vector<uint64_t> ColumnOffset{24, 42, 58};
std::string Metric =
std::string("max sample >= ") + std::to_string(MinCountThreshold);
std::vector<HotFuncInfo> PrintValues;
for (const auto &FuncPair : HotFunc) {
const auto &FuncPtr = FuncPair.second.first;
PrintValues.emplace_back(
HotFuncInfo(FuncPtr->getFuncName(), FuncPtr->getTotalSamples(),
(FuncPtr->getTotalSamples() * 100.0) / ProfileTotalSample,
FuncPair.second.second, FuncPtr->getEntrySamples()));
}
dumpHotFunctionList(ColNum, ColumnTitle, ColumnOffset, PrintValues,
HotFuncCount, Profiles.size(), HotFuncSample,
ProfileTotalSample, Metric, OS);

return 0;
}

static int showSampleProfile(const std::string &Filename, bool ShowCounts,
bool ShowAllFunctions, bool ShowDetailedSummary,
const std::string &ShowFunction,
bool ShowProfileSymbolList,
bool ShowSectionInfoOnly, raw_fd_ostream &OS) {
bool ShowSectionInfoOnly, bool ShowHotFuncList,
raw_fd_ostream &OS) {
using namespace sampleprof;
LLVMContext Context;
auto ReaderOrErr = SampleProfileReader::create(Filename, Context);
Expand Down Expand Up @@ -1064,6 +1195,9 @@ static int showSampleProfile(const std::string &Filename, bool ShowCounts,
PS.printDetailedSummary(OS);
}

if (ShowHotFuncList)
showHotFunctionList(Reader->getProfiles(), Reader->getSummary(), OS);

return 0;
}

Expand All @@ -1090,6 +1224,9 @@ static int show_main(int argc, const char *argv[]) {
cl::desc(
"Cutoff percentages (times 10000) for generating detailed summary"),
cl::value_desc("800000,901000,999999"));
cl::opt<bool> ShowHotFuncList(
"hot-func-list", cl::init(false),
cl::desc("Show profile summary of a list of hot functions"));
cl::opt<bool> ShowAllFunctions("all-functions", cl::init(false),
cl::desc("Details for every function"));
cl::opt<bool> ShowCS("showcs", cl::init(false),
Expand Down Expand Up @@ -1153,7 +1290,8 @@ static int show_main(int argc, const char *argv[]) {
else
return showSampleProfile(Filename, ShowCounts, ShowAllFunctions,
ShowDetailedSummary, ShowFunction,
ShowProfileSymbolList, ShowSectionInfoOnly, OS);
ShowProfileSymbolList, ShowSectionInfoOnly,
ShowHotFuncList, OS);
}

int main(int argc, const char *argv[]) {
Expand Down

0 comments on commit 7348b95

Please sign in to comment.