【问题标题】:Why does this parallel algorithm run more slowly than its sequential counterpart?为什么这种并行算法的运行速度比它的顺序算法慢?
【发布时间】:2013-10-14 22:54:30
【问题描述】:

顺序:

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 都是包含数千到数十万项的巨大列表。

【问题讨论】:

  • 你怎么知道它跑得慢?
  • 在第一个代码 sn-p 中,您将列表 d 传递给 tally(),而在第二个代码中,您将列表项 datum 传递。这是一个版本中的错字吗?
  • 无法说明原因,因为您没有提供足够的信息。
  • List&lt;D&gt; d 包含多少项?顺序和并行版本执行多长时间?

标签: java concurrency parallel-processing


【解决方案1】:

并行版本应该更快,

这是一个常见的误解。

但速度要慢一个数量级。

一个共同的结果

有什么想法吗?

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

例如假设将任务传递给另一个线程大约需要 10 微秒。如果您的任务需要 1 微秒,那么开销可能会影响您的性能。但是,如果任务需要 100 微秒,您可以看到显着的性能提升,并且值得为此付出代价。

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

【讨论】:

  • 是的,我明白这一点。但是在这种情况下,它确实应该更快。当我在 python 中执行此操作时,它会加快很多速度。
  • python 比 Java 快多少?如果您的任务很慢,使用更多线程会有所帮助。您可以做的是为每个处理器创建一个任务,该任务执行部分元素。
【解决方案2】:

A) 您是否排除了任何同步?如果getChampoid 命中同步数据结构(例如哈希表),我不会对性能急剧下降感到惊讶。

B) 对于 Java,对象开销 通常会影响性能。确保使用诸如 VisualVM 之类的分析器。在您的情况下,如果您看到大量时间进入垃圾收集,我不会感到惊讶。

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

【讨论】:

  • A) getChampoid() 读取(但不写入)列表 c。但是,tally() 方法是同步的,需要写入共享数组列表。
  • @EarlBellinger 从多个线程写入共享资源将比仅一个线程慢。您能否返回需要合并的结果,即使用 Callable。
  • 好了,您已经完成了同步。一次只有一个人可以写,其他人会等着什么都不做。有时你可以解决这个问题,例如通过使用AtomicIntegerArray。但是你能 A)按照这篇文章的建议对数据进行分块,然后 B)给每个线程一个单独的存储,然后有效地合并存储吗?
猜你喜欢
  • 1970-01-01
  • 2020-08-16
  • 2019-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-06
  • 1970-01-01
相关资源
最近更新 更多