• Feeds

  • Lua coroutine vs Java wait/notify

    在上文Lua coroutine 不一样的多线程编程思路 中想到coroutine的运行机制跟Java中的wait/notify很相似,所以写了一个简单程序比较一下。

    源代码

    Lua code

    co = coroutine.create(function(loops)
        for i = 1, loops do
            coroutine.yield()
        end
    end)
    
    local x = os.clock()
    local loops = 100 * 1000 * 1000
    coroutine.resume(co, loops)
    for i = 1, loops do
        coroutine.resume(co)
    end
    print(string.format("elapsed time: %.2f\n", os.clock() - x))

    Java code

    public class TestWait {
        public static void main(String[] args) {
            WaitClass wc = new WaitClass();
            wc.start();
            int loops = 100 * 1000 * 1000;
            long t1 = System.currentTimeMillis();
            for (int i = 0; i < loops; i++) {
                synchronized (wc) {
                    wc.notify();
                }
            }
            long t2 = System.currentTimeMillis();
            System.out.println("elapsed time: " + (t2 - t1) / 1000l);
        }
    }
    
    class WaitClass extends Thread {
        public void run() {
            while (true) {
                synchronized (this) {
                    try {
                        this.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    运行结果

    Lua elapsed time: 53.36
    Java elapsed time: 51

    CPU占用

    运行环境:4core XEON

    Lua 1CPU 100%, 其他CPU0%, total 25% (其中CPU sys 0%)

    Java 2个CPU 40%-50%, 其他CPU 0%, total 25% (其中CPU sys 5%-10%)

    从结果看,coroutine只利用了一个CPU, 和原理所说完全一致。

    Java利用了2个CPU, 各占用了50%的CPU时间运行和50%的时间等待,和设计也一致。另外Java用了5-10%的sys CPU时间用于线程context switch

    结论

    虽然这两种程序没有直接可比性,但仍然可以看出一些有趣的地方:

    • Lua虽然在各种性能评比中performance比Java低一个数量级,但在这个场景中也跑平了Java
    • Java为了调用notify/wait, 用了同步锁,因此测试场景对Java不利。

    再谈coroutine应用场景

    今天又看到qiezi的文章并发编程模型:Thread, Coroutine, Callback … 分析得很深入,对这方面感兴趣的可以进一步去深入了解。

    另外qiezi在Coroutine在并发程序中的应用中提到四种场景,可以理解是对我上篇文章对coroutine应用场景的一种答案。

    1. 状态机。
    2. 异步IO操作:异步IO操作通常是发起一个IO请求,由操作系统完成以后回调指定的方法或者使用其它方式通知。
    3. 高并发网络服务器,高并发服务器除了要处理场景一的情况外,可能还要结合场景二,多线程方案有时候完全不能接受,更多的是基于事件、异步IO或者是混合事件和多线程的模型。
    4. 客户端并发应用

    但是我还是觉得存在疑虑,后面几种我觉得用多线程/线程池模式也可以很好解决。其实select/epoll异步IO方式跟多线程并不矛盾。多线程并不代表每个线程需要recv阻塞在那里。目前网络服务器的多线程通常是指业务逻辑处理部分使用多线程。比如Java中用mina来处理连接(相当于epoll),mina在收到数据包之后再分发给负责业务逻辑的thread pool处理。如果是CPU密集型任务,简单把线程池的线程数设成CPU数即可达到性能最佳。这时如果把线程数设成1,就很类似coroutine模式了。而Java模式所增加的消耗,主要是new runnable class以及线程池调度的开销。

    如想及时阅读Tim Yang的文章,可通过页面右上方扫码订阅最新更新。

    « | »

    1 Comment  »

    1. Dimiter Stanev

      Hi Tim,

      With 32-bit lua-5.1.4 cygwin compiled I’m getting on my machine:
      elapsed time: 29.72

      but with 32-bit luajit (www.luajit.org) I’m getting:
      elapsed time: 7.06

      Cheers!

    Leave a Comment