当前位置: 首页 > 面试题库 >

ExecutorService令人惊讶的性能收支平衡点—经验法则?

蒙才
2023-03-14
问题内容

我试图弄清楚如何正确使用Java的执行器。我意识到向提交任务ExecutorService有其自身的开销。但是,我惊讶地发现它是如此之高。

我的程序需要以尽可能低的延迟处理大量数据(股市数据)。大多数计算是相当简单的算术运算。

我想测试的东西很简单:“ Math.random() * Math.random()

最简单的测试在一个简单的循环中运行此计算。第二个测试在匿名Runnable内部执行相同的计算(这应该用来衡量创建新对象的成本)。第三次测试将传递Runnable给。ExecutorService(这用于衡量引入执行者的成本)。

我在笔记本电脑(2 cpus,1.5 gig ram)上运行了测试:

(in milliseconds)
simpleCompuation:47
computationWithObjCreation:62
computationWithObjCreationAndExecutors:422

(大约四次运行一次,最后两个数字相等)

请注意,执行程序比在单个线程上执行所需的时间要长得多。对于1到8之间的线程池,该数字大致相同。

问题:我遗漏了一些明显的东西还是预期的结果?这些结果告诉我,我传递给执行程序的任何任务都必须进行一些非平凡的计算。如果我正在处理数百万条消息,并且需要对每条消息执行非常简单(且便宜)的转换,那么我仍然可能无法使用执行程序…试图将计算分散到多个CPU上可能最终会比仅仅花费更多在一个线程中执行它们。设计决策变得比我最初想象的要复杂得多。有什么想法吗?

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ExecServicePerformance {

 private static int count = 100000;

 public static void main(String[] args) throws InterruptedException {

  //warmup
  simpleCompuation();
  computationWithObjCreation();
  computationWithObjCreationAndExecutors();

  long start = System.currentTimeMillis();
  simpleCompuation();
  long stop = System.currentTimeMillis();
  System.out.println("simpleCompuation:"+(stop-start));

  start = System.currentTimeMillis();
  computationWithObjCreation();
  stop = System.currentTimeMillis();
  System.out.println("computationWithObjCreation:"+(stop-start));

  start = System.currentTimeMillis();
  computationWithObjCreationAndExecutors();
  stop = System.currentTimeMillis();
  System.out.println("computationWithObjCreationAndExecutors:"+(stop-start));


 }

 private static void computationWithObjCreation() {
  for(int i=0;i<count;i++){
   new Runnable(){

    @Override
    public void run() {
     double x = Math.random()*Math.random();
    }

   }.run();
  }

 }

 private static void simpleCompuation() {
  for(int i=0;i<count;i++){
   double x = Math.random()*Math.random();
  }

 }

 private static void computationWithObjCreationAndExecutors()
   throws InterruptedException {

  ExecutorService es = Executors.newFixedThreadPool(1);
  for(int i=0;i<count;i++){
   es.submit(new Runnable() {
    @Override
    public void run() {
     double x = Math.random()*Math.random();     
    }
   });
  }
  es.shutdown();
  es.awaitTermination(10, TimeUnit.SECONDS);
 }
}

问题答案:
  1. 使用执行程序是关于利用CPU和/或CPU内核的,因此,如果创建一个最多利用CPU数量的线程池,则必须拥有与CPU /内核一样多的线程。
  2. 没错,创建新对象的成本太高了。因此,减少费用的一种方法是分批使用。如果您知道要进行的计算的种类和数量,则可以创建批处理。因此,请考虑在一个执行的任务中完成了数千次计算。您为每个线程创建批处理。一旦计算完成(java.util.concurrent.Future),就可以创建下一批。甚至可以在parralel中完成新批处理的创建(4个CPU-> 3个用于计算的线程,1个用于批处理供应的线程)。最后,您可能会获得更高的吞吐量,但会带来更高的内存需求(批次,资源调配)。

编辑:我更改了您的示例,并让它在我的小双核x200笔记本电脑上运行。

provisioned 2 batches to be executed
simpleCompuation:14
computationWithObjCreation:17
computationWithObjCreationAndExecutors:9

如您在源代码中所看到的,我也从批处理和执行器生命周期中删除了度量。与其他两种方法相比,这更公平。

自己查看结果…

import java.util.List;
import java.util.Vector;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ExecServicePerformance {

    private static int count = 100000;

    public static void main( String[] args ) throws InterruptedException {

        final int cpus = Runtime.getRuntime().availableProcessors();

        final ExecutorService es = Executors.newFixedThreadPool( cpus );

        final Vector< Batch > batches = new Vector< Batch >( cpus );

        final int batchComputations = count / cpus;

        for ( int i = 0; i < cpus; i++ ) {
            batches.add( new Batch( batchComputations ) );
        }

        System.out.println( "provisioned " + cpus + " batches to be executed" );

        // warmup
        simpleCompuation();
        computationWithObjCreation();
        computationWithObjCreationAndExecutors( es, batches );

        long start = System.currentTimeMillis();
        simpleCompuation();
        long stop = System.currentTimeMillis();
        System.out.println( "simpleCompuation:" + ( stop - start ) );

        start = System.currentTimeMillis();
        computationWithObjCreation();
        stop = System.currentTimeMillis();
        System.out.println( "computationWithObjCreation:" + ( stop - start ) );

        // Executor

        start = System.currentTimeMillis();
        computationWithObjCreationAndExecutors( es, batches );    
        es.shutdown();
        es.awaitTermination( 10, TimeUnit.SECONDS );
        // Note: Executor#shutdown() and Executor#awaitTermination() requires
        // some extra time. But the result should still be clear.
        stop = System.currentTimeMillis();
        System.out.println( "computationWithObjCreationAndExecutors:"
                + ( stop - start ) );
    }

    private static void computationWithObjCreation() {

        for ( int i = 0; i < count; i++ ) {
            new Runnable() {

                @Override
                public void run() {

                    double x = Math.random() * Math.random();
                }

            }.run();
        }

    }

    private static void simpleCompuation() {

        for ( int i = 0; i < count; i++ ) {
            double x = Math.random() * Math.random();
        }

    }

    private static void computationWithObjCreationAndExecutors(
            ExecutorService es, List< Batch > batches )
            throws InterruptedException {

        for ( Batch batch : batches ) {
            es.submit( batch );
        }

    }

    private static class Batch implements Runnable {

        private final int computations;

        public Batch( final int computations ) {

            this.computations = computations;
        }

        @Override
        public void run() {

            int countdown = computations;
            while ( countdown-- > -1 ) {
                double x = Math.random() * Math.random();
            }
        }
    }
}


 类似资料:
  • 问题内容: 在我的应用程序中,我遇到了以下问题,并对结果感到惊讶: (两个整数)。 这是什么意思? 问题答案: 对于实际值,即结果为。 您使用整数除法得到的结果将向下舍入为的更负值。(也称为“地板部门”) 这就是为什么您会得到以下令人困惑的答案的原因: 注: 这是在Python 3中,其中的结果,“固定”是。因此,如果您没有理由使用Python 2,则应该升级。;) 在Python 3中,如果仍然

  • 谁能解释一下吗?

  • 问题内容: 我在timeit上获得了非常令人惊讶的结果,有人可以告诉我我做错了什么吗?我正在使用Python 2.7。 这是文件speedtest_init.py的内容: 这些是speedtest.py的内容: 控制台输出为: 我认为默认情况下timeit()运行代码的1000000倍,因此我需要将时间除以1000000,但是令人惊讶的是Counter的速度比defaultdict()慢。 那是预

  • 我有一个关于小数的整数的有趣问题。

  • 这是一个来自第三方库API的真实示例,但经过了简化。 用Oracle JDK 8U72编译 考虑以下两种方法: 两者都报告了一个“未经检查的强制转换”警告--我知道原因了。让我困惑的是为什么我可以打电话 试图传递会出现错误,正如所料: 如果报告为错误,为什么不是?