大数据处理引擎:mapreduce,spark
tensorflow、MPI
分布式计算模型:提出一种计算的方法,通过这种计算方法,就能够解决大量数据的分布式计算问题。区别在于提出的分布式计算模型不同,
Mapreduce:一个基本的map-reduce式计算模型。
Spark:定义一套RDD模型,本质上是一系列map、reduce组成的一个DAG图。
RDD模型比较适合哪种没有相互关联的数据并行任务。
tensorflow计算模型是一张图,但是需要为图中的每个节点和做出定义。适合处理特定类型的而计算(神经网络)
调度:
mapreduce 的调度是 yarn,spark 的调度有自己内嵌的调度器,tensorflow 也一样。MPI 呢?它的调度就是几乎没有调度,一切假设集群有资源,靠 ssh 把所有任务拉起来。调度实际上应当分为资源调度器和任务调度器。前者用于向一些资源管理者申请一些硬件资源,后者用于将计算图中的任务下发到这些远程资源进行计算,其实也就是所谓的两阶段调度。
TensorflowOnSpark 之类的项目。这类项目的本质实际上是用 spark 的资源调度,加上 tensorflow 的计算模型
改写为分布式环境
Ray的做法:
openmp做法:
#include<iostream>
#include"omp.h"
using namespace std;
void main() {
#pragma omp parallel for
for(int i = 0; i < 10; ++i) {
cout << "Test" << endl;
}
system("pause");
}
把头文件导入,添加一行预处理指令就可以了,这段代码立马变为并行执行。当然 openmp 不是分布式,只是借助编译器将代码中需要并行化的部分编译为多线程运行,本身还是一个进程,因此其并行度收到 CPU 线程数量所限。如果 CPU 是双线程,那只能 2 倍加速。在一些服务器上,CPU 可以是单核 32 线程,自然能够享受到 32 倍加速。
Ray实现方法:
就是定义了一些 API,类似于 MPI 中的定义的通信原语。使用的时候,将这些 API “注入”到代码合适的位置,那么代码就变成了用户代码夹杂着一些 Ray 框架层的 API 调用,整个代码实际上就形成了一张计算图。接下来的事情就是等待 Ray 把这张计算图完成返回就好了。Ray 的论文给了个例子:
@ray.remote
def create_policy():
# Initialize the policy randomly.
return policy
@ray.remote(num_gpus=1)
class Simulator(object):
def __init__(self):
# Initialize the environment.
self.env = Environment()
def rollout(self, policy, num_steps):
observations = []
observation = self.env.current_state()
for _ in range(num_steps):
action = policy(observation)
observation = self.env.step(action)
observations.append(observation)
return observations
@ray.remote(num_gpus=2)
def update_policy(policy, *rollouts):
# Update the policy.
return policy
@ray.remote
def train_policy():
# Create a policy.
policy_id = create_policy.remote()
# Create 10 actors.
simulators = [Simulator.remote() for _ in range(10)]
# Do 100 steps of training.
for _ in range(100):
# Perform one rollout on each actor.
rollout_ids = [s.rollout.remote(policy_id)
for s in simulators]
# Update the policy with the rollouts.
policy_id = update_policy.remote(policy_id, *rollout_ids)
return ray.get(policy_id)
生成的计算图为:
所以,用户要做的事情,就是在自己的代码里加入适当的 Ray API 调用,然后自己的代码就实际上变成了一张分布式计算图了。作为对比,我们再来看看 tensorflow 对图的定义。
import tensorflow as tf
# 创建数据流图:y = W * x + b,其中W和b为存储节点,x为数据节点。
x = tf.placeholder(tf.float32)
W = tf.Variable(1.0)
b = tf.Variable(1.0)
y = W * x + b
with tf.Session() as sess:
tf.global_variables_initializer().run() # Operation.run
fetch = y.eval(feed_dict={x: 3.0}) # Tensor.eval
print(fetch) # fetch = 1.0 * 3.0 + 1.0
'''
输出:
4.0
'''
通用硬件越来越逼近极限,要想要达到更高的效率,我们需要设计面向领域的架构(Domain Specific Architectures)。这是一个计算架构层出不穷的时代,每种架构都是为了解决其面对的领域问题出现的,必然包含对其问题的特殊优化。通用性不是用户解决问题的出发点,而更多的是框架设计者的“一厢情愿”,用户关注的永远是领域问题。从这个意义上讲,面向领域的计算架构应该才是正确的方向。