00:00
另外还有一种比较特殊的重分区方式,它跟轮旋分区有点相似,那使用呢,可能会比较特殊一点,那就是所谓的这个有时候会翻译成叫重缩放分区。因为我们知道本身就有规模范围这样的一个概念,那的话当然就是把当前的规模重新做一个调整,所以相当于是要做一个重缩放,那直观来看的话,它和轮询分区reb是非常相似的。区别在于啊,我们当前如果要调用的话,当然就是直接调用底层,它也是用了一个轮,也就是所谓的RO roby这样一个算法进行了轮巡,那它的区别呢?区区别在于并不是往下游所有的并行子分区里边去轮询发送,之前我们说是直接发牌嘛,对所有人去发牌,那现在呢,现在是。
01:08
按照某种分组。上面的这一组只在自己内部,针对当前的两个并行子任务轮询发牌,而下面的这一组呢,同样也只针对自己内部在。两个并行子任务之间轮巡发啊,所以接下来的这种操作可能就会涉及到我们上游,那就应该是两个并行子任务,然后下游是四个并行子任务,这个时候呢,刚好就可以把它们一分为二,分成两组。那么上边。前面的任务第一个并行分区输出的数据,那就只在当前这一组里边,对应下游任务的第一、第二个分区,而上游任务的第二个分区发出的数据。
02:00
经过重缩放分区轮巡发送之后,就只会在当前组里边,也就是下游任务的第三第四个分区里边去做轮询发送,诶,这就是的一个特点。那所以如果说之前的随机分区是洗牌所有人洗牌,轮巡分区是所有人轮发牌,那现在就相当于是分成了几组,在自己的小组内进行轮发牌。代码中的调用呢比较简单,那就是直接基于stream去调用一个方法就可以了,但是我们这里需要注意的是,想要明确的看到的效果,按照分组的这种情况去进行重新分配的话,那还需要对于当前的。S算子定义一个并行度,比如说我们应该是让S的并行度设成二,然后下面print的并行度设为四,这样的话就可以明确的看到分成了两个小组。
03:02
那我们可能会想到这样一种方法,它跟reb比起来之后,相当于就是只在自己小组内进行了一个重新的分配,重新的分区,那它比reb有什么优点呢?我们为什么要单独定义这样一种分区方式呢?其实我们会想到reb是在所有的并行子任务之间去做了一个。平均的轮巡发牌,那这种方式我们当前如果有多个task manager的话。每个task manager又有多个slot,如果是这样的一种情况,那很显然我们当前对于数据的发送就会跨task manager去进行数据的传输,那么在这个过程当中,很明显就会需要网络传输,那肯定就会让效率显得比较低下,那这个时候假如说我们配置的pass slot数量刚好是可以匹配起来,比如说我们这里呢,就是两个task manager,每个task manager配置了两个slot。
04:12
那这样很明很明显,我们就应该可以把所有的slot,所有的分区按照task manager分成两组,那如果刚好它的上游任务并行度就是二的话,很明显这两个可以分别在每一个task manager上去执行一个当前的分区并行子任务。那接下来我们就只在当前task manager内部去做一个数据的分配调整,这很显然就会避免网络传输带来的损耗,效率就会更高。的底层代码的实现上,那其实任务之间的连接机制re和reb是完全不一样的,因为对于rebance而言,所有下游的并行子任务,它应该跟所有上游的并行子任务之间都要建立这样的一对一的关系,这其实就是一个笛卡尔基的关系啊,就是第一个冰行子任务要下面的每一个都建立连接,第二个并行子任务也要跟下面的一个都建立连接。
05:21
而现在如果是的话,我们只是分区之后在内部进行连接就可以了,所以其实整个这个过程当中,建立通信连接通道的这个过程也会少很多,可以节省很多资源。所以可以认为是在某些场景下对于reb的一个优化。那接下来我们可以测试一下啊,本身调用其实非常的简单,Stream直接调用方法,然后后面再接上下游的。算子任务就可以了啊,那这里边需要注意的是,如果我们直接这样测测的话,可能看不到什么区别,因为我们当前上游的病情度是一,那下游是四的话,看起来其实就跟rebell是一样的,呃,就是当前就只有一组直接做一个。
06:13
所有任务并行子任务的重新分发牌就可以了。所以如果说我们想要测试出的效果的话,那理论上应该。把前面的任务并行度设成二,然后后边做打印输出的时候,Print并行度是四,我们就可以看到分成两个分区的效果。但是现在我们当前是一个from elements这种方式,我们只直接传入的是测试数据,它的并行度是不能再增大。那就如果说我们把它的并行度调大去测试的话,会发现抛出异常,这就像我们所说的,它只是一个非并行的s function,那所以这里边我们其实是要单独去实现一个并行的s function,然后将它的并行度指定为二,然后才可以去调用对应的。
07:05
重分区方式啊,所以这里边的话,我们就得基于env重新去创建一个新的数据流了,我们直接调用I,那这里边就要new一个parallel。SS方式。里边的数据类型,我们可以直接把它定义成这个,可以简单一点,我们直接定义成整形就好了,做一个测试,看到效果就可以了。里边我们知道对于S方式而言,最关键的有两个方法,要实现两个抽象方法,一个是RO,一个是pencil,啊,那pencil一般我们是加一个标志位去判断要不要生成数据的,现在的话我们不用了,我们就少一点数据,只生成八个,对应我们上面这个例子啊,还是八条数据。生成八个整数就是一到八,所以很显然这里边我们不需要Y,只要一个for去生成就可以了。里边逻辑也很简单,I等于零,I小于八。
08:06
爱佳佳。循环八次。为了在里边我们看的效果看的更加的清晰,那我们可以定义一个数据分配的规则,因为在这里呢,我们本身是一个s function,我们希望在后边将它的并行度。直接设为二,诶,那如果说是这样的一个定义的话,前面我们有两个并行子任务,那这个时候一到八的数据怎么分配呢?我们现在不是有两个并行子任务吗?一个索引为零,一个索引为一,哎,那么如果是偶数的话,对应的。索引为零的并行子任务如果是奇数的话,就对应索引为一的并行子任务。那这里面就涉及到一个问题,我们需要知道当前到底是第几号定型子任务,然后接下来我们就可以直接确定到底要不要去做这个发送了,也就是说当前我们调用这个run方法的时候。
09:01
必定是某一个并行子任务的实例调用到了s function的run方法,那这里我们可以判断一下当前这个实例到底是零号还是一号呢?如果是零号的话,诶,这个时候我们就发送当前的偶数,如果是一号的话,就发送当前的奇数啊,就相当于上面只发偶数,下面只发奇数。这里面就涉及到另外一个问题,我们需要获取到当前定型子任务的索引号。那普通的s function里边就应该拿不到了,但是我们知道在运行时上下文里边能够拿到,而运行上下文的话应该是一个函数,所以这里边我们把它定义成rich parallel function。如果定义成reach的话,接下来我们就应该可以在里边get runtime context,获取到运行时上下文,然后再拿到当前的。
10:04
Index of this sub task之前我们调过这样的一个方法,所以接下来我们的判断就是我们想要做的操作是。将。基偶数。分别发送到。零号和一号。并行分区。所以接下来我们要做一个一判断,如果当前的值啊,这里边我们是从零到八,那简单起见的话,我们也可以。直接一到八,这样写的话就看着更加明显,就不用再I加一了,就是直接I从一到八。所有的基数分配到一号并行子任务啊,那这里我们的判断就应该是当前的I。如果。怎么样叫做偶数?怎么样叫做奇数?奇数呢?明显要看的就是对二取模之后得到的那个余数到底是什么啊?那这里面我们看到I对二取模,如果是奇数的话,对二取模就是一,所以它应该等于。
11:16
当前get runtime context,然后获取当前的sub的index,如果相等的话,那么我们就直接可以把数据输出了啊,这个输出的过程就用CTTX。Ctx直接调用的方法,把I做一个输出就可以了啊,所以这个逻辑的话,本质上就是要将一到八所有的数据按照奇偶数,按照它的奇偶性分别发送到两个SS任务的分区里面去。所以接下来我们把它的并行度设成二啊,那在接下来就可以做一个。然后再做print打印输出,下边把它的并行度设成四。我们可以看一下当前。
12:05
的这种调用得到的结果是什么样?运行一下。我们可以看到当前输出的结果是23141324,这个看起来好像找不到任何的规律,但是我们应该想到啊,输出本身应该要看这里的奇偶数。所以我们看一下这里的1357。很显然,他们对应的。输出的分区是三和四。哦,那前面的2468,他们对应的输出分区是一和二,也就是说我们之前SS任务。零号分区,这里边输入进来的是2468。这是零号分区,下面是一号分区。2468。
13:02
而一号分区这里是1357。所以接下来经过调整之后。得到的结果。我们会发现。2468只分配到了第一个和第二个。分区里面去,而1357呢,只分配到了第三个和第四个分区里面去。这就是我们所说的。只是针对当前分组内进行轮询分配的一个过程。那如果说要进行对比的话,我们也可以看一下,如果这里是一个reb的话,那又是什么样的效果。再来运行一下。现在看的就会非常的明显了,如果这里是reb的话,我们最后输出1357。它就是1234完全轮学,而2468呢,也是在1234之间完全轮许。
14:10
通过这样的一个对比,我们就可以看得出来和re的区别了。这就是关于re。
我来说两句