代码之家  ›  专栏  ›  技术社区  ›  brain storm

为什么使用两个线程作为计数器会降低Java的性能?

  •  0
  • brain storm  · 技术社区  · 12 年前

    在里面 python 使用两个 Threads 对于一个简单的计数器程序(如下所示),它比使用单个计数器的程序慢 thread 。原因是背后的机制 Global Interpreter lock .

    我在 java 看演出。在这里,我再次看到 Thread 表现出色 two-threaded 一个具有显著的时间尺度。为什么会这样?

    代码如下:

    public class ThreadTiming {
    
        static void threadMessage(String message) {
            String threadName =
                Thread.currentThread().getName();
            System.out.format("%s: %s%n",
                              threadName,
                              message);
        }
    
        private static class Counter implements Runnable {
    
            private int count=500000000;
            @Override
            public void run() {
    
                while(count>0) {
                    count--;
                }
                threadMessage("done processing");
            }
        }
    
        public static void main(String[] args) throws InterruptedException{
            Thread t1 = new Thread(new Counter());
            Thread t2 = new Thread(new Counter());
    
            long startTime=System.currentTimeMillis();
            t1.start();
            t2.start();
            t1.join();
            t2.join();
            long endTime=System.currentTimeMillis();
            System.out.println("Time taken by two threads "+ (endTime-startTime)/1000.0);
    
            startTime=System.currentTimeMillis();
            Calculate(2*500000000);
            endTime=System.currentTimeMillis();
            System.out.println("Time taken by single thread "+ (endTime-startTime)/1000.0);
        }
    
        public static void Calculate(int x){
            while (x>0){
                x--;
            }
            threadMessage("Done processing");
        }
    }
    

    输出:

    Thread-1: done processing
    Thread-2: done processing
    Time taken by two threads 0.052
    main: Done processing
    Time taken by single thread 0.0010
    
    5 回复  |  直到 12 年前
        1
  •  4
  •   Voo    12 年前

    非常简单。单线程版本使用一个局部变量,热点没有问题,因为它永远不会离开作用域,因此整个函数被简化为nop。

    另一方面,证明实例变量永远不会离开作用域(hello reflection!)要困难得多,而且很明显,热点不能在这里实现,因此循环不会被删除。

    总的来说,基准测试很难(我认为至少还有三个错误可能导致“错误”的结果),需要大量的知识。最好使用jmh(java测量工具)来处理大多数事情。

        2
  •  3
  •   Peter Lawrey    12 年前

    基本的答案是,您有优化器可以消除的代码,并且您正在计时检测该代码需要多长时间。您还添加了启动和停止两个线程所需的时间,这一时间可能超过一半。

    第二个测试不启动新线程,它使用当前线程,因此您只需等待它检测到循环没有执行任何操作。

    例如,您已经确定了一个线程可以在1毫秒内完成10亿次循环的时间。如果您有一个3.33 GHz的处理器,那么它必须在一个时钟周期内完成300次迭代。如果这听起来太好而不是真的,那是因为它是真的。;)

        3
  •  2
  •   John Bollinger    12 年前

    @Voo似乎大体上是对的,你可以通过移动 ThreadTiming.Counter.count 成为的局部变量 ThreadTiming.Counter.run() 这消除了任何非本地引用的可能性,并且生成的程序表现出的单线程与双线程性能差异要小得多。

    然而,这并不能消除 全部的 差异。对于我来说,双线程情况下报告的时间仍然差了大约9倍。但是如果我交换,以便首先测量单线程情况,那么双线程情况将以大约2倍的优势获胜。

    但这也是虚幻的,因为这两个测试运行的代码不同,尽管相似。单线程情况可以很容易地运行与双线程情况完全相同的代码:

    Counter c = new Counter();
    c.run();
    c.run();
    

    (使用以下版本 count 是本地的 run() .)如果使用该方法,则无论首先测试哪种情况,我观察到单线程和双线程之间的性能(在测量分辨率下)没有差异。

    正如@Voo所说,基准测试很难。

        4
  •  0
  •   djechlin    12 年前

    它看起来就像是将每个线程及其上下文加载到CPU中。它在颠簸。也许还有更详细的答案等着揭晓,但让我们从发布基本信息开始。。。

        5
  •  0
  •   Sohcahtoa82    12 年前

    当运行两个线程时,计时器包括启动两个线程所花费的时间。创建和启动线程有一些开销,在这种情况下,开销比实际执行过程的时间长。