Skip to content

Commit a234010

Browse files
Martin Huschenbettcopybara-github
Martin Huschenbett
authored andcommitted
Add a new ActionType::SOURCE_SET for handling the case where we inspect the source sets of the origin. This requires the addition of two new fields to Action as well that are only used with this action type. Hence the unions.
The benefit of handling the source sets with a separate action is that we push fewer actions onto the `actions` stack and that the maximal stack size decreases. The runtime performance benefits of this seems to be rather small (140s -> 138s). PiperOrigin-RevId: 713244634
1 parent cdc2776 commit a234010

File tree

1 file changed

+44
-25
lines changed

1 file changed

+44
-25
lines changed

pytype/typegraph/solver.cc

Lines changed: 44 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ struct TraverseState {
4242

4343
enum ActionType {
4444
TRAVERSE,
45+
TRAVERSE_ALL_SOURCE_SETS,
4546
INSERT_GOALS_TO_REMOVE,
4647
ERASE_GOALS_TO_REMOVE,
4748
ERASE_SEEN_GOALS,
@@ -57,20 +58,32 @@ enum ActionType {
5758
// through "actions".
5859
struct Action {
5960
ActionType action_type;
60-
// Goal either to delete are added to the corresponding set.
61-
const Binding* goal;
62-
// The iterator is for std::set and this is stable upon deletion and insertion
63-
// if it's not directly the element being deleted or inserted. We will only
64-
// try to erase the element on the exact node traversal, so we can safely
65-
// reuse the iterator that was returned from the insertion.
66-
// Not using this for action ERASE_GOALS_TO_REMOVE, as we are requesting
67-
// for removal before the insertion has happened.
68-
GoalSet::iterator erase_it;
61+
union {
62+
// Goal either to delete are added to the corresponding set.
63+
const Binding* goal;
64+
// The iterator is for std::set and this is stable upon deletion and
65+
// insertion if it's not directly the element being deleted or inserted.
66+
// We will only try to erase the element on the exact node traversal, so
67+
// we can safely reuse the iterator that was returned from the insertion.
68+
// Not using this for action ERASE_GOALS_TO_REMOVE, as we are requesting
69+
// for removal before the insertion has happened.
70+
GoalSet::iterator erase_it;
71+
// Source set to handle by a TRAVERSE_ALL_SOURCE_SETS action and end of the
72+
// source sets. The two entries are never the same for actions on the
73+
// actions stack. (Ideally, this would be an anonymous struct with proper
74+
// field names but gcc does not allow this, as opposed to clang.)
75+
std::set<SourceSet>::const_iterator source_sets_it[2];
76+
};
77+
6978
Action(ActionType action_type, const Binding* goal)
7079
: action_type(action_type), goal(goal) {}
71-
Action(ActionType action_type, const Binding* goal,
72-
GoalSet::iterator erase_it)
73-
: action_type(action_type), goal(goal), erase_it(erase_it) {}
80+
Action(ActionType action_type, GoalSet::iterator erase_it)
81+
: action_type(action_type), erase_it(erase_it) {}
82+
Action(ActionType action_type,
83+
std::set<SourceSet>::const_iterator source_sets_it,
84+
std::set<SourceSet>::const_iterator source_sets_end)
85+
: action_type(action_type),
86+
source_sets_it{source_sets_it, source_sets_end} {}
7487
};
7588

7689
static void traverse(const CFGNode* position,
@@ -93,7 +106,7 @@ static void traverse(const CFGNode* position,
93106
return;
94107
}
95108
auto [it, _] = state.seen_goals.insert(goal);
96-
actions.emplace(ERASE_SEEN_GOALS, nullptr, it);
109+
actions.emplace(ERASE_SEEN_GOALS, it);
97110

98111
const auto* origin = goal->FindOrigin(position);
99112
if (!origin) {
@@ -105,18 +118,9 @@ static void traverse(const CFGNode* position,
105118

106119
state.removed_goals.push_back(goal);
107120
actions.emplace(ERASE_REMOVED_GOALS, nullptr);
108-
for (const auto& source_set : origin->source_sets) {
109-
for (const Binding* next_goal : source_set) {
110-
if (!state.goals_to_remove.count(next_goal)) {
111-
actions.emplace(ERASE_GOALS_TO_REMOVE, next_goal);
112-
}
113-
}
114-
actions.emplace(TRAVERSE, nullptr);
115-
for (const Binding* next_goal : source_set) {
116-
if (!state.goals_to_remove.count(next_goal)) {
117-
actions.emplace(INSERT_GOALS_TO_REMOVE, next_goal);
118-
}
119-
}
121+
if (!origin->source_sets.empty()) {
122+
actions.emplace(TRAVERSE_ALL_SOURCE_SETS, origin->source_sets.cbegin(),
123+
origin->source_sets.cend());
120124
}
121125
}
122126

@@ -149,6 +153,21 @@ static std::vector<RemoveResult> remove_finished_goals(const CFGNode* pos,
149153
case TRAVERSE:
150154
traverse(pos, results, actions, state);
151155
break;
156+
case TRAVERSE_ALL_SOURCE_SETS: {
157+
const auto& source_set = *action.source_sets_it[0];
158+
action.source_sets_it[0]++;
159+
if (action.source_sets_it[0] != action.source_sets_it[1]) {
160+
actions.push(action);
161+
}
162+
for (const Binding* next_goal : source_set) {
163+
auto [it, added] = state.goals_to_remove.insert(next_goal);
164+
if (added) {
165+
actions.emplace(ERASE_GOALS_TO_REMOVE, next_goal);
166+
}
167+
}
168+
actions.emplace(TRAVERSE, nullptr);
169+
break;
170+
}
152171
case INSERT_GOALS_TO_REMOVE:
153172
state.goals_to_remove.insert(action.goal);
154173
break;

0 commit comments

Comments
 (0)