并发程序降级的性能随着线程的增加而增加?

时间:2015-05-02 23:02:02

标签: java multithreading optimization concurrency

我一直在尝试在四核计算机上实现以下代码,并且Executor服务中100多次迭代中没有线程的平均运行时间如下

  

1个帖子= 78404.95

     

2个主题= 174995.14

     

4 thread = 144230.23

但根据我所研究的2*(no of cores)线程应该为程序提供最佳结果,这在我的程序中显然不是这样,这为单线程提供了最佳时间。

代码:

  import java.util.Collections;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class TestHashSet {

    public static void main(String argv[]){
        Set<Integer> S = Collections.newSetFromMap(new ConcurrentHashMap<Integer,Boolean>());
        S.add(1);
        S.add(2);
        S.add(3);
        S.add(4);
        S.add(5);
        long  startTime = System.nanoTime();
        ExecutorService executor = Executors.newFixedThreadPool(8);
        int Nb = 0;
        for(int i = 0;i<10;i++){
            User runnable = new User(S);
            executor.execute(runnable);

            Nb = Thread.getAllStackTraces().keySet().size();
        }
        executor.shutdown();
        try {
            executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        long endTime = System.nanoTime();
        System.out.println(0.001*(endTime-startTime)+" And "+Nb);
    }
}
class User implements Runnable{
    Set<Integer> S;
    User(Set<Integer> S){
        this.S = S;
    }
    @Override
    public void run() {
        // TODO Auto-generated method stub
        Set<Integer> t =Collections.newSetFromMap(new ConcurrentHashMap<Integer,Boolean>());;
        for(int i = 0;i<10;i++){
            t.add(i+5);
        }
        S.retainAll(t);
        Set<Integer> t2 =Collections.newSetFromMap(new ConcurrentHashMap<Integer,Boolean>());;
        for(int i = 0;i<10;i++){
            t2.add(i);
        }
        S.addAll(t);
        /*
        ConcurrentHashSet<Integer> D = new ConcurrentHashSet<Integer>();
        for(int i=0;i<10;i++){
            D.add(i+3);
        }
        S.difference(D);
        */
    }
}

更新:如果我将每个线程的查询数量增加到1000,则4线程的性能优于单线程。我认为当我每个线程只使用大约4个查询而没有查询时,开销高于运行时间增加的运行时间现在大于Overhead.Thanks

1 个答案:

答案 0 :(得分:3)

  

但5线程应该提高性能..?

那&gt;&gt;你&lt;&lt;&lt;&lt;&lt;假设。但实际上,无法保证添加线程会提高性能。

  

但根据我所研究的2 *(没有核心)线程应该给出最佳结果......

如果你在某处读到,那么你要么误读它,要么就是明白错误。

实际情况是,用于获得最佳性能的线程数量高度依赖应用程序的性质,以及运行的硬件。

基于粗略阅读您的代码,似乎这是测试Java处理多线程访问和更新共享集(S)的程度的基准。每个线程在线程限制集上执行一些操作,然后将线程限制集中的所有条目添加或删除到共享集。

问题是addAllretainAll调用可能是并发瓶颈。基于ConcurrentHashMap的集合将为集合的点访问/更新提供比基于HashMap的更好的并发性能。但是,addAll和retainAll在其他线程正在操作的相同条目上执行N个此类操作。鉴于此操作模式的性质,您可能会在ConcurrentHashMap的不同区域内获得重大争用。这可能会导致一个线程阻塞另一个线程......并且减速。

  

更新:如果我增加每个线程没有查询4线程的性能优于单线程。我认为开销高于运行时我每个线程只使用大约4个查询而且查询没有增加运行时间是现在大于开销。

我认为您的意思是增加哈希映射条目的数量。考虑到ConcurrentHashMap的工作方式,这可能会减少平均争用。 (该类将映射划分为区域,并安排涉及不同区域中的条目的操作产生最小可能的争用开销。通过增加不同条目的数量,可以降低两个同时操作将导致争用的可能性。)

所以返回&#34; 2 x no of threads&#34;仿真陈述。

我怀疑你一直在阅读的消息来源实际上并不是说这会给你带来最佳性能。我怀疑他们真的这么说:

  • &#34; 2 x没有线程&#34;是一个很好的起点... 你需要为你的应用程序/问题/硬件调整它和/或

  • 不要超过&#34; 2 x没有线程&#34;对于计算密集型任务......因为它不太可能有所帮助。

在您的示例中,争用的主要来源很可能是对共享集/映射的更新......以及确保它们以原子方式发生的开销。

您也可以在较低级别获得争用;即争用内存带宽(RAM读/写)和内存高速缓存争用。是否发生这种情况取决于您运行的硬件的规格......

最后需要注意的是,您的基准测试存在缺陷,因为它不允许各种VM预热效果......例如JIT编译。您的2个线程时间超过双倍 1个线程时间的事实指向该问题。

您的基准测试还有其他可疑方面:

  • run()方法完成的工作量太小。

  • 此基准似乎不代表现实世界的用例。在完全虚拟(无意义)算法中测量加速并不会给你任何关于缩放线程数时真实算法可能执行的线索。

  • 在4核计算机上运行测试意味着您可能没有足够的数据点来得出具有科学意义的结论......假设基准测试结果合理。

话虽如此,你似乎看到的2到4线程减速对我来说并不意外......