浅谈在线并行计算框架

文章目录

  • 浅谈在线并行计算框架
    • 1. 背景
    • 2. 关键问题
      • 2.1 链式处理
      • 2.2 并行拆分
        • 2.2.1 数据横向拆分
        • 2.2.2 流水线并行
      • 高内聚无副作用算子
      • 图执行引擎
    • 后续

1. 背景

并行计算我们并不陌生,Hadoop, Spark,甚至最近特别火的深度学习模型的 GPU 加速,都是充分发掘了并行计算的潜力。业界涌现出很多多线程编程框架,典型的如brpc。但是随着业务的越来越复杂,以单条请求维度的并行处理已经不能满足业务在请求过程中的延时需求。在这种情况下一般有两种的优化思路:

  • 数据并行化:请求数据横向拆分,通过多个同构服务的RPC请求的扇出来降低延时要求。
  • 计算并行化:通过数值计算并行优化例如使用SIMD和GPU加速运算,或者使用openMP或者自己多线程来实现

2. 关键问题

上面提到的都是通用的常见的做法,对于复杂的C++业务系统编程,需要解决业务抽象和并行计算的适配问题。

2.1 链式处理

每个请求的处理往往包含着多个阶段,而可能同时有上百人在开发各个阶段。怎么对各个阶段进行抽象,才能使所有人的开发能有序开展,互不冲突,也使请求的处理逻辑清晰有序,秩序井然?

在早期的时候,很容易想到的办法就是管道。就像 POSIX 的 PIPE 一样,上游的输出作为下游的输入,把整个过程分为多个阶段。对离线数据处理来说,管道的方式非常理想,即使到了现代,Spark RDD 的数据处理也是管道化的。

但对于在线服务程序来说,管道的方式就有些不足:如果想在管道中插入一个环节,就必须得适配上游管道的输出下游管道的输入,即增加环节的输入输出已经无法自定义了,必须服从已有管道的设定。而且还有一个缺点,就是管道的处理过程无法随意组装、合并

既然每个阶段的输入输出无法自定义了,那么干脆大家就共享同样的输入输出数据结构得了。这样反而可以把处理过程进行一定的组合、合并。这样就产生了我们目前最常见的处理方式,链式的请求处理过程。拉链上的每个算子,都共享同一个输入和输出。每个算子,其实就是一个接口相同的函数,这样就可以随意地调整函数指针的组合,形成不同的处理链。比如:一个请求走 A 模型排序,一个请求要走 B 模型排序,他们可以共享前序的算子,只在最后一个算子有所不同。

在链式处理基础上,算子也可以是继承同一基类接口的派生类,或者 lambda 表达式。结合工厂模式等一些编程技巧,处理链的调整可以配置化、动态化、脚本化。

2.2 并行拆分

2.2.1 数据横向拆分

前面讲到 数据可以横向拆分 到同构的服务里进行并行处理,但这样做代价并不低。比如 RPC 增加了很多 拆包封包解包合并 的处理过程,同时并行的 Processor 数量增多也会消耗更多的内存 Quota。当数据已经足够小时,是否还有其它的数据并行办法可以降低整体处理的时延呢?

2.2.2 流水线并行

如果为了避免问题,一些框架中使用了流水线并行的办法来解决这个问题:

流水线并行类似于 CPU 的指令流水线设计, 如忘记了可以参考这篇回顾一下https://www.cnblogs.com/myseries/p/14458367.html。

  • 链式处理的每个环节可以在单独的线程中执行,这样算子在处理不同的数据时可以并行起来。
  • 而对同一个数据来说,它在不同时刻只会被一个算子(线程)处理,经历的算子处理过程完全符合算子链的顺序,避免了并行计算的数据一致性问题。

高内聚无副作用算子

然后处理链越来越长、越来越长,忽然有一天,我们发现已经无法理解这个处理链了。每个算子都可以共享同一个输入输出,那么每个算子都可能会修改其中的任何一个数据。别说并行化可能会产生的数据一致性问题了,即使某个数据出了问题,我们也不知道到底被哪个算子给改了。

所以,在解决并行计算问题之前,对于复杂的 C++ 业务系统,首先要解决业务计算的抽象问题。

回到管道式结构?嗯,其实是可以的。但除了上面讲的问题外,还有一个成本问题,如果每个算子的输入输出都不同,那就意味着额外的内存消耗,以及额外的内存分配和释放消耗。对于离线任务来说,由于它们追求的是吞吐,可以攒一批一起处理,启动、传输延迟稍微大一点也没关系,任务经常启停,也不会存在严重的内存碎片问题。但对于在线的时延敏感的 C++ 服务来说,内存的管理会是一个严峻的挑战,需要仔细地去处理。

那么有没有一种办法,既能让同一个数据在不同的算子间自由地流动,又能限制算子只访问需要的数据(高内聚),且能限制算子不访问未在算子参数中声明的数据(无副作用)?这个问题我思考了半年,一直没想到好的办法,直到我看到了 Fugue 框架。

我用一个小技巧把 Fugue 对数据处理的基本思想做了一点简化:数据仍是那个数据,但参数不是那个参数。在算子之间流动的,仍然是同样的一块内存,但每个算子看到的数据结构是不同的。大略可以和 BERT 模型训练的思想相类比:每次训练用的还是那段文本,但是 MASK 掉的部分不一样。

简单来说,每个算子处理的数据,仍然是同一个 struct/class,但是每个算子看到的只是这个 class 的一个派生类,这个派生类中只有对 class 中有限个成员变量的访问接口。其它的成员变量都被 MASK 掉了。

但将父类指针强转成派生类指针,是有风险的。万一这个派生类增加了一个父类中没有的成员变量,将派生类指针指向父类对象时,对这个成员变量的访问是很容易出问题的。我们用一个现代 C++ 的编程技巧,解决了这个指针强转的安全性问题。如果一个指针的类型是 A 类型或者 A 类型的派生类型(std::is_convertible<>),而且这个指针的类型大小和 A 类型一样(is_all_eq<>),那么就可以安全地将 A 类型对象(父类对象)的指针强转成该类型的指针。这里的判断都是模板元编程,一旦函数的参数类型有问题,在程序的编译期就会报错。

// 编译期检查可变模板参数列表是否全为真
template <bool…> struct bool_pack;
template <bool… v>
using is_all_true = std::is_same<bool_pack<true, v…>, bool_pack<v…, true>>;
// 编译期检查可变模板参数列表数值是否与 a 一致
template <int…> struct int_pack;
template <int a, int… v>
using is_all_eq = std::is_same<int_pack<a, v…>, int_pack<v…, a>>;

图执行引擎

在管道式算子结构下,图执行引擎的设计相对简单,完全可以自动做计算图的推导,Spark 就是一个成熟的例子。Fugue 也是使用了 Single Assignment 的指导原则,保证每个数据只被赋值一次,也实现了自动的计算图推导。基于模板元编程的编译期自动计算图推导,非常厉害。

但是在很多业务中,大部分算子的处理可能都集中在同一个数据上。比如搜索的排序过程可能就是对队列的各种修改。如果基于 Single Assignment 的原则,每次修改都产生一个新的队列,那么内存的消耗,或者内存重分配的消耗是相当高的。

不过一般在线的图执行引擎设计没有追求这种极致。因为在设计上考虑对于在线服务来说,自动推导计算图的价值没有那么高。在线服务的计算图在线上往往需要重复执行上亿次,研发人员完全有动力去自己优化计算图,而且手动指定的计算图也更可控一些。

既然无法实现自动的计算图推导,为什么又要设计前面的高内聚、无副作用的算子呢?原因有两个:

  • 一是它本身就是一种优雅的业务计算抽象方案。从编程模型上来说,希望限制算子的维护者不乱来。当前人已经通过参数显式限制了某个算子的输入输出,那么后人在维护算子时如果想修改其它的数据,必须显式地修改参数,这对复杂系统的维护来说更友好。

  • 二是虽然它不能直接推导计算图,但可以使计算图的人工构建更容易。如果每个算子的输入输出都是明确无副作用的,那么研发人员可能更好地去设计计算图。如果把自动计算图推导的图引擎视为 L4/L5 自动驾驶的话,在线的图执行引擎大概只有 L3 级别。

后续

这些东西都是在阅读学习到高级的C++在线图计算引擎的一些心得,一定还有一些不完善的地方

浅谈在线并行计算框架相关推荐

  1. 浅谈javaweb三大框架和MVC设计模式

    浅谈javaweb三大框架和MVC设计模式 转载自:http://blog.csdn.net/sunpeng19960715/article/details/50890705 小序:博主以前在学jav ...

  2. 浅谈android网络框架——以课程格子的bug为例

    大家好! 在使用课程格子过程中 ,发现如果网络断开,点击树洞秘密,课程格子会因为无法从网络上刷新数据而崩溃掉.今天借解决此bug的为例,浅谈android的网络框架.

  3. 浅谈Python flask框架浅析

    前言 Python 面向对象的高级编程语言,以其语法简单.免费开源.免编译扩展性高,同时也可以嵌入到C/C++程序和丰富的第三方库,Python运用到大数据分析.人工智能.web后端等应用场景上. P ...

  4. 基于若依框架的二次开发_浅谈若依框架

    何为框架?若依框架又是什么?具备什么功能? 框架的英文为Framework,带有骨骼,支架的含义.在软件工程中,框架往往被定义为整个或部分系统的可重用设计,是一个可重复使用的设计构件.类似于一个数学公 ...

  5. python twisted和flask_浅谈Python Web 框架:Django, Twisted, Tornado, Flask, Cyclone 和 Pyramid...

    Django 是一个高级的 Python Web 框架,支持快速开发,简洁.实用的设计.如果你正在建一个和电子商务网站相似的应用,那你应该选择用 Django 框架.它能使你快速完成工作,也不必担心太 ...

  6. 浅谈ASP.NET框架

       本篇文章更适合具有一定开发经验,一定功底,且对底层代码有所研究的朋友!!! 本篇文章稍微偏原理且底层,有一定难度和且比较晦涩,文章粒度稍微粗些,更细粒度的,会在后续的文章中,结合具体的Demo实 ...

  7. 浅谈Spring测试框架+junit4单元测试原理

    Spring的主要测试框架核心: 3个接口 1TestContext:负责持有一个当前测试的上下文 2TestContextManger: (1)每次启动都会创建,管理一个TestContext (2 ...

  8. 浅谈 SAP UI5 框架对一些其他前端框架比如 Vue 的支持

    我们都知道 Fiori 代表 SAP 新一代 UI 的界面风格,而 UI5 是 Fiori UX(User Experience,用户体验)的具体实现技术.从下图这则新闻 能够看出,SAP 决定将 F ...

  9. 浅谈基于SSM框架的权限管理(代码)

    pom.xml的配置 <spring.security.version>5.0.1.RELEASE</spring.security.version><dependenc ...

最新文章

  1. Swift 基本基本运算符
  2. 勇士斗恶龙:没那么复杂的Js闭包(改)
  3. Android底部菜单栏 仿微博效果
  4. tensorflow从入门到精通100讲(四)-细粒度的情感分析Gated Convolutional Networks
  5. PHP.ini 中的错误提示
  6. Redis-集合(Set)基础
  7. 在 TMG 更新中心中使用 WSUS进行每日的定义更新
  8. 软件详细设计文档【转】
  9. Atitit 人员评价能力模型 目录 1.1. 深度、大局观、 1 1.2. 影响力, 影响力 分享 1 1.3. 业务洞察力 价值识别 1 1.4. 视野 战略和人才 专业 1 1.5.
  10. Linux宝库幕后推手齐聚OpenInfra Days China
  11. get请求中传json参数报400的错误_react的数据请求
  12. 人工智能革命(上):通往超级智能之路
  13. Unity学习笔记–无限地图
  14. ❤ ❤响应式小米官网源码!!!(js+css+html)❤ ❤
  15. 小米4进入开发者模式
  16. 几何布朗运动模拟 MATLAB实现
  17. 关于vray5.2怎么关闭日志窗口
  18. 使用Python 对ENVI SPECTRAL LIBRARY(.sli)进行读取
  19. Vue笔记随笔---kalrry
  20. 基于生成对抗网络结构的图像修复(GAN)

热门文章

  1. 2016年创业项目-2
  2. wxml input
  3. 《最详细的docker+php开发环境教程》(五) 搭建开发环境概要
  4. 2023山东科技大学计算机考研信息汇总
  5. 101页4万字数字孪生能源互联网智慧能源物联网大数据建设方案
  6. 【Android Studio】修改C盘.gradle文件夹位置
  7. 告诉你用Python赚钱的五种方法,闲余月赚1000~5000
  8. 中国代步平衡车市场销售态势与消费趋势预测报告(2022-2027年)
  9. 亚马逊、迪士尼、资生堂、林清轩等众多商家推出节日礼遇 | 2021女王节
  10. 好用的蓝牙耳机有哪些推荐?十大蓝牙耳机品牌!