tensorflow节点布放(device assignment of node)算法:simpler_placer
core/common_runtime/simple_placer_test.cc测试片段如下
////////////////////////////////////////////////////////////////////////////////
//
// A SimplePlacerTest method has three phases:
//
// 1. Build a TensorFlow graph, with no (or partial) device assignments.
// 2. Attempt to compute a placement using the SimplePlacer.
// 3. EITHER: test that the constraints implied by the graph are respected;
// or that an appropriate error was reported.
//
////////////////////////////////////////////////////////////////////////////////
class SimplePlacerTest : public ::testing::Test {
protected:
SimplePlacerTest() {
// Build a set of 10 GPU and 10 CPU devices.
// NOTE: this->local_devices_ owns the device objects;
// this->devices_ contains borrowed pointers to the device
// objects.
for (int i = ; i < ; ++i) { // 添加了10 cpu和10 gpu的fake devices
local_devices_.emplace_back(FakeDevice::MakeCPU(
strings::StrCat("/job:a/replica:0/task:0/cpu:", i)));
devices_.AddDevice(local_devices_.back().get());
// Insert the GPUs in reverse order.
local_devices_.emplace_back(FakeDevice::MakeGPU(
strings::StrCat("/job:a/replica:0/task:0/gpu:", - i)));
devices_.AddDevice(local_devices_.back().get());
}
}
...
}
...
// Test that a graph with no constraints will successfully assign nodes to the
// "best available" device (i.e. prefer GPU over CPU).
TEST_F(SimplePlacerTest, TestNoConstraints) {
Graph g(OpRegistry::Global());
{ // Scope for temporary variables used to construct g. // 用GraphDefBuilder构建graph的结构
GraphDefBuilder b(GraphDefBuilder::kFailImmediately);
Node* input = ops::SourceOp("TestInput", b.opts().WithName("in"));
ops::UnaryOp("TestRelu", ops::NodeOut(input, ), b.opts().WithName("n1"));
ops::UnaryOp("TestRelu", ops::NodeOut(input, ), b.opts().WithName("n2"));
TF_EXPECT_OK(BuildGraph(b, &g)); // BuildGraph函数将GraphDefBuilder的图写入到Graph中
} TF_EXPECT_OK(Place(&g)); // Place函数将graph中的node布放到设备列表中
EXPECT_DEVICE_TYPE(g, "in", DEVICE_CPU); // 期望:input节点在CPU中,n1节点在GPU中,n2节点在GPU中,故而GPU优先级大于CPU
EXPECT_DEVICE_TYPE(g, "n1", DEVICE_GPU);
EXPECT_DEVICE_TYPE(g, "n2", DEVICE_GPU);
}
其中BuildGraph函数将GraphDefBuilder 对象中的graph 结构定义写入到Graph中。Place函数将graph中的node布放到设备列表中,其中device assignment算法的核心在SimplePlacer::Run函数中
// Builds the given graph, and (if successful) indexes the node
// names for use in placement, and later lookup.
Status BuildGraph(const GraphDefBuilder& builder, Graph* out_graph) {
TF_RETURN_IF_ERROR(builder.ToGraph(out_graph));
nodes_by_name_.clear();
for (Node* node : out_graph->nodes()) {
nodes_by_name_[node->name()] = node->id();
}
return Status::OK();
}
// Invokes the SimplePlacer on "graph". If no DeviceSet is specified, the
// placement will use the default DeviceSet (of 10 CPU and 10 GPU devices).
//
// REQUIRES: "*graph" was produced by the most recent call to BuildGraph.
Status Place(Graph* graph, DeviceSet* devices, SessionOptions* options) {
SimplePlacer placer(graph, devices, options);
return placer.Run();
}
SimplePlacer::Run()在core/common_runtime/simple_placer.cc文件中,具体实现分为4个步骤:
// 1. First add all of the nodes. Note that steps (1) and (2)
// requires two passes over the nodes because the graph (and hence
// the constraints) may not be acyclic. 这里graph可能是有环的?
for (Node* node : graph_->nodes()) {
// Skip the source and sink nodes.
if (!node->IsOp()) { continue; }
status = colocation_graph.AddNode(*node);
if (!status.ok()) return AttachDef(status, node->def());
}
// 2. Enumerate the constraint edges, and use them to update the disjoint node set. // disjoint set(并查集,即不相交的节点集合),一种树型数据结构,
...
ColocationGraph maintains the connected components of a colocation constraint graph, and uses this information to assign a satisfying device placement to the nodes of the graph.
The implementation uses the union- find algorithm to maintain the connected components efficiently and incrementally as edges (implied by ColocationGraph::ColocateNodes() invocations) are added.
参考:并查集wiki


. For each node, assign a device based on the constraints in thedisjoint node set.
std::vector<Device*> devices;
std::vector<Node*> second_pass;
for (Node* node : graph_->nodes()) {
// Skip the source and sink nodes.
if (!node->IsOp()) {
continue;
}
// Skip nodes that already have an assigned name.
if (!node->assigned_device_name().empty()) {
continue;
}
// Heuristic A: prefer to place "generators" with their only
// consumers.
//
// If this is a node with no inputs and a single (non-ref)
// consumer, we save this for a second pass, so that the
// consumer's placement is chosen.
if (IsGeneratorNode(node)) { // generator node: no input, one output, not a reference-type node
second_pass.push_back(node);
continue;
}
status = colocation_graph.GetDevicesForNode(node, &devices);
...
// Returns the first device in sorted devices list so we will always
// choose the same device.
//
// TODO(vrv): Factor this assignment out into a pluggable
// algorithm, so that SimplePlacer is responsible for enforcing
// preconditions and we can experiment with other algorithms when
// given a choice of devices. Once we have a better idea of the
// types of heuristics we want to use and the information needed
// to perform good placement we can add an interface for this.
string assigned_device = devices[]->name();
// Heuristic B: If the node only operates on metadata, not data,
// then it is desirable to place that metadata node with its
// input.
if (IsMetadataNode(node)) {
// Make sure that the input device type is in the list of supported
// device types for this node.
const Node* input = (*node->in_edges().begin())->src();
// TODO(vrv): if the input is empty, consider postponing this
// node's assignment to the second pass, so that we handle the
// case where a metadata node's input comes from a backedge
// of a loop.
const string& input_device_name = input->assigned_device_name();
if (CanAssignToDevice(input_device_name, devices)) {
assigned_device = input_device_name;
}
}
AssignAndLog(assigned_device, node); // 将assigned_device分配个node节点,在步骤3中没有对符合Heuristic A的GeneratorNode分配设备,而是在步骤4中完成的
}
bool IsGeneratorNode(const Node* node) {
return node->num_inputs() == && node->num_outputs() == && node->out_edges().size() == && !IsRefType(node->output_type());
}
bool IsMetadataNode(const Node* node) {
const string& node_type = node->type_string();
return (node_type == "Size" || node_type == "Shape" || node_type == "Rank");
}
// 4. Perform a second pass assignment for those nodes explicitly skipped during the first pass.
...
部分参考:
tensorflow节点布放(device assignment of node)算法:simpler_placer的更多相关文章
- 获取所有树叶子节点 注册添加事件 if ($(node).tree('isLeaf', node.target)) 是否叶子节点
//获取所有树叶子节点 注册添加事件 if ($(node).tree('isLeaf', node.target)) 是否叶子节点 $(function () { $('.easyui-tree') ...
- [图解tensorflow源码] Simple Placer节点布放算法
- 笔记︱基于网络节点的node2vec、论文、算法python实现
看到一个很有意思的算法,而且腾讯朋友圈lookalike一文中也有提及到,于是蹭一波热点,学习一下.论文是也发KDD2016 . . 一.主要论文:node2vec: Scalable Feature ...
- TensorFlow实现knn(k近邻)算法
首先先介绍一下knn的基本原理: KNN是通过计算不同特征值之间的距离进行分类. 整体的思路是:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于 ...
- HDU 5289 Assignment (ST算法区间最值+二分)
题目链接:pid=5289">http://acm.hdu.edu.cn/showproblem.php?pid=5289 题面: Assignment Time Limit: 400 ...
- kaggle赛题Digit Recognizer:利用TensorFlow搭建神经网络(附上K邻近算法模型预测)
一.前言 kaggle上有传统的手写数字识别mnist的赛题,通过分类算法,将图片数据进行识别.mnist数据集里面,包含了42000张手写数字0到9的图片,每张图片为28*28=784的像素,所以整 ...
- Kubernetes 二进制部署(一)单节点部署(Master 与 Node 同一机器)
0. 前言 最近受“新冠肺炎”疫情影响,在家等着,入职暂时延后,在家里办公和学习 尝试通过源码编译二进制的方式在单一节点(Master 与 Node 部署在同一个机器上)上部署一个 k8s 环境,整理 ...
- k8s kubernetes给node节点添加标签和删除node节点标签
node节点IP 192.168.1.205 给节点添加标签的命令 添加label语法 kubectl label nodes <node-name> <label-key>= ...
- TensorFlow从0到1之回归算法(11)
回归是数学建模.分类和预测中最古老但功能非常强大的工具之一.回归在工程.物理学.生物学.金融.社会科学等各个领域都有应用,是数据科学家常用的基本工具. 回归通常是机器学习中使用的第一个算法.通过学习因 ...
随机推荐
- python7
字典-dict 字典也是一种组合数据,没有顺序的组合数据,数据以键值对的方式存在 字典的定义 1.创建空字符串 变量 = {} 或者 变量 = dict() 2 ...
- 七 FileChannel
FileChannel是一个连接到文件的通道,可以通过文件通道读写文件 FileChannel无法设置为非阻塞模式,它总是运行在阻塞模式下. 打开FileChannel 在使用FileChannel之 ...
- struts2 国际化语言转换
学习struts2,了解了使用struts2的配置文件可以走向国际化,实现页面的语言转换.我已中文和英文为例,简单的实现登录页面的国际化 废话不多说,上代码 一,login.jsp页面 使用s标签&l ...
- BBS需求分析和orm设计
一.BBS博客需求分析 首页(现实文章) 文章详情 点赞 文章评论(子评论,评论的展示) 登录功能(图片验证码) 注册功能(基于form验证) 个人站点(不同人不同样式,文章过滤) 后台管理(文章展示 ...
- JavaEE中表现层、持久层、业务层的职责分析(转载)
表现层.持久层.业务层 注:本文转载于:http://www.blogjava.net/jiabao/archive/2007/04/08/109189.html 为了实现web层(struts)和持 ...
- Re-thinking Deep Residual Networks
本文是对ImageNet 2015的冠军ResNet(Deep Residual Networks)以及目前围绕ResNet这个工作研究者后续所发论文的总结,主要涉及到下面5篇论文. 1. Link: ...
- Js 对Dom的操作
一.DOM的概述 DOM(Document Object Model,文档对象模型)描绘了一个层次化的节点树,允许开发人员添加.移除和修改页面的某一部分.这使得JavaScript操作HTML,不是在 ...
- python 替换指定目录下,所有文本字符串
网页保存后,会把js文件起名为.下载,html里面的引用也会有,很不美观,解决方案:用python替换字符串 import os import re """将当前目录下所 ...
- Linux ->> VMWare Workstation虚拟机里的UBuntu系统安装VMWare-tools
1) mkdir创建一个临时目录 2)复制gz压缩包到临时目录下 3)解压到当前目录 4)运行.pl文件安装 root@ubuntu:/# root@ubuntu:/# cd /tmp/ root@u ...
- mysql 免安装版安装(window7)
初次使用mysql免安装版步骤: 1.设置环境变量,将mysql 加压文件路径添加到环境变量path中(作用是不用每次都切换路径) 控制面板>系统和安全>系统>高级系统设置 2.安装 ...