3.1 KiB
UNDF: UNDF-2026-000000485
opencv-0001: tvUpdateConfictMap — O(C²×G) std::find on graphConflictMap vectors inside recursive DFS
Location
modules/dnn/src/op_timvx.cpp lines 27–41, line 869–875
Repository: https://github.com/opencv/opencv
Severity
HIGH — Called during DNN network initialization when partitioning layers across TimVX sub-graphs. A DNN with C consumer edges and G TimVX graphs incurs O(C²×G) work per call site due to recursive DFS with linear conflict-set membership at every node. For transformer architectures with many attention layers (hundreds of consumer connections), this is a serious bottleneck.
Complexity
- Before: O(C × G) per recursive call, O(C²×G) over full DFS traversal
- After: O(C + G) over full DFS traversal using unordered_set
Defective Code
// op_timvx.cpp:23-41
void Net::Impl::tvUpdateConfictMap(int graphIndex, LayerData& ld,
std::vector<std::vector<int>>& graphConflictMap)
{
if (ld.consumers.empty()) return;
for (int i = 0; i < ld.consumers.size(); i++)
{
LayerData &consumerld = layers[ld.consumers[i].lid];
std::vector<int>::iterator it = std::find(
graphConflictMap[ld.consumers[i].lid].begin(), // O(G) linear scan
graphConflictMap[ld.consumers[i].lid].end(),
graphIndex);
if (it == graphConflictMap[ld.consumers[i].lid].end())
{
graphConflictMap[ld.consumers[i].lid].push_back(graphIndex);
tvUpdateConfictMap(graphIndex, consumerld, graphConflictMap); // recursive
}
}
}
// op_timvx.cpp:864-875
bool TimVXInfo::isConflict(int layerId, int graphIndex)
{
if (graphConflictMap[layerId].empty()) return false;
std::vector<int>::iterator it = std::find( // O(G) linear scan
graphConflictMap[layerId].begin(),
graphConflictMap[layerId].end(), graphIndex);
return it != graphConflictMap[layerId].end();
}
Problem: graphConflictMap stores each layer's conflicting graph indices as a vector<int>.
Every std::find call is O(G) where G = number of TimVX sub-graphs. The recursive DFS
visits every consumer of every layer making the total O(C × G). Since this is recursive
over the consumer graph (depth up to C nodes), total work is O(C²×G).
Fixed Code
// Change graphConflictMap type from vector<vector<int>> to vector<unordered_set<int>>
void Net::Impl::tvUpdateConfictMap(int graphIndex, LayerData& ld,
std::vector<std::unordered_set<int>>& graphConflictMap)
{
if (ld.consumers.empty()) return;
for (int i = 0; i < ld.consumers.size(); i++)
{
int cid = ld.consumers[i].lid;
LayerData &consumerld = layers[cid];
if (graphConflictMap[cid].insert(graphIndex).second) // O(1) insert+dedup
{
tvUpdateConfictMap(graphIndex, consumerld, graphConflictMap);
}
}
}
bool TimVXInfo::isConflict(int layerId, int graphIndex)
{
return graphConflictMap[layerId].count(graphIndex) > 0; // O(1)
}
CWE
CWE-407: Inefficient Algorithmic Complexity — O(C²×G) → O(C+G)