上篇中提到了很多Alluxio为了减速读取数据做的各种各样的优化,那么对于用户来说还有一个十分重要的问题——在机器学习训练中应用Alluxio读数据到底有多快?

比如说数据是贮存在云上的,那从Alluxio读会不会比间接从云上读更快,具体能快多少?有了比拟咱们能力判断出应用Alluxio到底能不能取得训练性能晋升,能晋升多少。

在Alluxio中有一个本人的性能测试工具,叫StressBench,它能够通过在肯定工夫内进行一系列操作,比方读、写或者元数据操作,来测试不同环境下Alluxio各个局部的处理速度。它的长处在于不依赖内部组件,只须要一个运行的Alluxio集群,这个集群能够是只有一个节点,一个master,一个worker,也能够是一个更大的集群,有更多的节点。

在StressBench中退出了fuse StressBench,就是一个对于fuse的测试,用来测试通过POSIX接口,从Alluxio中读取文件的速度。那么fuse StressBench除了须要一个Alluxio集群之外,还须要额定在每一个worker节点上都将Alluxio通过fuse挂载到本地文件系统。

既然要测试性能,最次要的一个目标就是能够量化读性能,在有这个测试工具之前,只能说从Alluxio POSIX接口读文件要比从云上读快很多,然而具体有多快,快多少,用户并没有方法测量,当初有了这个工具,就能够拿出数据证实的确快,也能够展现有多快。

最简略状况下,就是Alluxio集群只有一个节点,一个worker和一个读文件的挂载点,在测试中第一步咱们将测试文件写入Alluxio worker,第二步通过POSIX API去读它们,测量在一段时间内读了多少字节,就能够返回在这段时间内读的速度。整个过程只须要跑两个命令,就能够看到单机状况下的读性能了。

相比单机测试,集群测试更贴近理论生产中的机器学习场景。其中有多个节点运行了Alluxio worker,每个节点都通过fuse挂载了Alluxio,每个节点上都有本人从Alluxio中读取文件的工作,当然理论生产环境中训练还有计算的局部,咱们这里fuse StressBench只是测试读取数据的行为。

咱们利用了Alluxio的job service来模仿这个场景,能够了解为每台机器都有一个程序在读文件,就像理论训练中每个节点上的训练任务,每个节点上的fuse StressBench各自计算肯定工夫内本人读了多少字节,从而就能够晓得每个节点读的速度,进而晓得整个集群的速度。同样,整个过程只须要两个命令就能够实现。

这里用单节点状况做了一个简略的例子,有一个单节点的Alluxio集群,一个worker和一个本地用于读的挂载点,通过bin/alluxio runClass来跑StressBench测试工具,能够看到还同时提供了写的挂载点,这一步用来将测试文件写进Alluxio的门路,而后指定操作为写文件。咱们还提供了线程的数量,一共将测试文件写进多少个文件夹,每个文件夹有多少文件,以及每个文件的大小,期待命令返回之后,这些文件就会全副贮存到了Alluxio worker外面。

文件写好后进行读操作,在读测试文件时,用雷同的这个 bin/alluxio runClass来启动测试。接下来指定读文件的挂载门路、读的形式、线程数量、测试工夫,以及和方才生成文件是一样的,文件夹数量,每个文件夹有多少个文件和文件大小。可能有人发现咱们这个测试中读和写的本地门路不一样,是因为在生成测试文件的时候fuse就会有一些缓存,如果再去同一个文件夹读的话,这些缓存会影响测试读性能的准确性,所以用了不同的挂载点来防止这个问题。也就是在开始测试前挂载的时候,将Alluxio挂载到两个不同的门路,一个负责写,一个负责读。这样的话写文件时产生的缓存,就不会影响到读文件了。

这是读测试返回后生成的局部后果。能够看到这个单节点达到了每秒183.1兆的读取速度。咱们方才指定了一些参数,包含门路、文件大小等等,以及如果在测试中呈现了任何谬误,也会在这里显示。

目前这个fuse StressBench的测试工具有两个次要的限度,第一个是它只反对读性能的测试,临时还不反对写数据或者元数据操作的测试。之前的写操作只是为了测试读性能生成的数据,它本人并不能作为一个测试。另外一个就是只反对读用它自身生成的文件,不能应用任意文件来进行测试,否则会报错,次要起因是因为如果用任意文件测试的话,很难保障资源平均分配,可能有些线程很早就完结了它的工作,然而有些线程前面又读了很久,那这样的话这个测试后果就不精确了。

想理解更多对于fuse StressBench内容能够查看官网文档中对于fuse StressBench的局部,其中有对如何应用这个工具更为具体的介绍。接下来更为具体的介绍一下如何在Alluxio POSIX API中debug以及性能剖析指南。

首先故障排除,是fuse有个很大的问题,在于具体的故障起因不会显示在咱们的命令输入中,这次要是fuse自身自带的一个限度,咱们只能确定错误码是什么,但这个错误码可能翻译给用户之后,它并没有蕴含那么多的信息。比如说咱们这个命令可能ls一个Alluxio fuse中的文件,而后会呈现一个I/O报错,但无奈具体晓得到底是什么样的问题,而具体的问题显示在日志文档中,就包含如果独自起fuse过程的话,你须要查看fuse.log,而如果fuse起在worker过程中的话,你查看worker.log,它外面会显示具体的起因,有可能是因为没有方法去连贯master,导致读不到数据之类的种种原因。

能够对日志进行等级批改,批改等级为debug之后,咱们能够取得更加具体的信息。批改日志等级次要有两种形式,一种是通过批改Alluxio的conf/log4j.properties间接批改日志等级,咱们能够对任意的包,把它批改成debug这种日志等级。

如果fuse起在worker过程中,能够动静地通过一个Alluxio的CLI的命令,而后开启debug级别的日志输入。比如说logLevel这个命令,它能够把worker中所有Alluxio.fuse包的所有日志变成一个debug级别的日志输入,就能够不便咱们更好去排查呈现的问题。这些日志通过批改为debug,能够看到fuse各种操作执行程序以及消耗工夫,有助于发现到底是哪一些fuse操作花了比拟多的工夫,以及是否有一些非预期的执行程序。已经有过一些故障就来源于一些非预期的执行程序。

当fuse应用了Alluxio POSIX API之后,发现可能性能不如预期,那性能不如预期这个问题到底呈现在哪一个方面?

有几个层面:一个是利用,一个是fuse,还有Alluxio。性能不如预期的状况下,就要回过头来看这一整套货色。先理解一下整个fuse的工作流程,当用户收回了一个申请,比如说ls这个申请,它会把这个申请发送到fusekernel端,而后Kernel端进行肯定的解决之后,再返回到利用端,发送给Libfuse,而后Libfuse把这些命令变成一个能够自定义的模式,通过实现Libfuse,来实现咱们本人的文件系统。

然而Libfuse是写在C++上的,而Alluxio是写在Java外面的,就引入了另外一层形象叫JNIFuse,通过JNIFuse之后, Alluxio fuse再定义每个操作具体要执行什么样的具体的内容。比如说Alluxio fuse接到底层fuse的一个命令说ok,这个用户做了一个ls操作,曾经拿到了一系列的ls的指令相干内容,比如说你要读哪个文件的一些信息,而后Alluxio fuse要决定如何去应答这些操作,咱们拿到ls操作之后,就会去Alluxio集群内去问,这个用户要读文件a,能不能给我文件a的信息,而Alluxio fuse拿到这部分信息之后,再通过一系列的fuse返回给用户。

所以这其中其实蕴含了三个方面,一方面是应用层,利用是与fuse如何进行交互的;另一方面就是 fuse层,包含fuse,kernel,Libfuse以及JNIFuse,这些都统称为fuse层,就是 fuse是如何把一个应用层的命令转化给Alluxio去具体地实现。最初一层就是Alluxio层,包含Alluxio是如何实现fuse的,以及Alluxio整个零碎。

首先须要明确性能瓶颈是不是来源于Alluxio POSIX API,因为训练性能不佳,可能是来源于各种各样的起因,包含资源有余,包含算法和训练逻辑比拟迟缓,以及包含利用的元数据延时长或者数据吞吐低。而咱们就要剖析这个性能不佳起源是不是跟数据或者元数据相干,如果与其无关可能性能瓶颈并就不在Alluxio POSIX API这一层,那钻研它也就杯水车薪。

所以,须要先剖析性能瓶颈是否来源于Alluxio POSIX API,咱们能够比照Alluxio POSIX API与文件系统以及其余数据计划的性能差别,而且能够移除训练逻辑只留下数据相干操作,看看性能如何。

当咱们发现可能性能瓶颈来源于Alluxio POSIX API之后,咱们须要更多的去理解利用与Alluxio POSIX API之间的互动,比如说这个利用跑了哪些与POSIX API相干的指令,利用是如何呼叫这些指令的,它的呼叫程序是怎么样的,呼叫的并发度是怎么样的,这些信息都是须要提前理解的。

剖析到底是fuse还是Alluxio是性能瓶颈,咱们先应用了一个stack fuse来剖析这部分性能。

Stack fuse是跟Alluxio fuse应用了雷同的fuse组件,它也包含fuse kernel,Libfuse和JNIFuse,然而与Alluxio fuse不同的在于,它是间接对本地文件系统进行读写与以及元数据操作,不波及任何的Alluxio组件。

能够通过一个命令能把本地文件系统mount在另外一个文件夹,而后对这个mount文件夹进行一系列的操作来比照性能,通过比照本地文件夹以及stack fuse,能够理解fuse这个层的性能损耗,通过比照stack fuse以及Alluxio fuse,能够得出Alluxio的性能损耗。

之所以要理解是fuse还是Alluxio的性能瓶颈,也是因为如果是fuse的性能损耗,其实在Alluxio这端是做不了过多的性能晋升的,就是任何一个fuse的实现,包含s3 fuse之类的其余实现,它都会带有fuse原生的性能损耗,而fuse这种操作实现形式是很多年前就曾经实现了的,近期也没有什么大的扭转,也不会因为要把fuse利用在咱们的训练中,进行一个大版本的降级。而如果发现是Alluxio的性能损耗,就能够进一步去剖析到底是Alluxio中哪一部分出了问题,哪一部分能够进一步的优化来晋升训练性能。

Alluxio fuse的剖析包含几个方面,首先能够通过看一些指标,比如说Alluxio fuse的相干指标,发现有多少个文件正在并发读取,多少个文件正在并发写入,性能不好是不是因为有超高量的并发读取和写入的操作,而这个超高量的fuse方面的读取跟写入,可能并没有被Alluxio的线程池或者存储系统很好地撑持起来,这是第一步发现到底是什么性能问题的一个指标。

也能够剖析fuse相干操作的频繁度以及耗时。对于每个fuse操作都会记录相应的指标,比如说fuse每个读命令都会记录这个读命令被呼叫了多少次,均匀的实现工夫是多少?超过5%的读操作超过了多少工夫?从这些信息中咱们能够看出哪个fuse利用被呼叫的最多,而哪个fuse利用消耗了最长的工夫,是均匀工夫长还是偶然工夫长,这些信息都对进一步剖析性能瓶颈有十分重要的帮忙。比方发现fuse的读操作破费工夫最长,那咱们可能就专一于在读操作上,看看是哪一步的性能有肯定的损耗。如果是均匀工夫特地长的话,是不是波及到配置或者波及到存储系统的连贯的问题?那如果是均匀工夫十分短,然而有1%或者5%的操作花了十分长的工夫,是不是就波及到资源的抢夺?就能够进一步的去剖析性能瓶颈在哪里了。

还有一些更高级的追踪性能瓶颈的形式,能够通过指标零碎进行工夫追踪,比方fuse读这个操作十分的慢,就能够在fuse读操作外面加上一些timer metrics,而后一步一步的追踪上来,看看到底在哪里花了最长的工夫,最初针对那个方面进行性能的晋升。

除工夫追踪之外,还能够进行CPU资源、内存资源以及锁资源的追踪,有个利用叫Async profiler,它能够帮忙进行CPU memory以及锁的瓶颈的剖析,能够让咱们发现哪一个Alluxio的Java class花了最多的CPU资源或者最多的内存和锁资源,再或者能够对RPC指标进行追踪,目前反对gRPC和s3。

能够发现,比如说这些RPC操作十分的耗时,那到底是在哪里耗时呢?咱们的client对worker发送了一个申请,十分耗时,那到底是这个申请发送途中很耗时还是worker解决这个申请很耗时呢?就能够发现到底是worker解决慢,还是可能存在资源竞争的问题。这一整套剖析都是比拟有难度的,这里也放了一个性能剖析的案例,也就是咱们最近在一个百万小文件的场景下,通过性能剖析,把最高的吞吐从2400小文件每秒晋升到超过13,000小文件每秒。能够通过这个案例来具体看一下,咱们是如何做整一套性能剖析的,大家谢谢。