大数据技术最常见的编程语言肯定是以 Java 为代表的 Jvm 系语言,比如 Hadoop 和 Flink 是用 Java 写的、Spark 是用 Scala 写的,只有少数的几种大数据技术是用非 Jvm 系语言写的,例如 Impala。至于为什么整个大数据技术会钟爱 jvm 系语言,毕竟谷歌的那三篇奠定大数据基础的经典论文(HDFS、MapReduce和BigTable)没有限定某种编程语言。追溯历史的话,首先因为 Hadoop 是用 Java 实现的,按照它的创始人 Doug Cutting 说法,
Java 当时很酷,他很喜欢这门语言,再加上他之前开发的检索引擎库 Apache Lucene 是用 Java 写的,Hadoop 可以算 Lucene 的一个子项目,因此Hadoop基于 Java 就很理所当然。
随着 Hadoop 大获成功,各种各样基于 Hadoop 的大数据技术自然而然就会选择 Java 语言或者是其它的 jvm 系语言,这样会更好融合进 Hadoop 里。这就造成了现在的大数据技术都选择了 Java、Scala 等 Jvm 系语言。
Java、Scala 等 Jvm 系语言有着自己的优点:良好的语法规范,丰富的第三方类库以及完善的工具支持,庞大的程序员使用群体,最最重要的是,不需要程序员自己管理内存和垃圾回收。
在大数据技术发展初期,因为其优秀的分布式系统架构设计,可以简单粗暴使用多台服务器去计算:一台服务器不能解决的事,那就多加几台。但是服务器数量终究是有限的。同时随着 磁盘和网络技术的发展,SSD 存储不再昂贵,万兆网络也成为了标配,也就是说 IO带宽不是瓶颈限制了,内存管理和CPU计算才是了。
在这种背景下,用户想要更快、更强,就必须进一步追求服务器性能的压榨,于是大家就发现 Jvm 系编程语言的一些不足之处了。
在优化方面,低垂的果子都已经被摘完了,想要进一步优化,比如提升算子的执行效率,基于当前的JVM内核引擎越来越难做,JVM语言对于底层控制力太弱,比如Memory Pipeline和SIMD指令集的使用用户完全是无法控制的。
JVM GC的影响,在超过64GB时,对性能有较大的负面影响。
Spark Java内核的Runtime CodeGen受限于Java Compiler有诸多限制,比如Method Size等,使得线上查询经常fallback到火山模型执行,效率很低。
归根结底,要想充分利用硬件性能,就必须要使用 C++ 等可以直接操作底层CPU指令集的编程语言,而这一点是 jvm 系语言所不能满足的。在这一点上,最先做出示范的是 Spark。当然,如果像 Impala、clickhouse 这种本来就是用 C++ 写的,就不在本文的考虑范围了。
在刚开始的时候,Spark 背后的公司 Databricks 推出的是 Project Tungsten。在这个项目里,主要是包括了这三个方面的努力:
Memory Management和Binary Processing:利用应用的语义(application semantics)来更明确地管理内存,同时消除JVM对象模型和垃圾回收开销。
Cache-aware computation(缓存友好的计算):使用算法和数据结构来实现内存分级结构(memory hierarchy)。
代码生成(Code generation):使用代码生成来利用新型编译器和CPU。
这个项目的效果是很明显的,这一点可以在 Spark 1.0 到 Spark 3.0 巨大的性能提升上看得出来。但是这个项目依然没有解决 jvm 系语言的弊病,只能说,在基于编程语言上的性能优化已经做到了极致,想更进一步就只能替换 Spark 底层的编程语言了。
这当然不是说用新的编程语言去重写 Spark(这也太愚蠢了),现在 Spark 的上层接口已经足够好用,且不说新的编程语言能否做到这种程度的好用,光是兼容性就足以让开发者喝一壶了。因此最好的方向,就是替换 Spark 的执行引擎,保留上层的应用。据公开消息,databricks 推出了 Photon,Intel和Kyligence开源了Gluten项目,阿里的 EMR 使用的是 Weld 方案。
Photon 目前能搜到的只有一篇在ACM Sigmod2022发表的论文,它是Databricks的闭源商业化版本的Spark内核引擎的核心部分,在与 Spark 兼容方向可以做到最好,毕竟 Spark 背后的公司就叫做 Databricks。
Photon实际上最主要的工作是用C++实现了Spark所有的Vectorized版本的物理算子及表达式,在逻辑执行计划以上的API、Analyzer、Resolver、Optimizer等部分都没有任何变化,主要是在逻辑执行计划到物理执行计划的部分,从原来的翻译成火山模型的算子Pipeline或者WholeStageCodeGen变成使用C++ Vectorized版本的物理算子。当然,这其中除了算子本身,还有不少的衔接部分的工作,比如任务调度,内存管理,UDF支持等等。
Photon实现了一个类似Arrow的Column Batch存储结构,用于存储Photon算子的输入和输出数据,Photon算子和表达式的实现部分直接使用SIMD intrinsics,但是主要还是以来编译器进行向量化指令优化,我理解这里的原因可能更多的是为了保证代码的可读性,而且C++的代码结合Hint可以比较确定的进行向量化指令优化。
Intel 和 Kyligence 开源的 Gluten 项目首次亮相是 Databricks Data & AI Summit 2022。按照飞总聊IT里的说法,“如果它(Gluten)成功了,或者类似的项目成功了,我们才有可能见到Photon开源的那一天。”,就像没有社区里的 Iceberg 的话,Delta Lake 是不可能看到完整版本的。
Gluten 通过 Spark Plugin 的机制,把 Spark 查询计划拦截并下发给 Native Engine 来执行,跳过原生 Spark 不高效的执行路径。整体的执行框架仍沿用 Spark 既有实现,包括消费接口、资源和执行调度、查询计划优化、上下游集成等。对于 Native Engine 无法承接的算子,Gluten 安排 fallback 回正常的 Spark 执行路径进行计算。在线程模型的角度,Gluten 使用以 JNI 调用 Library 的形式,在 Spark Executor Task 线程中直接调用 Native 代码,并且严格控制 JNI 调用的次数。在 Gluten 中,Native 空间的代码在申请内存的时候,会先向本地的 Memory Pool 申请内存,如果内存不足,会进一步向 JVM 中 Task Memory Manager 申请内存配额,得到相应配额后才会在 Native 空间成功申请下内存。通过这种方式,Native 空间的内存申请也受到 Task Memory Manager 的统一管理。当发生内存不足的现象时,Task Memory Manager 会触发 spill,不管是 Native 还是 JVM 中的 operator 在收到 spill 通知时都会释放内存。
Photon 项目和Gluten 项目预示着在未来大数据技术如果想得到更进一步的发展,运行的更快的话,对于新的大数据技术,jvm 系语言应该不再是首选项,这一点可以在 clickhouse 上看出来,旧项目的话也会如 Photon 项目和Gluten 项目一样,将之前的 jvm 系语言替换成 C++ 之类可以操作底层指令的编程语言。
参考链接:
上一篇:微信视频号如何运营