并行化for循环

时间:2011-04-16 11:37:41

标签: java multithreading for-loop

我有一个for循环,其中迭代i的计算不依赖于前一次迭代中的计算。

我希望并行化for循环(我的代码在java中),以便多个迭代的计算可以在多个处理器上同时运行。我应该为每次迭代的计算创建一个线程,即要创建的线程数等于迭代次数(for循环中的迭代次数是多少)?怎么做?

4 个答案:

答案 0 :(得分:46)

这是一个小例子,您可能会发现有助于开始并行化。它假设:

  1. 您创建一个Input对象,其中包含计算的每次迭代的输入。
  2. 您创建一个Output对象,其中包含计算每次迭代输入的输出。
  3. 您希望传入输入列表并一次性获取所有输出列表。
  4. 您的输入是一项合理的工作,因此开销不会太高。
  5. 如果您的计算非常简单,那么您可能需要考虑批量处理它们。你可以通过在每个输入中输入100来做到这一点。它使用与系统中的处理器一样多的线程。如果您正在处理纯粹的CPU密集型任务,那么这可能就是您想要的数字。如果他们被阻止等待其他东西(磁盘,网络,数据库等),你会想要更高。

    public List<Output> processInputs(List<Input> inputs)
            throws InterruptedException, ExecutionException {
    
        int threads = Runtime.getRuntime().availableProcessors();
        ExecutorService service = Executors.newFixedThreadPool(threads);
    
        List<Future<Output>> futures = new ArrayList<Future<Output>>();
        for (final Input input : inputs) {
            Callable<Output> callable = new Callable<Output>() {
                public Output call() throws Exception {
                    Output output = new Output();
                    // process your input here and compute the output
                    return output;
                }
            };
            futures.add(service.submit(callable));
        }
    
        service.shutdown();
    
        List<Output> outputs = new ArrayList<Output>();
        for (Future<Output> future : futures) {
            outputs.add(future.get());
        }
        return outputs;
    }
    

答案 1 :(得分:10)

您不应手动执行线程处理。代替:

  • 创建一个reasonably-sized thread pool executor service(如果您的计算没有IO,请使用与核心一样多的线程)。
  • 运行一个循环,将每个单独的计算提交给执行程序服务,并保留生成的Future个对象。请注意,如果每个计算只包含少量工作,这将产生大量开销,甚至可能比单线程程序慢。在这种情况下,提交执行mdma建议的计算数据包的作业。
  • 运行第二个循环,收集所有Future的结果(它将隐式等待所有计算完成)
  • 关闭执行程序服务

答案 2 :(得分:3)

不,您不应为每次迭代创建一个线程。最佳线程数与可用处理器数量相关 - 线程太多,并且浪费了太多时间上下文切换而没有增加性能。

如果您还没有完全依赖Java,那么您可能需要尝试像OpenMPI这样的并行高性能C系统。 OpenMPI适用于此类问题。

答案 3 :(得分:0)

不要自己创建线程。我建议你使用fork / join框架(jsr166y)并创建迭代给定范围的项目的任务。它将使用与硬件支持一样多的线程来处理线程管理。

任务粒度是这里的主要问题。如果每次迭代计算相对较低(比如少于100次操作),那么将每次迭代作为单独的任务执行将引入大量的任务调度开销。最好让每个任务接受要计算的参数列表,并将结果作为列表返回。通过这种方式,您可以让每个任务计算1个,10个或数千个元素,从而将任务粒度保持在合理的水平,从而平衡保持工作可用性,并减少任务管理开销。

jsr166z中还有一个ParallelArray类,允许在数组上重复计算。如果您正在计算的值是原始类型,那么这可能对您有用。