为什么这个并行算法比顺序算法运行得慢?

时间:2013-10-14 22:54:30

标签: java concurrency parallel-processing

依序:

void do(List<D> d, final List<C> c) {
for (D datum : d)
    getChampoid(datum, c).tally(datum);

并行:

static final int procs = Runtime.getRuntime().availableProcessors();
static final ExecutorService pool = Executors.newFixedThreadPool(procs);
void do(List<D> d, final List<C> c) {
    List<Future> futures = new ArrayList<>();
    for (final D datum : d)
        futures.add(pool.submit(new Runnable() {

            @Override
            public void run() {
                getChampoid(datum, c).tally(datum);
            }

        }));
    for (Future f : futures)
        try {
            f.get();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

我很难过,因为对他来说他们看起来像是做同样的事情,并行版本应该更快,但它要慢一个数量级。有什么想法吗?

仅供参考,d和c都是巨大的列表,其中包含数千到数十万个项目。

2 个答案:

答案 0 :(得分:4)

  

并行版本应该更快,

这是一种常见的误解。

  

但它的速度要慢一个数量级。

常见结果

  

有什么想法吗?

使用多个线程会产生一个线程没有的开销。开销可能比实际完成的工作高几个数量级,使得速度慢得多。如果完成的工作量远大于开销,则可以获得非常好的可伸缩性。

e.g。说将任务传递给另一个线程需要大约10微秒。如果您的任务需要1微秒,那么开销可能会影响您的性能。但是,如果任务需要100微秒,您可以看到显着的性能提升,并且值得花费开销的代价。

简而言之,没有什么是免费的,尤其是多线程。

答案 1 :(得分:0)

A)你排除了任何同步吗?如果getChampoid命中同步数据结构(例如Hashtable),那么性能下降并不令人惊讶。

B)使用Java,对象开销通常会导致性能下降。确保使用VisualVM等分析器。在你的情况下,如果你看到很多时间进入垃圾收集,我不会感到惊讶。

考虑将列表d分成少量部分,然后处理线程中的每个部分。现在,对于每个datum,您将创建一个Runnable 一个Future对象,可能还有一些。当d很大时,对于垃圾收集器来说意味着很多工作(在这里,Hotspot也无法对其进行优化)。