【问题标题】:Performance Issues with newFixedThreadPool vs newSingleThreadExecutornewFixedThreadPool 与 newSingleThreadExecutor 的性能问题
【发布时间】:2013-04-14 02:05:00
【问题描述】:

我正在尝试对我们的客户端代码进行基准测试。所以我决定编写一个多线程程序来对我的客户端代码进行基准测试。我正在尝试测量以下方法将花费多少time (95 Percentile)-

attributes = deClient.getDEAttributes(columnsList);

下面是我编写的多线程代码,用于对上述方法进行基准测试。我在两种情况下看到了很多变化-

1) 首先,使用20 threadsrunning for 15 minutes 的多线程代码。我得到 95 个百分位数为37ms。我正在使用-

ExecutorService service = Executors.newFixedThreadPool(20);

2) 但是如果我正在为15 minutes 运行我的相同程序,使用-

ExecutorService service = Executors.newSingleThreadExecutor();

而不是

ExecutorService service = Executors.newFixedThreadPool(20);

当我使用 newFixedThreadPool(20) 运行我的代码时,我得到 95 个百分位数为 7ms,这比上述数字要少。

谁能告诉我这些高性能问题的原因是什么-

newSingleThreadExecutor vs newFixedThreadPool(20)

通过这两种方式,我正在为15 minutes 运行我的程序。

下面是我的代码-

public static void main(String[] args) {

    try {

        // create thread pool with given size
        //ExecutorService service = Executors.newFixedThreadPool(20);
        ExecutorService service = Executors.newSingleThreadExecutor();

        long startTime = System.currentTimeMillis();
        long endTime = startTime + (15 * 60 * 1000);//Running for 15 minutes

        for (int i = 0; i < threads; i++) {
            service.submit(new ServiceTask(endTime, serviceList));
        }

        // wait for termination        
        service.shutdown();
        service.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
    } catch (InterruptedException e) {

    } catch (Exception e) {

    }
}

下面是实现Runnable接口的类-

class ServiceTask implements Runnable {

    private static final Logger LOG = Logger.getLogger(ServiceTask.class.getName());
    private static Random random = new SecureRandom();

    public static volatile AtomicInteger countSize = new AtomicInteger();

    private final long endTime;
    private final LinkedHashMap<String, ServiceInfo> tableLists;

    public static ConcurrentHashMap<Long, Long> selectHistogram = new ConcurrentHashMap<Long, Long>();


    public ServiceTask(long endTime, LinkedHashMap<String, ServiceInfo> tableList) {
        this.endTime = endTime;
        this.tableLists = tableList;
    }

    @Override
    public void run() {

        try {

            while (System.currentTimeMillis() <= endTime) {

                double randomNumber = random.nextDouble() * 100.0;

                ServiceInfo service = selectRandomService(randomNumber);

                final String id = generateRandomId(random);
                final List<String> columnsList = getColumns(service.getColumns());

                List<DEAttribute<?>> attributes = null;

                DEKey bk = new DEKey(service.getKeys(), id);
                List<DEKey> list = new ArrayList<DEKey>();
                list.add(bk);

                Client deClient = new Client(list);

                final long start = System.nanoTime();

                attributes = deClient.getDEAttributes(columnsList);

                final long end = System.nanoTime() - start;
                final long key = end / 1000000L;
                boolean done = false;
                while(!done) {
                    Long oldValue = selectHistogram.putIfAbsent(key, 1L);
                    if(oldValue != null) {
                        done = selectHistogram.replace(key, oldValue, oldValue + 1);
                    } else {
                        done = true;
                    }
                }
                countSize.getAndAdd(attributes.size());

                handleDEAttribute(attributes);

                if (BEServiceLnP.sleepTime > 0L) {
                    Thread.sleep(BEServiceLnP.sleepTime);
                }
            }
        } catch (Exception e) {

        }
    }
}

更新:-

这是我的处理器规格 - 我正在使用定义为 2 个处理器的 Linux 机器运行我的程序:

vendor_id       : GenuineIntel
cpu family      : 6
model           : 45
model name      : Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz
stepping        : 7
cpu MHz         : 2599.999
cache size      : 20480 KB
fpu             : yes
fpu_exception   : yes
cpuid level     : 13
wp              : yes
flags           : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss syscall nx rdtscp lm constant_tsc arch_perfmon pebs bts rep_good xtopology tsc_reliable nonstop_tsc aperfmperf pni pclmulqdq ssse3 cx16 sse4_1 sse4_2 popcnt aes hypervisor lahf_lm arat pln pts
bogomips        : 5199.99
clflush size    : 64
cache_alignment : 64
address sizes   : 40 bits physical, 48 bits virtual
power management:

【问题讨论】:

    标签: java multithreading performance concurrency threadpool


    【解决方案1】:

    谁能告诉我newSingleThreadExecutor vs newFixedThreadPool(20) 出现如此高性能问题的原因是什么...

    如果您并行运行的任务(在这种情况下为 20 个)比您拥有的处理器多得多(我怀疑您有 20 多个处理器盒),那么是的,每个单独的任务都需要更长的时间才能完成。计算机一次执行一项任务比在同时运行的多个线程之间切换更容易。即使您将池中的线程数限制为您拥有的 CPU 数,每个任务也可能会运行得更慢,尽管会稍微慢一些。

    但是,如果您比较不同大小的线程池的吞吐量(完成多个任务所需的时间),您应该会发现 20 个线程的吞吐量应该更高。如果您使用 20 个线程执行 1000 个任务,它们总体上将比仅使用 1 个线程完成得快得多。每个任务可能需要更长的时间,但它们将并行执行。考虑到线程开销等,它可能不会快 20 倍,但可能会快 15 倍。

    您不应该担心单个任务的速度,而应该尝试通过调整池中的线程数来最大化任务吞吐量。使用多少线程很大程度上取决于 IO 量、每个任务使用的 CPU 周期、锁、同步块、操作系统上运行的其他应用程序以及其他因素。

    人们经常使用 1-2 倍的 CPU 数量作为开始池中线程数量以最大化吞吐量的好地方。更多 IO 请求或线程阻塞操作然后添加更多线程。更多的 CPU 绑定然后减少线程数以更接近可用的 CPU 数。如果您的应用程序正在与服务器上其他更重要的应用程序竞争操作系统周期,那么可能需要更少的线程。

    【讨论】:

    • 非常感谢格雷的建议。更多信息——我正在对 lnp 盒子进行负载和性能测试(类似于我们这里的生产盒子)。是的,20 个线程的吞吐量比一个线程大得多。所以你的建议是,我应该将线程数从 20 降低到更低的数量,然后尝试一下?对吗?
    • 对。玩弄这个数字,直到你最大化你的吞吐量。了解如果您切换架构,您将不得不再次加速 @TechGeeky。
    • 当然。我感到困惑的另一件事是-您说最大化吞吐量?但我想知道如果我降低线程数,那么吞吐量会继续下降吗?
    • 可能不会。例如,如果您使用 10 个线程并且每个线程需要(假设)15 毫秒,那么您总体上会更快,因为您使用了 1/2 线程但时间少于 1/2。
    • 我明白了。嗯,是的,有点道理。我还使用我的机器配置规范以及运行程序的位置更新了我的问题。您认为这是否意味着它可以承受负载?
    猜你喜欢
    • 2016-07-23
    • 2015-06-19
    • 2020-06-03
    • 2013-02-27
    • 2017-12-23
    • 1970-01-01
    • 2012-07-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多