Files
wehub-resource-sync 8a852e4b4e
cffconvert / validate (push) Has been skipped
License Check / license-check (push) Failing after 2s
chore: import upstream snapshot with attribution
2026-07-13 12:14:16 +08:00

264 lines
9.7 KiB
C++

/* Copyright 2020 The TensorFlow Authors. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/
#include <algorithm>
#include <memory>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/status/status.h"
#include "absl/strings/string_view.h"
#include "pybind11/pybind11.h" // from @pybind11
#include "pybind11/stl.h" // from @pybind11
#include "tensorflow/core/framework/graph.pb.h"
#include "tensorflow/core/framework/node_def_util.h"
#include "tensorflow/core/framework/op.h"
#include "tensorflow/core/grappler/costs/graph_properties.h"
#include "tensorflow/core/grappler/costs/op_performance_data.pb.h"
#include "tensorflow/core/grappler/grappler_item.h"
#include "tensorflow/core/grappler/grappler_item_builder.h"
#include "tensorflow/core/grappler/utils.h"
#include "tensorflow/core/grappler/utils/topological_sort.h"
#include "tensorflow/core/protobuf/meta_graph.pb.h"
#include "tensorflow/python/lib/core/pybind11_status.h"
namespace py = pybind11;
// Manages disjoint sets of colocation groups using a union-find data structure
// with path compression and union by rank.
class ColocationGroups {
public:
// Ensures a node is tracked in the disjoint-set structure.
void RegisterNode(absl::string_view node_name) { Find(node_name); }
// Unions the colocation sets containing nodes x and y.
void Group(absl::string_view x, absl::string_view y) {
Rep* x_root = Find(x);
Rep* y_root = Find(y);
// x and y are already in the same set
if (x_root == y_root) {
return;
}
// x and y are not in same set, so we merge them
// Use the occasion to strengthen what we know about the handle by merging
// the information about the 2 subsets.
if (x_root->rank < y_root->rank) {
x_root->parent = y_root;
} else if (x_root->rank > y_root->rank) {
y_root->parent = x_root;
} else {
// Arbitrarily make one root the new parent
y_root->parent = x_root;
++x_root->rank;
}
}
// Extracts all disjoint colocation groups as a list of node name lists.
std::vector<std::vector<std::string>> ExtractGroups() {
std::vector<std::vector<std::string>> groups;
groups.reserve(nodes_.size());
absl::flat_hash_map<const Rep*, int> group_ids;
for (const auto& [node_name, rep_ptr] : nodes_) {
Rep* r = Find(rep_ptr.get());
auto [it, inserted] = group_ids.try_emplace(r, groups.size());
int id = it->second;
if (inserted) {
// If inserted, this is a new group. The value stored (groups.size())
// is the index where the new group should be added.
groups.emplace_back();
}
groups[id].push_back(node_name);
}
for (auto& g : groups) {
std::sort(g.begin(), g.end());
}
std::sort(groups.begin(), groups.end());
return groups;
}
private:
struct Rep {
// Parent in the tree used to encode the set.
Rep* parent;
// Rank in the tree, used to figure out how to compress the path to the root
// of the tree.
int rank;
};
Rep* Find(absl::string_view n) {
// Try to emplace a new Rep. If the key already exists, try_emplace does
// nothing and returns an iterator to the existing element. Otherwise,
// it inserts a new unique_ptr<Rep> and returns an iterator to it.
auto [it, inserted] = nodes_.try_emplace(n, std::make_unique<Rep>());
if (inserted) {
// First time processing this handle, initialize the new entry.
Rep* raw_node = it->second.get();
raw_node->parent = raw_node;
raw_node->rank = 0;
return raw_node;
}
return Find(it->second.get());
}
Rep* Find(Rep* node) {
Rep* root = node->parent;
while (root != root->parent) {
root = root->parent;
}
while (node->parent != root) {
Rep* next = node->parent;
node->parent = root;
node = next;
}
return root;
}
absl::flat_hash_map<std::string, std::unique_ptr<Rep>> nodes_;
};
PYBIND11_MAKE_OPAQUE(tensorflow::grappler::GrapplerItem);
PYBIND11_MODULE(_pywrap_tf_item, m) {
py::class_<tensorflow::grappler::GrapplerItem> grappler_item(m,
"GrapplerItem");
m.def("TF_NewItem",
[](const py::bytes& serialized_metagraph, bool ignore_colocation,
bool ignore_user_placement)
-> std::unique_ptr<tensorflow::grappler::GrapplerItem> {
tensorflow::MetaGraphDef metagraph;
py::buffer_info info = py::buffer(serialized_metagraph).request();
if (!metagraph.ParseFromArray(info.ptr, info.size)) {
throw std::invalid_argument(
"The MetaGraphDef could not be parsed as a valid protocol "
"buffer");
}
if (metagraph.collection_def().count("train_op") == 0) {
tsl::MaybeRaiseRegisteredFromStatus(absl::InvalidArgumentError(
"train_op not specified in the metagraph"));
}
tensorflow::grappler::ItemConfig cfg;
cfg.ignore_user_placement = ignore_user_placement;
cfg.ignore_colocation = ignore_colocation;
std::unique_ptr<tensorflow::grappler::GrapplerItem> item =
tensorflow::grappler::GrapplerItemFromMetaGraphDef(
"item", metagraph, cfg);
if (item == nullptr) {
tsl::MaybeRaiseRegisteredFromStatus(
absl::InvalidArgumentError("Invalid metagraph"));
}
return item;
});
m.def("TF_IdentifyImportantOps",
[](tensorflow::grappler::GrapplerItem* item,
bool sort_topologically) -> std::vector<std::string> {
std::vector<const tensorflow::NodeDef*> main_ops =
item->MainOpsFanin();
std::vector<const tensorflow::NodeDef*> enqueue_ops =
item->EnqueueOpsFanin();
absl::flat_hash_set<std::string> op_names;
for (const tensorflow::NodeDef* op : main_ops) {
op_names.insert(op->name());
}
for (const tensorflow::NodeDef* op : enqueue_ops) {
op_names.insert(op->name());
}
std::vector<std::string> ops;
if (sort_topologically) {
tensorflow::GraphDef subgraph;
for (const tensorflow::NodeDef& node : item->graph.node()) {
if (op_names.find(node.name()) != op_names.end()) {
*subgraph.add_node() = node;
}
}
tsl::MaybeRaiseRegisteredFromStatus(
tensorflow::grappler::TopologicalSort(&subgraph));
for (const tensorflow::NodeDef& node : subgraph.node()) {
ops.push_back(node.name());
}
} else {
ops.reserve(op_names.size());
for (const auto& op_name : op_names) {
ops.push_back(op_name);
}
std::sort(ops.begin(), ops.end());
}
return ops;
});
m.def("TF_GetOpProperties",
[](tensorflow::grappler::GrapplerItem* item)
-> std::unordered_map<std::string, std::vector<py::bytes>> {
tensorflow::grappler::GraphProperties properties(*item);
tsl::MaybeRaiseRegisteredFromStatus(
properties.InferStatically(false));
std::unordered_map<std::string, std::vector<py::bytes>> props;
for (const tensorflow::NodeDef& node : item->graph.node()) {
const std::string& node_name = node.name();
const std::vector<tensorflow::OpInfo::TensorProperties>&
output_props = properties.GetOutputProperties(node_name);
std::vector<py::bytes> prop;
prop.reserve(output_props.size());
for (const tensorflow::OpInfo::TensorProperties& output_prop :
output_props) {
prop.push_back(output_prop.SerializeAsString());
}
props[node_name] = std::move(prop);
}
return props;
});
m.def("TF_GetColocationGroups",
[](tensorflow::grappler::GrapplerItem* item)
-> std::vector<std::vector<std::string>> {
ColocationGroups groupings;
tensorflow::OpRegistry* registry = tensorflow::OpRegistry::Global();
for (const tensorflow::NodeDef& node : item->graph.node()) {
const tensorflow::OpDef* op_def;
if (!registry->LookUpOpDef(node.op(), &op_def).ok()) {
continue;
}
tensorflow::NameRangeMap inputs;
if (!tensorflow::NameRangesForNode(node, *op_def, &inputs, nullptr)
.ok()) {
continue;
}
for (const tensorflow::OpDef::ArgDef& arg : op_def->input_arg()) {
if (!arg.is_ref()) {
continue;
}
const auto& range = inputs[arg.name()];
for (int i = range.first;
i < range.second && i < node.input_size(); ++i) {
groupings.Group(node.name(),
tensorflow::grappler::NodeName(node.input(i)));
}
}
}
return groupings.ExtractGroups();
});
}