Skip to content

[InstrProf] Adding utility weights to BalancedPartitioning #72717

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

Merged
merged 5 commits into from
Jan 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 23 additions & 3 deletions llvm/include/llvm/Support/BalancedPartitioning.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,27 @@ class BPFunctionNode {
friend class BalancedPartitioning;

public:
/// The type of ID
using IDT = uint64_t;
using UtilityNodeT = uint32_t;
/// The type of UtilityNode
struct UtilityNodeT {
UtilityNodeT(uint32_t Id, uint32_t Weight = 1) : Id(Id), Weight(Weight) {}
uint32_t Id;
uint32_t Weight;

// Deduplicate utility nodes for a given function.
// TODO: One may experiment with accumulating the weights of duplicates.
static void sortAndDeduplicate(SmallVector<UtilityNodeT, 4> &UNs) {
llvm::sort(UNs, [](const UtilityNodeT &L, const UtilityNodeT &R) {
return L.Id < R.Id;
});
UNs.erase(std::unique(UNs.begin(), UNs.end(),
[](const UtilityNodeT &L, const UtilityNodeT &R) {
return L.Id == R.Id;
}),
UNs.end());
}
};

/// \param UtilityNodes the set of utility nodes (must be unique'd)
BPFunctionNode(IDT Id, ArrayRef<UtilityNodeT> UtilityNodes)
Expand All @@ -78,8 +97,7 @@ class BPFunctionNode {
uint64_t InputOrderIndex = 0;

friend class BPFunctionNodeTest_Basic_Test;
friend class BalancedPartitioningTest_Basic_Test;
friend class BalancedPartitioningTest_Large_Test;
friend class BalancedPartitioningTest;
};

/// Algorithm parameters; default values are tuned on real-world binaries
Expand Down Expand Up @@ -188,6 +206,8 @@ class BalancedPartitioning {
float CachedGainRL;
/// Whether \p CachedGainLR and \p CachedGainRL are valid
bool CachedGainIsValid = false;
/// The weight of this utility node
uint32_t Weight = 1;
};

protected:
Expand Down
9 changes: 4 additions & 5 deletions llvm/lib/ProfileData/InstrProf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -923,15 +923,15 @@ std::vector<BPFunctionNode> TemporalProfTraceTy::createBPFunctionNodes(

const int N = Log2_64(LargestTraceSize) + 1;

// TODO: We need to use the Trace.Weight field to give more weight to more
// TODO: We may use the Trace.Weight field to give more weight to more
// important utilities
DenseMap<IDT, SmallVector<UtilityNodeT, 4>> FuncGroups;
for (size_t TraceIdx = 0; TraceIdx < Traces.size(); TraceIdx++) {
for (uint32_t TraceIdx = 0; TraceIdx < Traces.size(); TraceIdx++) {
auto &Trace = Traces[TraceIdx].FunctionNameRefs;
for (size_t Timestamp = 0; Timestamp < Trace.size(); Timestamp++) {
for (int I = Log2_64(Timestamp + 1); I < N; I++) {
auto FunctionId = Trace[Timestamp];
UtilityNodeT GroupId = TraceIdx * N + I;
UtilityNodeT GroupId(TraceIdx * N + I);
FuncGroups[FunctionId].push_back(GroupId);
}
}
Expand All @@ -940,8 +940,7 @@ std::vector<BPFunctionNode> TemporalProfTraceTy::createBPFunctionNodes(
std::vector<BPFunctionNode> Nodes;
for (auto Id : FunctionIds) {
auto &UNs = FuncGroups[Id];
llvm::sort(UNs);
UNs.erase(std::unique(UNs.begin(), UNs.end()), UNs.end());
UtilityNodeT::sortAndDeduplicate(UNs);
Nodes.emplace_back(Id, UNs);
}
return Nodes;
Expand Down
57 changes: 35 additions & 22 deletions llvm/lib/Support/BalancedPartitioning.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,13 @@ using namespace llvm;
#define DEBUG_TYPE "balanced-partitioning"

void BPFunctionNode::dump(raw_ostream &OS) const {
OS << formatv("{{ID={0} Utilities={{{1:$[,]}} Bucket={2}}", Id,
make_range(UtilityNodes.begin(), UtilityNodes.end()), Bucket);
OS << "{ID=" << Id << " Utilities={";
for (auto &N : UtilityNodes)
OS << N.Id << " ,";
OS << "}";
if (Bucket.has_value())
OS << " Bucket=" << Bucket.value();
OS << "}";
}

template <typename Func>
Expand Down Expand Up @@ -166,34 +171,40 @@ void BalancedPartitioning::runIterations(const FunctionNodeRange Nodes,
unsigned RecDepth, unsigned LeftBucket,
unsigned RightBucket,
std::mt19937 &RNG) const {
unsigned NumNodes = std::distance(Nodes.begin(), Nodes.end());
DenseMap<BPFunctionNode::UtilityNodeT, unsigned> UtilityNodeIndex;
// Count the degree of each utility node.
DenseMap<uint32_t, unsigned> UtilityNodeIndex;
for (auto &N : Nodes)
for (auto &UN : N.UtilityNodes)
++UtilityNodeIndex[UN];
++UtilityNodeIndex[UN.Id];
// Remove utility nodes if they have just one edge or are connected to all
// functions
// functions.
unsigned NumNodes = std::distance(Nodes.begin(), Nodes.end());
for (auto &N : Nodes)
llvm::erase_if(N.UtilityNodes, [&](auto &UN) {
return UtilityNodeIndex[UN] == 1 || UtilityNodeIndex[UN] == NumNodes;
return UtilityNodeIndex[UN.Id] == 1 ||
UtilityNodeIndex[UN.Id] == NumNodes;
});

// Renumber utility nodes so they can be used to index into Signatures
// Renumber utility nodes so they can be used to index into Signatures.
UtilityNodeIndex.clear();
for (auto &N : Nodes)
for (auto &UN : N.UtilityNodes)
UN = UtilityNodeIndex.insert({UN, UtilityNodeIndex.size()}).first->second;
UN.Id = UtilityNodeIndex.insert({UN.Id, UtilityNodeIndex.size()})
.first->second;

// Initialize signatures
// Initialize signatures.
SignaturesT Signatures(/*Size=*/UtilityNodeIndex.size());
for (auto &N : Nodes) {
for (auto &UN : N.UtilityNodes) {
assert(UN < Signatures.size());
if (N.Bucket == LeftBucket) {
Signatures[UN].LeftCount++;
} else {
Signatures[UN].RightCount++;
}
assert(UN.Id < Signatures.size());
if (N.Bucket == LeftBucket)
Signatures[UN.Id].LeftCount++;
else
Signatures[UN.Id].RightCount++;
// Identical utility nodes (having the same UN.Id) have the same weight
// (unless there are hash collisions mapping utilities to the same Id);
// thus, we get a new weight only when the signature is uninitialized.
Signatures[UN.Id].Weight = UN.Weight;
}
}

Expand Down Expand Up @@ -221,9 +232,11 @@ unsigned BalancedPartitioning::runIteration(const FunctionNodeRange Nodes,
Signature.CachedGainLR = 0.f;
Signature.CachedGainRL = 0.f;
if (L > 0)
Signature.CachedGainLR = Cost - logCost(L - 1, R + 1);
Signature.CachedGainLR =
(Cost - logCost(L - 1, R + 1)) * Signature.Weight;
if (R > 0)
Signature.CachedGainRL = Cost - logCost(L + 1, R - 1);
Signature.CachedGainRL =
(Cost - logCost(L + 1, R - 1)) * Signature.Weight;
Signature.CachedGainIsValid = true;
}

Expand Down Expand Up @@ -282,14 +295,14 @@ bool BalancedPartitioning::moveFunctionNode(BPFunctionNode &N,
// Update signatures and invalidate gain cache
if (FromLeftToRight) {
for (auto &UN : N.UtilityNodes) {
auto &Signature = Signatures[UN];
auto &Signature = Signatures[UN.Id];
Signature.LeftCount--;
Signature.RightCount++;
Signature.CachedGainIsValid = false;
}
} else {
for (auto &UN : N.UtilityNodes) {
auto &Signature = Signatures[UN];
auto &Signature = Signatures[UN.Id];
Signature.LeftCount++;
Signature.RightCount--;
Signature.CachedGainIsValid = false;
Expand Down Expand Up @@ -318,8 +331,8 @@ float BalancedPartitioning::moveGain(const BPFunctionNode &N,
const SignaturesT &Signatures) {
float Gain = 0.f;
for (auto &UN : N.UtilityNodes)
Gain += (FromLeftToRight ? Signatures[UN].CachedGainLR
: Signatures[UN].CachedGainRL);
Gain += (FromLeftToRight ? Signatures[UN.Id].CachedGainLR
: Signatures[UN.Id].CachedGainRL);
return Gain;
}

Expand Down
30 changes: 18 additions & 12 deletions llvm/unittests/ProfileData/BPFunctionNodeTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
#include "gtest/gtest.h"

using testing::Field;
using testing::Matcher;
using testing::UnorderedElementsAre;
using testing::UnorderedElementsAreArray;

namespace llvm {

Expand All @@ -24,29 +24,35 @@ void PrintTo(const BPFunctionNode &Node, std::ostream *OS) {
}

TEST(BPFunctionNodeTest, Basic) {
auto UNIdsAre = [](auto... Ids) {
return UnorderedElementsAre(Field("Id", &BPFunctionNode::UtilityNodeT::Id,
std::forward<uint32_t>(Ids))...);
};
auto NodeIs = [](BPFunctionNode::IDT Id,
ArrayRef<BPFunctionNode::UtilityNodeT> UNs) {
return AllOf(Field("Id", &BPFunctionNode::Id, Id),
Field("UtilityNodes", &BPFunctionNode::UtilityNodes,
UnorderedElementsAreArray(UNs)));
Matcher<ArrayRef<BPFunctionNode::UtilityNodeT>> UNsMatcher) {
return AllOf(
Field("Id", &BPFunctionNode::Id, Id),
Field("UtilityNodes", &BPFunctionNode::UtilityNodes, UNsMatcher));
};

auto Nodes = TemporalProfTraceTy::createBPFunctionNodes({
TemporalProfTraceTy({0, 1, 2, 3}),
});
EXPECT_THAT(Nodes,
UnorderedElementsAre(NodeIs(0, {0, 1, 2}), NodeIs(1, {1, 2}),
NodeIs(2, {1, 2}), NodeIs(3, {2})));
EXPECT_THAT(Nodes, UnorderedElementsAre(NodeIs(0, UNIdsAre(0, 1, 2)),
NodeIs(1, UNIdsAre(1, 2)),
NodeIs(2, UNIdsAre(1, 2)),
NodeIs(3, UNIdsAre(2))));

Nodes = TemporalProfTraceTy::createBPFunctionNodes({
TemporalProfTraceTy({0, 1, 2, 3, 4}),
TemporalProfTraceTy({4, 2}),
});

EXPECT_THAT(Nodes,
UnorderedElementsAre(NodeIs(0, {0, 1, 2}), NodeIs(1, {1, 2}),
NodeIs(2, {1, 2, 4, 5}), NodeIs(3, {2}),
NodeIs(4, {2, 3, 4, 5})));
EXPECT_THAT(Nodes, UnorderedElementsAre(NodeIs(0, UNIdsAre(0, 1, 2)),
NodeIs(1, UNIdsAre(1, 2)),
NodeIs(2, UNIdsAre(1, 2, 4, 5)),
NodeIs(3, UNIdsAre(2)),
NodeIs(4, UNIdsAre(2, 3, 4, 5))));
}

} // end namespace llvm
73 changes: 66 additions & 7 deletions llvm/unittests/Support/BalancedPartitioningTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,20 @@ class BalancedPartitioningTest : public ::testing::Test {
Ids.push_back(N.Id);
return Ids;
}

static testing::Matcher<BPFunctionNode> NodeIdIs(BPFunctionNode::IDT Id) {
return Field("Id", &BPFunctionNode::Id, Id);
};

static testing::Matcher<BPFunctionNode>
NodeBucketIs(std::optional<uint32_t> Bucket) {
return Field("Bucket", &BPFunctionNode::Bucket, Bucket);
};

static testing::Matcher<BPFunctionNode>
NodeIs(BPFunctionNode::IDT Id, std::optional<uint32_t> Bucket) {
return AllOf(NodeIdIs(Id), NodeBucketIs(Bucket));
};
};

TEST_F(BalancedPartitioningTest, Basic) {
Expand All @@ -48,11 +62,6 @@ TEST_F(BalancedPartitioningTest, Basic) {

Bp.run(Nodes);

auto NodeIs = [](BPFunctionNode::IDT Id, std::optional<uint32_t> Bucket) {
return AllOf(Field("Id", &BPFunctionNode::Id, Id),
Field("Bucket", &BPFunctionNode::Bucket, Bucket));
};

EXPECT_THAT(Nodes,
UnorderedElementsAre(NodeIs(0, 0), NodeIs(1, 1), NodeIs(2, 2),
NodeIs(3, 3), NodeIs(4, 4)));
Expand All @@ -79,8 +88,7 @@ TEST_F(BalancedPartitioningTest, Large) {

Bp.run(Nodes);

EXPECT_THAT(
Nodes, Each(Not(Field("Bucket", &BPFunctionNode::Bucket, std::nullopt))));
EXPECT_THAT(Nodes, Each(Not(NodeBucketIs(std::nullopt))));
EXPECT_THAT(getIds(Nodes), UnorderedElementsAreArray(OrigIds));
}

Expand All @@ -97,4 +105,55 @@ TEST_F(BalancedPartitioningTest, MoveGain) {
30.f);
}

TEST_F(BalancedPartitioningTest, Weight1) {
std::vector<BPFunctionNode::UtilityNodeT> UNs = {
BPFunctionNode::UtilityNodeT(0, 100),
BPFunctionNode::UtilityNodeT(1, 100),
BPFunctionNode::UtilityNodeT(2, 100),
BPFunctionNode::UtilityNodeT(3, 1),
BPFunctionNode::UtilityNodeT(4, 1),
};
std::vector<BPFunctionNode> Nodes = {
BPFunctionNode(0, {UNs[0], UNs[3]}), BPFunctionNode(1, {UNs[1], UNs[3]}),
BPFunctionNode(2, {UNs[2], UNs[3]}), BPFunctionNode(3, {UNs[0], UNs[4]}),
BPFunctionNode(4, {UNs[1], UNs[4]}), BPFunctionNode(5, {UNs[2], UNs[4]}),
};

Bp.run(Nodes);

// Check that nodes that share important UNs are ordered together
auto NodesRef = ArrayRef(Nodes);
auto Groups = {NodesRef.slice(0, 2), NodesRef.slice(2, 2),
NodesRef.slice(4, 2)};
EXPECT_THAT(Groups, UnorderedElementsAre(
UnorderedElementsAre(NodeIdIs(0), NodeIdIs(3)),
UnorderedElementsAre(NodeIdIs(1), NodeIdIs(4)),
UnorderedElementsAre(NodeIdIs(2), NodeIdIs(5))));
}

TEST_F(BalancedPartitioningTest, Weight2) {
std::vector<BPFunctionNode::UtilityNodeT> UNs = {
BPFunctionNode::UtilityNodeT(0, 1),
BPFunctionNode::UtilityNodeT(1, 1),
BPFunctionNode::UtilityNodeT(2, 1),
BPFunctionNode::UtilityNodeT(3, 100),
BPFunctionNode::UtilityNodeT(4, 100),
};
std::vector<BPFunctionNode> Nodes = {
BPFunctionNode(0, {UNs[0], UNs[3]}), BPFunctionNode(1, {UNs[1], UNs[4]}),
BPFunctionNode(2, {UNs[2], UNs[3]}), BPFunctionNode(3, {UNs[0], UNs[4]}),
BPFunctionNode(4, {UNs[1], UNs[3]}), BPFunctionNode(5, {UNs[2], UNs[4]}),
};

Bp.run(Nodes);

// Check that nodes that share important UNs are ordered together
auto NodesRef = ArrayRef(Nodes);
auto Groups = {NodesRef.slice(0, 3), NodesRef.slice(3, 3)};
EXPECT_THAT(Groups,
UnorderedElementsAre(
UnorderedElementsAre(NodeIdIs(0), NodeIdIs(2), NodeIdIs(4)),
UnorderedElementsAre(NodeIdIs(1), NodeIdIs(3), NodeIdIs(5))));
}

} // end namespace llvm