liusheng / liusheng.github.io

Liusheng's blog
http://liusheng.github.io
5 stars 1 forks source link

Hadoop ARM性能优化点梳理 #34

Open liusheng opened 3 years ago

liusheng commented 3 years ago

Hadoop ARM性能优化点梳理

0. 思路

Hadoop主要是由Java实现的,Java本身是一种跨平台语言,不区分x86还是Arm架构,因此,对于Hadoop的绝大部分代码实现,我们很难针对ARM平台做出相对于x86平台的差异化性能优化。除此之外,Hadoop还包含Native Library实现,所谓的Native Library指的是和系统平台相关的代码实现,最终编译成.so文件,在Hadoop里面指的是少量C代码实现的功能,主要包括libhadoop.solibhdfs.so等。

对于libhadoop.so来说,主要包含以下几部分功能,参加官方文档

对于libhdfs.so,其实现了基于C语言的HDFS API的JNI接口。具体见文档。从Hadoop代码来看,很多C代码实现的模块功能在Hadoop的官方文档中都没有相关介绍,具体看下面的分析。

1. 代码分析

Hadoop本地库代码实现都是C代码,在Hadoop的代码中,native代码一般的路径为<module name>/src/main/native, 编译后为.so文件,在Hadoop源码编译的时候,通过指定-Pnative来编译native库,Hadoop代码中native代码编译出的.so文件主要包括:

比如hadoop-common-project/hadoop-common/src/main/native目录下面就包含了压缩库,CRC32等native代码。那么让我们来看一下Hadoop中C代码的分布:

  1. hadoop-common-project/hadoop-common/src/main/native 该目录下面包含了Hadoop中最主要的本地代码实现,包括:

    • crypto 主要包括Hadoop 对接OpenSSL加解密的接口实现
    • io 这一部分包括3块功能:1)压缩库的本地接口实现(bzip2、lz4、snappy、zlib、zstd);2) 基于ISA-L库的纠删码实现(erasurecode);3)本地IO
    • net 实现DomainSockets接口
    • security 包括linux系统中Hadoop集群的用户和组的管理接口
    • util 主要包括CRC32 checksum的功能实现
    • yarn 适配Windows操作系统上nodemanager的进程管理相关的功能
  2. hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native 该目录下面包括了HDFS相关的本地代码实现,主要包括:

    • fuse-dfs Fuse-DFS实现将HDFS挂载到操作系统中当成本地文件系统使用。这里面主要包括了文件系统的操作接口(cp,ls,more 等等)
    • libhdfs HDFS API接口的C语言实现
    • libhdfsapp
  3. hadoop-tools/hadoop-pipes/src/main/native

    Hadoop-Pipes Hadoop MapReduce C++语言的接口实现,即用来支持C++语言的MapReduce应用的执行。

  4. hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-nativetask/src/main/native

    NativeTask是在Hadoop 3.0新增加的一个优化功能,是一个Hadoop MapReduce的高性能C++ API和运行时,这一部分功能是单独封装成为一个SO文件:libnativetask.so

  5. hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native 主要实现了nodemanager具体执行MapReduce应用的时候所使用的的容器相关的功能,包括:

    • container-executor 基于Cgroup和RunC实现的容器能力,用来执行Hadoop应用的单个task
    • oom-listener 监听CGroup可能发生的OOM异常

2. hadoop-common中Native实现

2.1 压缩库 CodeC

通过在mapred-site.xml中增加如下配置来开启:

    <property>
      <name>mapreduce.map.output.compress</name>
      <value>true</value>
    </property>
    <property>
      <name>mapreduce.map.output.compress.codec</name>
      <value>org.apache.hadoop.io.compress.SnappyCodec</value>
    </property>
    <property>
      <name>mapreduce.map.env</name>
      <value>LD_LIBRARY_PATH=/usr/local/lib</value>
    </property>
    <property>
      <name>mapreduce.reduce.env</name>
      <value>LD_LIBRARY_PATH=/usr/local/lib</value>
    </property>

上述配置分别配置了map阶段输出开启压缩,map阶段输出压缩的CodeC,以及使用压缩库本地路径。

在此之前,我们已经完成了Hadoop目前所支持的各种压缩库的性能对比测试和调优,具体可以参加之前的测试结果。

2.2 纠删码 ErasureCode

在hadoop-common的C代码实现io/erasurecode目录下面有hadoop基于ISA-L的纠删码实现,纠删码的目的是在HDFS中默认使用3副本来存储数据,每一个数据都有200%的额外存储开销和其他资源消耗(比如网络带宽)。对于比较低I/O活动的暖数据和冷数据,在正常操作期间对其额外的副本很少访问,单是仍然消耗与第一副本同样的资源。

一个改进措施就是使用纠删码来替代副本机制,使用更少的存储空间提供容错级别。在Hadoop中通过core-site.xml如下配置开启:

<property>
  <name>io.erasurecode.codec.rs.rawcoders</name>
  <value>rs_native,rs_java</value>
</property>
<property>
  <name>io.erasurecode.codec.rs-legacy.rawcoders</name>
  <value>rs-legacy_java</value>
</property>
<property>
  <name>io.erasurecode.codec.xor.rawcoders</name>
  <value>xor_native,xor_java</value>
</property>

hdfs ec子命令提供了纠删码相关的管理命令。

由于之前Hadoop的native库在ARM机器上不支持ISA-L,因此应该也不支持当前的纠删码实现,目前ISA-L已经支持了ARM平台了,可以考虑在Hadoop中适配,测试其性能,看能否有所提升。

2.3 NativeIO

代码位置: hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/io/nativeio/NativeIO.c

对应的JNI实现位置:hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/io/nativeio/NativeIO.java

该部分代码主要实现了一些有错做系统差异的系统IO或者文件设备操作,对于没有操作系统差异或者依赖于特定硬件特性支持的操作,由Java本身的能力提供。主要包括:

这一块需要逐个分析这些接口函数以及分析PMDK看是否能够在ARM平台上做优化。

2.4 net相关DomainSocket实现

这一块基于C语言实现了DomainSocket功能,DomainSocket就是为了前文提到的HDFS Short-Circuit Local Reads 功能而实的,具体可以参见Short-Circuit文档。

按照文档就介绍,HDFS Short-Circuit Local Reads 特性对于Hadoop应用读写HDFS性能会有提升,但是经过我的初步测试,结果显示没有明显的提升,还需要进一步研究。

2.5 security相关

这一块主要实现了Hadoop安全认证相关的功能,由于Hadoop各服务的安全认证依赖于操作系统本身的usergroup 信息,对于不同的操作系统,其关于用户和组信息的查询存在差异性,因此这一块用C语言实现。

这一部分native代码和Hadoop ARM平台性能优化没有太大关系

2.6 crc32 utils实现

实现了操作系统平台相关的CRC32 checksum的功能。在MapReduce应用执行的过程中,CRC校验的计算也是一个频繁的操作,其性能对于整个Hadoop应用处理流程也有一定影响,但是这一块经过之前的分析,在其ARM平台适配的代码bulk_crc32_aarch63.cc中已经做了一些性能相关的优化。代码中也实现了ARM平台的CRC32 硬件加速的优化,需要进一步深入了解代码,看能否发掘到优化点,有没有可能利用KAE的做优化 ?

2.7 YARN windows适配

主要是Windows操作系统上应用执行container相关的一些适配,有一个专门的winutils.dll动态链接库。和ARM平台性能优化无关。

3. HDFS相关native实现

3.1 fuse-dfs

fuse-dfs是HDFS中实现的一个独立的功能,实现了将HDFS挂载到操作系统上当做普通的文件系统使用。这部分代码主要实现了文件系统常用的一些操作命令,如:cp,ls,more 等, 和ARM平台性能优化无关

3.2 libhdfs

libhdfs是Hadoop分布式文件系统HDFS的基于JNI的C API,提供了一组C语言实现的管理和操作HDFS文件系统的的APIs。目前看来这一块的C代码也和ARM平台性能优化无关

3.3 libhdfsapp

代码位置:hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp

这一部分代码比较多,实际上是单独实现了一个叫libhdfs++的组件,libhdfs++是用C++11实现的比较先进的HDFS client。主要对于大批量并行处理应用(MMP, Massive Parallel Processing)并发访问数以千计的HDFS文件做了优化

libhdfs++的亮点:

目前还不确定这块的功能对于用户MapReduce应用是否有特殊要求,需要进一步研究。

4. Hadoop-pipes

Hadoop-pipes 实际上就是MapReduce C++接口的代称。为了方便C/C++用户编写MapReduce程序而设计的工具,其设计思想是将应用裸机相关的C++代码放在单独进程中,然后通过SOcket让Java代码于C++代码同行以完成数据计算,不同于使用标准输入于标准输出来实现map代码和reduce代码之间的Streaming编程。

这一块功能对于用户的MapReduce应用有特别的要求,对于ARM平台性能优化意义也不大

5. Hadoop native task实现

具体参见:https://issues.apache.org/jira/browse/MAPREDUCE-2841

Intel为了改进Hadoop中应用执行性能而提出的一个特性。在Hadoop代码中没有相关的文档介绍(吐槽一下~),但是在上年的Issue中的附件中有该特性的设计文档,Issue本身也经历了一个漫长的过程最终才合入到主干(11年8月提出到14年9月合入),在Issue下面也有很多讨论内容。根据设计文档介绍,NativeTask是一个针对Hadoop MapReduce高性能C++ API和Runtime实现。之所以叫做NativeTask是因为它是一个本地化计算单元,只关注数据处理,也就是Hadoop MapReduce的Task中正真干活的部分,而不关注资源管理,job调度以及容错等能力。 总体来看,像是将MapReduce Task执行流程中中除了资源调度和管理相关的部分全部用C语言重写了一遍。

通过在mapred-site.xml中配置开启:

<property>
  <name>mapreduce.job.map.output.collector.class</name>
  <value>org.apache.hadoop.mapred.nativetask.NativeMapOutputCollectorDelegator</value>
</property>

上述配置会对Map-Reduce流程中Mapper输出时候的sortspill流程调用native实现来处理。

这一块功能,我觉得可以重点研究一下,从文档介绍来看,NativeTask实现的目的也是为了改进Hadoop性能的。

6 Nodemanager Native实现部分

这一部分用C语言实现了nodemanager的container-executor,也就是执行MapReduce应用的task的执行容器,基于RunC和Cgroup等技术实现资源隔离实现轻量化的容器来执行Task。目前还没有深入了解过。初步任务这一块功能对于MapReduce应用本身执行速度应该没有太大关系,ARM平台做性能优化可能性比较小。代码比较复杂,还没有做深入的了解。

7. 其他

对于Hadoop集群的性能调优,很多时候我们使用TeraSort应用来测试验证,这也是业界的一种通用的方法,为了方便多次执行TeraSort测试,以及对于测试结果的整理,我写了一个小脚本方便用来执行TeraSort,脚本能够自动收集测试结果。方便后续的调优测试。

https://github.com/liusheng/dockerfile/tree/master/hadoop/hadoopbench

qwerhyp commented 1 year ago

Hadoop ARM性能优化点梳理

0. 思路

Hadoop主要是由Java实现的,Java本身是一种跨平台语言,不区分x86还是Arm架构,因此,对于Hadoop的绝大部分代码实现,我们很难针对ARM平台做出相对于x86平台的差异化性能优化。除此之外,Hadoop还包含Native Library实现,所谓的Native Library指的是和系统平台相关的代码实现,最终编译成.so文件,在Hadoop里面指的是少量C代码实现的功能,主要包括libhadoop.solibhdfs.so等。

对于libhadoop.so来说,主要包含以下几部分功能,参加官方文档

对于libhdfs.so,其实现了基于C语言的HDFS API的JNI接口。具体见文档。从Hadoop代码来看,很多C代码实现的模块功能在Hadoop的官方文档中都没有相关介绍,具体看下面的分析。

1. 代码分析

Hadoop本地库代码实现都是C代码,在Hadoop的代码中,native代码一般的路径为<module name>/src/main/native, 编译后为.so文件,在Hadoop源码编译的时候,通过指定-Pnative来编译native库,Hadoop代码中native代码编译出的.so文件主要包括:

  • libhadoop.so
  • libhdfs.so
  • libhdfsapp.so
  • libnativetask.so

比如hadoop-common-project/hadoop-common/src/main/native目录下面就包含了压缩库,CRC32等native代码。那么让我们来看一下Hadoop中C代码的分布:

  1. hadoop-common-project/hadoop-common/src/main/native 该目录下面包含了Hadoop中最主要的本地代码实现,包括:

    • crypto 主要包括Hadoop 对接OpenSSL加解密的接口实现
    • io 这一部分包括3块功能:1)压缩库的本地接口实现(bzip2、lz4、snappy、zlib、zstd);2) 基于ISA-L库的纠删码实现(erasurecode);3)本地IO
    • net 实现DomainSockets接口
    • security 包括linux系统中Hadoop集群的用户和组的管理接口
    • util 主要包括CRC32 checksum的功能实现
    • yarn 适配Windows操作系统上nodemanager的进程管理相关的功能
  2. hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native 该目录下面包括了HDFS相关的本地代码实现,主要包括:

    • fuse-dfs Fuse-DFS实现将HDFS挂载到操作系统中当成本地文件系统使用。这里面主要包括了文件系统的操作接口(cp,ls,more 等等)
    • libhdfs HDFS API接口的C语言实现
    • libhdfsapp
  3. hadoop-tools/hadoop-pipes/src/main/native Hadoop-Pipes Hadoop MapReduce C++语言的接口实现,即用来支持C++语言的MapReduce应用的执行。
  4. hadoop-mapreduce-project/hadoop-mapreduce-client/hadoop-mapreduce-client-nativetask/src/main/native NativeTask是在Hadoop 3.0新增加的一个优化功能,是一个Hadoop MapReduce的高性能C++ API和运行时,这一部分功能是单独封装成为一个SO文件:libnativetask.so
  5. hadoop-yarn-project/hadoop-yarn/hadoop-yarn-server/hadoop-yarn-server-nodemanager/src/main/native 主要实现了nodemanager具体执行MapReduce应用的时候所使用的的容器相关的功能,包括:

    • container-executor 基于Cgroup和RunC实现的容器能力,用来执行Hadoop应用的单个task
    • oom-listener 监听CGroup可能发生的OOM异常

2. hadoop-common中Native实现

2.1 压缩库 CodeC

通过在mapred-site.xml中增加如下配置来开启:

    <property>
      <name>mapreduce.map.output.compress</name>
      <value>true</value>
    </property>
    <property>
      <name>mapreduce.map.output.compress.codec</name>
      <value>org.apache.hadoop.io.compress.SnappyCodec</value>
    </property>
    <property>
      <name>mapreduce.map.env</name>
      <value>LD_LIBRARY_PATH=/usr/local/lib</value>
    </property>
    <property>
      <name>mapreduce.reduce.env</name>
      <value>LD_LIBRARY_PATH=/usr/local/lib</value>
    </property>

上述配置分别配置了map阶段输出开启压缩,map阶段输出压缩的CodeC,以及使用压缩库本地路径。

在此之前,我们已经完成了Hadoop目前所支持的各种压缩库的性能对比测试和调优,具体可以参加之前的测试结果。

2.2 纠删码 ErasureCode

在hadoop-common的C代码实现io/erasurecode目录下面有hadoop基于ISA-L的纠删码实现,纠删码的目的是在HDFS中默认使用3副本来存储数据,每一个数据都有200%的额外存储开销和其他资源消耗(比如网络带宽)。对于比较低I/O活动的暖数据和冷数据,在正常操作期间对其额外的副本很少访问,单是仍然消耗与第一副本同样的资源。

一个改进措施就是使用纠删码来替代副本机制,使用更少的存储空间提供容错级别。在Hadoop中通过core-site.xml如下配置开启:

<property>
  <name>io.erasurecode.codec.rs.rawcoders</name>
  <value>rs_native,rs_java</value>
</property>
<property>
  <name>io.erasurecode.codec.rs-legacy.rawcoders</name>
  <value>rs-legacy_java</value>
</property>
<property>
  <name>io.erasurecode.codec.xor.rawcoders</name>
  <value>xor_native,xor_java</value>
</property>

hdfs ec子命令提供了纠删码相关的管理命令。

由于之前Hadoop的native库在ARM机器上不支持ISA-L,因此应该也不支持当前的纠删码实现,目前ISA-L已经支持了ARM平台了,可以考虑在Hadoop中适配,测试其性能,看能否有所提升。

2.3 NativeIO

代码位置: hadoop-common-project/hadoop-common/src/main/native/src/org/apache/hadoop/io/nativeio/NativeIO.c

对应的JNI实现位置:hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/io/nativeio/NativeIO.java

该部分代码主要实现了一些有错做系统差异的系统IO或者文件设备操作,对于没有操作系统差异或者依赖于特定硬件特性支持的操作,由Java本身的能力提供。主要包括:

  • PMDK PMDK是一个工具集方便系统管理员或者应用开发者来管理和访问持久化内存设备的,具体见github repo ,可以通过hadoop checknative -a 命令来查看当前Hadoop包和操作系统是否支持PMDK。
  • 操作系统差异的一些文件相关的命令,比如Windows系统和Linux系统的对文件statmlock, open等操作的不同实现等。

这一块需要逐个分析这些接口函数以及分析PMDK看是否能够在ARM平台上做优化。

2.4 net相关DomainSocket实现

这一块基于C语言实现了DomainSocket功能,DomainSocket就是为了前文提到的HDFS Short-Circuit Local Reads 功能而实的,具体可以参见Short-Circuit文档。

按照文档就介绍,HDFS Short-Circuit Local Reads 特性对于Hadoop应用读写HDFS性能会有提升,但是经过我的初步测试,结果显示没有明显的提升,还需要进一步研究。

2.5 security相关

这一块主要实现了Hadoop安全认证相关的功能,由于Hadoop各服务的安全认证依赖于操作系统本身的usergroup 信息,对于不同的操作系统,其关于用户和组信息的查询存在差异性,因此这一块用C语言实现。

这一部分native代码和Hadoop ARM平台性能优化没有太大关系

2.6 crc32 utils实现

实现了操作系统平台相关的CRC32 checksum的功能。在MapReduce应用执行的过程中,CRC校验的计算也是一个频繁的操作,其性能对于整个Hadoop应用处理流程也有一定影响,但是这一块经过之前的分析,在其ARM平台适配的代码bulk_crc32_aarch63.cc中已经做了一些性能相关的优化。代码中也实现了ARM平台的CRC32 硬件加速的优化,需要进一步深入了解代码,看能否发掘到优化点,有没有可能利用KAE的做优化 ?

2.7 YARN windows适配

主要是Windows操作系统上应用执行container相关的一些适配,有一个专门的winutils.dll动态链接库。和ARM平台性能优化无关。

3. HDFS相关native实现

3.1 fuse-dfs

fuse-dfs是HDFS中实现的一个独立的功能,实现了将HDFS挂载到操作系统上当做普通的文件系统使用。这部分代码主要实现了文件系统常用的一些操作命令,如:cp,ls,more 等, 和ARM平台性能优化无关

3.2 libhdfs

libhdfs是Hadoop分布式文件系统HDFS的基于JNI的C API,提供了一组C语言实现的管理和操作HDFS文件系统的的APIs。目前看来这一块的C代码也和ARM平台性能优化无关

3.3 libhdfsapp

代码位置:hadoop-hdfs-project/hadoop-hdfs-native-client/src/main/native/libhdfspp

这一部分代码比较多,实际上是单独实现了一个叫libhdfs++的组件,libhdfs++是用C++11实现的比较先进的HDFS client。主要对于大批量并行处理应用(MMP, Massive Parallel Processing)并发访问数以千计的HDFS文件做了优化

libhdfs++的亮点:

  • 事件驱动的架构
  • 零拷贝(Zero-copy)的API实现
  • 使用Continuation Pass Style实现的异步API
  • 支持Windows、Linux、Mac OS

目前还不确定这块的功能对于用户MapReduce应用是否有特殊要求,需要进一步研究。

4. Hadoop-pipes

Hadoop-pipes 实际上就是MapReduce C++接口的代称。为了方便C/C++用户编写MapReduce程序而设计的工具,其设计思想是将应用裸机相关的C++代码放在单独进程中,然后通过SOcket让Java代码于C++代码同行以完成数据计算,不同于使用标准输入于标准输出来实现map代码和reduce代码之间的Streaming编程。

这一块功能对于用户的MapReduce应用有特别的要求,对于ARM平台性能优化意义也不大

5. Hadoop native task实现

具体参见:https://issues.apache.org/jira/browse/MAPREDUCE-2841

Intel为了改进Hadoop中应用执行性能而提出的一个特性。在Hadoop代码中没有相关的文档介绍(吐槽一下~),但是在上年的Issue中的附件中有该特性的设计文档,Issue本身也经历了一个漫长的过程最终才合入到主干(11年8月提出到14年9月合入),在Issue下面也有很多讨论内容。根据设计文档介绍,NativeTask是一个针对Hadoop MapReduce高性能C++ API和Runtime实现。之所以叫做NativeTask是因为它是一个本地化计算单元,只关注数据处理,也就是Hadoop MapReduce的Task中正真干活的部分,而不关注资源管理,job调度以及容错等能力。 总体来看,像是将MapReduce Task执行流程中中除了资源调度和管理相关的部分全部用C语言重写了一遍。

通过在mapred-site.xml中配置开启:

<property>
  <name>mapreduce.job.map.output.collector.class</name>
  <value>org.apache.hadoop.mapred.nativetask.NativeMapOutputCollectorDelegator</value>
</property>

上述配置会对Map-Reduce流程中Mapper输出时候的sortspill流程调用native实现来处理。

这一块功能,我觉得可以重点研究一下,从文档介绍来看,NativeTask实现的目的也是为了改进Hadoop性能的。

6 Nodemanager Native实现部分

这一部分用C语言实现了nodemanager的container-executor,也就是执行MapReduce应用的task的执行容器,基于RunC和Cgroup等技术实现资源隔离实现轻量化的容器来执行Task。目前还没有深入了解过。初步任务这一块功能对于MapReduce应用本身执行速度应该没有太大关系,ARM平台做性能优化可能性比较小。代码比较复杂,还没有做深入的了解。

7. 其他

对于Hadoop集群的性能调优,很多时候我们使用TeraSort应用来测试验证,这也是业界的一种通用的方法,为了方便多次执行TeraSort测试,以及对于测试结果的整理,我写了一个小脚本方便用来执行TeraSort,脚本能够自动收集测试结果。方便后续的调优测试。

https://github.com/liusheng/dockerfile/tree/master/hadoop/hadoopbench