matlab 并行计算算例_解释一个简单用例的并行化

matlab 并行计算算例

前段时间,我的一个朋友问我有关加快以下过程的可能性的问题:他们正在分两个阶段生成一些数据,即从数据库中读取数据并处理结果。 读取大约需要70%的时间,而处理需要30%的时间。 不幸的是,他们不能简单地将整个数据加载到内存中,因此他们将读取分为多个小块(页面),并在检索到这些页面后对其进行处理,从而将这两个阶段交错在一起。 这是到目前为止他们拥有的伪代码:

public Data loadData(int page) {
    //70% of time...
}

public void process(Data data) {
    //30% of time...
}

for (int i = 0; i < MAX; ++i) {
    Data data = loadData(i);
    process(data);
}

他改进算法的想法是,当仍在处理当前页面时,以某种方式开始获取下一页数据,从而减少了算法的总运行时间。 他是正确的,但是不知道如何将其放入Java代码中,并且对华丽的java.util.concurrent包还没有足够的经验。 本文针对此类人群,简要介绍了Java并发编程的基本概念,例如线程池和Future<T>类型。 首先,让我们使用甘特图可视化初始和所需的实现:

第二张图表代表了我们要实现的解决方案。 您应该做的第一个观察是第二个过程较早完成,这很好。 第二个是:当我们在处理第一页(黄色1)时,第二页已经被下载(绿色2)。 当我们开始处理第2页时,第3页开始下载。 等等。 一旦有了可行的实现,我们将在稍后返回此图表。 让我们将其放入代码中。

线程是实现后台加载数据(绿色块)的方法。 但是,仅为每个绿色块启动线程既缓慢又不方便。 仅具有一个线程的线程池更加灵活,更易于使用。 首先, loadData() Callable<Data>包装到Callable<Data>

private class LoadDataTask implements Callable<Data> {

    private final int page;

    private LoadDataTask(int page) {
        this.page = page;
    }

    @Override
    public Data call() throws Exception {
        return loadData(page);
    }
}

一旦有了这样的类,就很容易提供线程池(由ExecutorService表示)并等待答复。 这是一个完整的实现:

ExecutorService executorService = Executors.newSingleThreadExecutor();
Future<Data> next = executorService.submit(new LoadDataTask(0));
for (int i = 0; i < MAX; ++i) {
    Future<Data> current = next;
    if (i + 1 < MAX) {
        next = executorService.submit(new LoadDataTask(i + 1));
    }
    Data data = current.get();  //this can block
    process(data);
}
executorService.shutdownNow();

Executors.newSingleThreadExecutor()基本上创建一个后台线程,等待任务运行。 我们不能使用更大的池(具有更多线程),因为那样的话,我们将有风险在处理数据之前将太多数据保留在内存中。

出于示例目的,假设在处理页面(黄色块)时加载页面(绿色块)需要700毫秒-300毫秒。 首先,我们提交初始任务以加载页面0(第一个蓝色箭头指向下方)。 因此,我们必须为第一个块等待700ms。 但是,一旦数据可用,在开始处理数据之前,我们会立即要求下一页。 当我们运行第二次迭代时,我们不必再等待700毫秒,因为加载数据已经进行了300毫秒,因此Future.get()仅阻塞400毫秒。 我们重复此过程,直到处理完最后一页。 当然,由于我们已经处理了所有数据,因此我们没有加载下一页数据,因此循环内会出现这种丑陋的情况。 通过在页面超出范围时从loadData()返回空对象很容易避免这种情况,但是为了示例清楚起见,我们将其保留。

这种方法在企业中非常普遍,以至于SpringEJB都添加了专门的支持。 让我们以Spring为例。 我们唯一需要更改的是将loadData()返回值从Data调整为Future<Data> 。 需要使用AsyncResult包装结果值才能进行编译:

@Async
public Future<Data> loadData(int page) {
    //...
    return new AsyncResult<Data>(new Data(...));
}

当然,此类是某些Spring bean(例如dao )的一部分。 API现在更加简洁:

Future<Data> next = dao.loadData(0);
for (int i = 0; i < MAX; ++i) {
    Future<Data> current = next;
    if (i + 1 < MAX) {
        next = dao.loadData(i + 1);
    }
    Data data = current.get();
    processor.process(data);
}

我们不再需要使用Callable并与某些线程池进行交互。 另外,引导Spring从未如此简单(所以不要告诉我Spring是重量级的!):

@Configuration
@ComponentScan("com.blogspot.nurkiewicz.async")
@EnableAsync
public class Config implements AsyncConfigurer {

    @Override
    public Executor getAsyncExecutor() {
        return Executors.newSingleThreadExecutor();
    }

}

从技术上讲, getAsyncExecutor() ,但是默认情况下,Spring将为@Async方法创建一个包含10个线程的线程池(我们只需要一个)。 现在,只需在代码中的某个地方运行它即可。

ApplicationContext context = 
  new AnnotationConfigApplicationContext(Config.class);

从本文中学到的教训:不要害怕并发,只要您使用内置抽象并理解它们,它就会比您想象的简单得多。

参考: Java和社区博客上的JCG合作伙伴 Tomasz Nurkiewicz 解释了一个简单用例的并行化

翻译自: https://www.javacodegeeks.com/2012/11/parallelization-of-a-simple-use-case-explained.html

matlab 并行计算算例

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值