chukonu阅读笔记(4)

发布于:2025-08-05 ⋅ 阅读:(13) ⋅ 点赞:(0)

Chukonu表示和优化在Chukonu框架内部保持不变。用户只需要理解RDD在逻辑上是一个元素的分布式数据集,而无需关心其分区或生产者的类型。这由C++14提供的占位符类型[6]特性启用。

4.2 典型紧凑数据布局

这个是前文中写的没有那么清晰的部分

为了高效处理,Chukonu内部将分区表示为紧凑分区布局,其中分区的元素高效地存储在少量的缓冲区中。这有助于避免由于分配小对象而导致的内存碎片,并消除了垃圾收集器(gc)运行时压缩的需求。此外,它增加了数据局部性并提供了规则的内存访问模式,从而可以提高CPU效率。

看后文,到底是怎么紧凑的

在这里插入图片描述

如图8所示,Chukonu提供了几个模板来为各种数据结构生成紧凑的分区布局。扁平数组将固定长度的元素放置在缓冲区中。位图将布尔元素放置在缓冲区中。数组-数组和字符串-数组将数组元素放置在CSR布局中[45]。可空数组将可空元素放置在位图和数组中。元组数组将元组元素放置在数组的元组中。虽然如果需要,可以实现更多种类的紧凑布局,但Chukonu的紧凑分区布局涵盖了大数据分析中的大多数情况:我们所有的工作负载都基于这些紧凑分区布局。

细节不太重要,这个是六种点优化。

从紧凑数据布局中产生的元素是视图,如字符串视图或跨度,避免了物理构造字符串或向量的需求,从而减少了对象创建的开销。视图本质上是指针,因此需要小心处理。Chukonu保证可以从用户定义函数(UDF)返回输入视图或其切片。但是,返回指向本地对象的视图是非法的。为了解决这个问题,Chukonu允许用户以对象形式返回对象,并在内部将其转换为视图形式。

格式转换问题

4.3 Chukonu RDD API
Chukonu RDD API的设计与Spark RDD API几乎完全相同,但使用原生编程语言C++。它提供了一种基于粗粒度不可变转换的数据集抽象,其中新数据集是基于先前数据集创建的。数据集转换是延迟计算的:由转换生成的数据集不保存值,而是保存生成这些值的DAG。

只记录下来源就可以了,需要的时候在计算,类似于lazy tag

其运算符的名称和语义与Spark的运算符相同,以便Spark用户可以轻松访问。由于采用了现代C++语法,基于Chukonu RDD API和Spark RDD API的程序非常相似。

在这里插入图片描述

Scala是一种多范式编程语言,结合了面向对象和函数式编程特性,运行在JVM上,并非Spark专属。但Spark最初是用Scala开发的,其API对Scala支持极佳,因此Scala成为Spark开发的主流语言之一。Scala语法简洁,可与Java无缝互操作,广泛用于大数据、分布式系统等领域。

图9比较了使用Spark和Chukonu RDD API的logistic回归程序。它们生成几乎相同的DAG,并且源代码级别的差异仅反映了特定于语言的细节,例如C++中的“auto”。

由于 C++ 中缺乏标准序列化支持,Chukonu 使用了 cereal [4] 的序列化接口,这是一个广泛使用的 C++ 序列化库。Spark 要求用户自定义函数是可序列化的,以实现控制传递并支持弹性。标准 C++ 中的 Lambda 表达式简化了 UDF 的编写,但不可序列化。因此,Chukonu 提供了一个宏 “FN”,以帮助用户通过 lambda 表达式定义 UDF。

5.chukonu引擎

Chukonu引擎借鉴了Spark引擎的大部分特性,并对其进行了增强,以支持高效的Spark集成。§5.1介绍了Chukonu引擎如何重用Spark特性。§5.2介绍了Chukonu引擎如何提供安全的显式指针传递,以实现高效的Spark集成。§5.3介绍了Chukonu如何消除集成开销。

5.1 集成方法

在这里插入图片描述

图10展示了运行Chukonu程序的工作流程,并说明了集成方法。在原生环境中,Chukonu引擎包含一个库,该库提供了Apache Spark的简单C++绑定(称为CppSpark)。由于篇幅限制,我们在此不介绍其API。

DAGs运行时部分的转换由CppSpark实现。

Chukonu程序与库一起编译,以产生一个可动态加载的模块。(图左侧绿色部分)

在JVM环境中,Chukonu引擎包含一个驱动程序(右侧下部,chukonu driver),用于执行已编译的Chukonu程序。Chukonu驱动程序是一个用Java实现的普通Spark应用程序,可以像普通的Spark应用程序一样提交到与Spark兼容的集群。(图右侧向上提交)

成功提交后,Chukonu驱动程序加载已编译的Chukonu程序,将引擎实现注册到模块,并调用Chukonu程序的主例程。为了支持远程执行,Chukonu驱动程序还指示新创建的执行器准备环境,例如,从HDFS下载已编译的Chukonu程序。

结构类似一个驱动程序和一堆算子,chukonu主例程估计是个比较上层管理的东西。

5.2 安全高效的显式指针传递

在这里插入图片描述

解释一下概念
好的,让我们用更简单的例子来解释这些概念。

1. 序列化(Serialization)

定义:将对象转换为字节流,以便保存到文件或通过网络发送。

例子
假设我们有一个简单的Python字典,我们想要将其保存到文件中。

person = {'name': 'Alice', 'age': 25}
import json
with open('person.json', 'w') as f:
    json.dump(person, f)

这里,我们使用 json.dump 方法将字典转换为JSON格式的字符串,并将其写入文件。这就是序列化。

2. 注册(Registration)

定义:在使用某些序列化库时,需要先告诉库有哪些类型的数据需要序列化。

例子
假设我们使用一个自定义的序列化库,需要先注册数据类型。

class Serializer:
    def __init__(self):
        self.registry = {}

    def register(self, type):
        self.registry[type] = True

    def serialize(self, obj):
        if type(obj) in self.registry:
            return json.dumps(obj.__dict__)
        else:
            raise TypeError("Type not registered")

serializer = Serializer()
serializer.register(Person)
serialized_person = serializer.serialize(person)

在这个例子中,我们创建了一个 Serializer 类,它需要我们先注册要序列化的类型(如 Person 类)。这样,在序列化时,它才能知道如何处理这些类型。

3. 无需注册的快速序列化(Registration-free fast serialization)

定义:使用现代序列化库,不需要显式注册数据类型,库会自动处理。

例子
假设我们使用Python的 pickle 模块进行序列化,不需要注册类型。

import pickle

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

person = Person('Bob', 30)
with open('person.pkl', 'wb') as f:
    pickle.dump(person, f)

在这个例子中,我们使用 pickle.dump 方法直接序列化 Person 对象,而不需要先注册 Person 类。pickle 会自动处理类型信息,实现快速序列化。

总结

  • 序列化:将对象转换为字节流。
  • 注册:在使用某些序列化库时,需要先告诉库有哪些类型的数据需要序列化。
  • 无需注册的快速序列化:使用现代序列化库,不需要显式注册数据类型,库会自动处理。

解释完成。
在这里插入图片描述

图 11 说明了 Chukonu 驱动程序和 Chukonu 库如何交互,以在 JVM 和本机环境之间提供安全高效的显式指针传递机制。

JVM 世界中的 Chukonu 驱动程序向 Chukonu 库提供 DAG 构建和评估功能。这是通过 JNA 提供的,其中 Chukonu 驱动程序将 Java 函数作为本机函数指针传递给 Chukonu 库。

Chukonu 库中的 DAG 由 Spark RDD 支持。Chukonu 库中的每个对象都由 Chukonu 驱动程序中的 Java 对象支持。Java 对象保存对象的指针。完成时,Java 包装器会释放底层的 Chukonu 对象。但是,由于 Java 垃圾回收而延迟的最终化会膨胀内存使用量,因此希望尽快释放 Java 包装器,而无需等待 GC 启动。
Chukonu 强制执行迁入迁出终身合约,以实现正确的及早对象释放。例如,要在 JVM 世界中调用本机函数,**传递给该函数的指针被认为已移交给它。此后,JVM 站点无法再访问已移动的指针,本机函数有责任释放输入指针或将其移动到其他位置。**这种生命周期合约可能会遇到一些不允许迁入的例外情况,例如,当内存中缓存的数据稍后将被重用,不应迁入时。为了解决这个问题,Chukonu 将 Chukonu 对象封装在 std::shared_ptr 中,这是一个由 C++ 标准提供的智能引用机制,它提供类似于 Java 中引用的自动生命周期管理。对于内存中缓存的数据,原始指针指向的引用会被复制,然后迁入。

技术细节,看不太懂,大概意思是,当java将指针传给cpp之后,就在java管理中丢弃这个指针,方便启动java的垃圾回收,剩下的事情全部交给c++

5.3 其他开销消除

Chukonu引擎也消除了其他开销,包括快速数据序列化和加速数据加载方法。
5.3.1 快速数据序列化。Cereal支持从/到标准I/O流的序列化,这是一种常见的做法,并且足够通用,可以支持每种可序列化的类型。然而,它的局限性在于其性能:基于I/O流的序列化效率低下。Chukonu提出了一种数据序列化技术,该技术为紧凑的数据布局提供了一种基于指针传递的快速路径,该技术将数据序列化过程分为两个阶段:元数据序列化和数据序列化,如图12所示。
在这里插入图片描述

元数据序列化考虑到其通用性,使用基于流的序列化,并使其与cereal兼容。它适用于序列化元数据,例如Chukonu RDD表示中的依赖项、生产者和UDF。数据序列化是一种快速路径,支持对紧凑数据布局中连续内存区域的零拷贝序列化,仅涉及少量的指针传递开销,并且不会产生大量的数据复制开销。

技术细节

6. 实验结果

我们通过评估原型系统Chukonu的性能,并将其与原生Apache Spark、基于Apache Spark的特定领域框架以及两个纯原生大数据框架Thrill和Husky的性能进行比较,从而检验了构建在Apache Spark之上的大数据框架的优势和局限性。框架:Thrill和Husky。我们在四个典型的大数据领域评估了这些方法:非结构化分析、图计算、机器学习和结构化分析。为前三个领域中的每一个领域选择了两个具有代表性的大数据应用程序,并选择了TPC-DS [33]的所有查询来代表结构化分析。总而言之,我们在整个评估过程中研究了以下问题:
(1) How much performance can be improved by using Chukonu from the perspective of Spark users? (§6.2)
(1)从Spark用户的角度来看,使用Chukonu可以提高多少性能?(§6.2)
(2) How effective is the compile-time optimizations and the elimination of Spark integration overheads? (§6.3)
(2)编译时优化和消除Spark集成开销的效果如何?(§6.3)
(3) Does Chukonu consume less memory than Spark? (§6.4)
(3)Chukonu是否比Spark消耗更少的内存?(§6.4)
(4) What are Chukonu’s advantages compared with state-ofthe-art pure-native big data frameworks? (§6.5, §6.6, §6.7)
(4)与最先进的纯原生大数据框架相比,Chukonu的优势是什么?(§6.5,§6.6,§6.7)
(5) How much room is Chukonu leaving for future improvement? (§6.8, §6.9)
(5)chukonu为未来改进留下了多少空间?(§6.8,§6.9)

6.1 实验方法

我们在我们的Hadoop集群上运行了实验,该集群由
8个节点组成,每个节点具有56个核心(两个Intel E5-2680 v4处理器)、256GB主内存、一个100Gbps网络(InfiniBand EDR,配置为IPoIB以将其作为普通的TCP/IP网络公开)以及一个6.4T B NVMe SSD,可以提供5.3GB/s的带宽。分布式文件系统(HDFS)和集群资源管理器(YARN)由Hadoop 3.0.0提供。Apache Hive 3.1.2的元数据存储被部署用于存储结构化分析的表的元数据。Apache Spark 3.0最近发布,并且比其前身具有显着的性能增强,我们在评估中使用了它。Spark的shuffle服务在YARN中配置,以管理中间结果。由于Spark 3.0同时支持Java 8和Java 11,因此我们也使用了较新版本的JVM(Oracle JDK 11.0.8),因为它具有更好的性能。Chukonu和Spark程序都通过YARN提交,其中Spark执行器的每个容器都有7个核心和22GB的内存。这遵循了避免使用大型执行器的惯例,因为这会妨碍性能[5]。Chukonu和Chukonu程序由GCC 10.1编译。
我们选择了六个具有代表性的大数据应用。WC(词频统计,统计给定语料库中单词的出现次数)和TS(TeraSort,对具有10字节键和90字节记录的分布式数据集进行全局排序)被选为非结构化分析的代表,这些分析是非迭代型工作负载,涉及处理具有异构数据格式并存储在分布式文件系统中的大型数据集,并可选择将结果写回分布式文件系统。PR(PageRank)和CC(弱连接组件)被选为图计算的代表。KM(K-Means2)和LR(逻辑回归)被选为机器学习的代表。
Apache Spark3 的示例应用程序被用作参考 Spark 应用程序,然后手动移植到 Chukonu 作为 Chukonu 程序。还评估了其他几个基线系统,以便与 Chukonu 进行比较。PR 和 CC 是使用 GraphX [19] 实现的,GraphX 是一个构建在 RDD API 之上的图计算框架。KM 和 LR 是使用 MLLib [25] 实现的,MLLib 是一个构建在 RDD API 之上的机器学习框架,但用户可以使用 RDD API 和 DataFrame API。在 MLlib 中,数值计算通过使用快速的本地 BLAS 库来加速。Spark 和 Chukonu 的 KM 和 LR 都是直接使用 Scala 和 C++ 实现的,没有本地 BLAS 加速。为了检查从 Spark 使用本地 BLAS 库带来的改进,我们还为 Spark 实现了 KM 和 LR 的加速版本(称为 SparkBLAS),使用了 MLlib 中捆绑的本地 BLAS 库。Project Tungsten [7] 通过使用类似 SQL 的 DataFrame API 和查询规划器 [10] 进行不安全序列化数据处理来优化 Apache Spark,应将其视为调查其改进的基线。WC 和 TS 直接使用 DataFrame API 实现。PR 和 CC 是使用 GraphFrame [12] 实现的,GraphFrame 是一个构建在 DataFrame API 之上的图计算框架。Thrill4 和 Husky5 也在实验中进行了测试,以揭示它们作为参考的本地性能。大多数应用程序在该框架中都有内置实现,我们实现了其余部分(Thrill6 的 CC 和 TS,Husky7 的 KM)。

在这里插入图片描述
数据集总结于表2。数据集以文本格式存储在HDFS中,唯一的例外是TS,它需要二进制格式。报告端到端时间,即从应用程序开始到结束的时间,可能包括从HDFS读取、预处理数据和写入HDFS所需的时间。为了适当地预热JVM,每个应用程序的性能在同一会话中测量三次,并报告后两次的平均值。PR、KM和LR在单次运行中执行了10次迭代。相反,CC被迭代直到在单次运行中收敛。
自 Spark 3.0 以来,Spark 提供了监控功能,可以定期以 CSV 文件的形式报告每个执行器的性能指标,这使得分析和查找应用程序的瓶颈变得容易。我们的评估使用了 Spark 的性能监控功能,采样间隔为一秒。集群的时钟偏差在 100 毫秒以内,因此可以通过时间戳聚合集群中节点的指标以形成总体指标,这是可以接受的。由于篇幅限制,我们只报告感兴趣的指标。
为了评估TPC-DS基准测试,我们使用了Databricks8开发的数据生成器和查询脚本。选择了1,000的比例因子,以便生成1TB的数据集。表的元数据存储在Apache Hive的元存储中,表的数据以Snappy压缩的Parquet文件格式存储在HDFS中。我们实现了一个翻译器,将SparkSQL的物理计划翻译成Chukonu程序的C++源代码。Chukonu共享Spark的执行计划,这确保了Chukonu和Spark之间的公平比较。由于Chukonu的DAG拆分设计,输入数据格式和DAG调度带来的复杂性被转移到Spark,从而实现了一个简单的实现:翻译器使用了2K行Scala代码,运算符使用了1.9K行C++代码。


网站公告

今日签到

点亮在社区的每一天
去签到