0

顺序:

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

4

2 回答 2

4

并行版本应该更快,

这是一个普遍的误解。

但它慢了一个数量级。

一个共同的结果

有什么想法吗?

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

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

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

于 2013-10-14T22:58:22.870 回答
0

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

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

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

于 2013-10-14T23:18:47.357 回答