依序:
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都是巨大的列表,其中包含数千到数十万个项目。
答案 0 :(得分:4)
并行版本应该更快,
这是一种常见的误解。
但它的速度要慢一个数量级。
常见结果
有什么想法吗?
使用多个线程会产生一个线程没有的开销。开销可能比实际完成的工作高几个数量级,使得速度慢得多。如果完成的工作量远大于开销,则可以获得非常好的可伸缩性。
e.g。说将任务传递给另一个线程需要大约10微秒。如果您的任务需要1微秒,那么开销可能会影响您的性能。但是,如果任务需要100微秒,您可以看到显着的性能提升,并且值得花费开销的代价。
简而言之,没有什么是免费的,尤其是多线程。
答案 1 :(得分:0)
A)你排除了任何同步吗?如果getChampoid
命中同步数据结构(例如Hashtable),那么性能下降并不令人惊讶。
B)使用Java,对象开销通常会导致性能下降。确保使用VisualVM等分析器。在你的情况下,如果你看到很多时间进入垃圾收集,我不会感到惊讶。
考虑将列表d
分成少量部分,然后处理线程中的每个部分。现在,对于每个datum
,您将创建一个Runnable
和一个Future
对象,可能还有一些。当d
很大时,对于垃圾收集器来说意味着很多工作(在这里,Hotspot也无法对其进行优化)。