Pipeline并行处理模型

前言


在我们平时的程序处理过程中,在效率上而言,串行处理的效率不如并行处理的效率,从线程层面而言,即多线程效率不如单线程。但是尽管说并行处理效率确实会比较高,但是它在处理拥有数据结果依赖关系的逻辑时,需要额外的同步管控。例如我的输出怎么临时被存放,然后被下游程序收到处理等等。倘若我们设计的并行处理程序能很好地解决,逻辑依赖关系,那么无疑并行处理的方式将会大大提速我们实际系统中的执行效率。本文笔者来聊聊其中一种被称为Pipeline(流水线)模式的并行处理作业模式,相信此并行处理模式在实际工作中还是有所应用场景的。

Pipeline并行处理模式概要


首先一个问题,什么叫做Pipeline并行处理模式呢?Pipeline并行处理模式,首先它具有Pipeline属性,它有一条完整的依赖关系处理流程。比如一道工序总共分3个流程,A,B和C, 并且有严格的先后顺序执行要求。B流程的执行必须依赖A流程的执行结果,同样C流程需要依赖B的。其实这么来看,串行处理模式是天然适用于Pipeline处理模式的。

不过本文我们要讨论的是并行处理Pipeline模式作业。如果按照惯常使用的多线程依赖同步的处理方法,主要有以下两种:

  • 将程序中间结果写出到第三方存储介质内,然后并行处理线程与此第三方存储进行数据交互,同步。
  • 程序中间结果保存到本地变量内,通过线程安全的做法将中间结果进行赋值输出,灵活一点的,还可以进行同步的控制。

接下来笔者将要讨论的方法是第二种方法。

车厢模拟式的Pipeline并行处理模式


这里我们将用模拟车厢的方式来进行Pipeline work的执行,每个车厢代表一个执行单元,车厢具有Pipeline work的独有特征:

  • 车厢具有连接关系,每个车厢有它的前车厢和后车厢。
  • 头车厢无前车厢,它的处理无须依赖前车厢执行结果。
  • 尾车厢无后车厢,它的处理完毕即意味着总执行过程的结束。

另外在每个车厢内,它还具有以下变量:

  • 多执行线程
  • 中间结果置换列表

因此,基于车厢模型的Pipeline处理模式如下图所示:
在这里插入图片描述
上图中Middle Carriage(中部车厢)可能会有很多节,每个线程都有对应的中间结果输出列表。

Pipeline并行处理模式代码实现


此部分笔者来分享一段Pipeline并行处理模式的多线实现代码,引用自Hadoop社区JIRA的一个patch。

在这个patch代码中,还是沿用了上节车厢的概念,另外它的整个过程可拆分为以下几个步骤:

  1. Pipeline work的定义以及Pipeline task的构建
  2. 根据Pipeline task,来构建车厢
  3. 车厢内部逻辑处理
    3.1) 头车厢(起始线程)处理过程
    3.2) 中部车(中部线程)厢处理过程
    3.3) 尾部车厢(末尾线程)处理过程

针对上述子步骤,我们逐一来阐述。首先是Pipeline work的定义,此work的是每个车厢的线程的具体执行逻辑。

    PipelineWork header = new PipelineWork<Object, TestItem>() {
   
      int idx = 0;

      @Override
      public TestItem doWork(Object obj) throws IOException {
   
        if (idx < items.size()) {
   
          TestItem item = items.get(idx);
          item.setVal(item.getVal() * 2);
          if (exp && (idx == items.size() - 1)) {
   
            throw ioe;
          }
          idx++;
          
          LOG.info(Thread.currentThread().getName() +
              ": Head worker produce item: " + item.getVal());
          return item;
        } else {
   
          LOG.info("Head worker finsihed produce item.");
          return null;
        }
      }
    };
    PipelineWork middle = new PipelineWork<TestItem, TestItem>() {
   
      @Override
      public TestItem doWork(TestItem item) {
   
        item.setVal(item.getVal() * 2);
        LOG.info(Thread.currentThread().getName() +
            ": Middle worker set value: " + item.getVal());

        return item;
      }
    };
    PipelineWork trailer = new PipelineWork<TestItem, Object>() {
   
      @Override
      public Object doWork(TestItem item) {
   
        item.setVal(item.getVal() * 2);
        LOG.info(Thread.currentThread().getName() +
            ": Trailer woker set value: " + item.getVal());

        return EMPTY;
      }
    };

然后根据上面的work,构建Pipeline task,

    PipelineTask task = new PipelineTask(conf, "pipeline.testcarriage");
    task.appendWork(header, "header");
    task.appendWork(middle, "middle" + i);
    task.appendWork(trailer, "trailer");
    task.kickOff();
    task.join();

在Pipeline的task的构建过程中,会进行车厢的组织,


                
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值