Goroutine与Libtask协程切换性能比较

gerry · 2016-06-08 13:53:38 · 5551 次点击 · 大约8小时之前 开始浏览    置顶
这是一个创建于 2016-06-08 13:53:38 的主题,其中的信息可能已经有所发展或是发生改变。

##Goroutine与Libtask协程切换性能比较

##Libtask Libtask是一个非常轻量级的C语言协程库,作者是大名鼎鼎的拉斯.考克斯,也是Go语言的作者之一。Libtask的实现原理也很简单,主要是利用系统调用如:getcontext、setcontext(注:不同的操作系统有不同的系统调用)封装成makecontext、swapcontext两个函数,利用这两个函数实现在单个线程中执行多个task,协程上下文切换非常轻量,因为是在同一个线程中,所以只需要保存和设置SP和PC就可实现上下文切换。

###Goroutine Goroutine的实现应该有参考Libtask,主体思想都差不多,只不过Goroutine扩展到可以支持多核处理器,系统调用、IO等阻塞都会移到一个独立的线程中,释放阻塞线程,以便其他协程能够继续处理;Libtask也支持异步IO和同步IO,同步IO会阻塞线程,异步IO不会阻塞线程,并且多个异步非阻塞IO会放到同一个异步协程处理,很遗憾Libtask目前仅支持poll,暂不支持epoll,不过可以自己实现。

###对比测试 为了对比协程切换性能,设计一个简单的测试用例,分别切换100000000次,比较运行时间。

###测试环境 硬件:MacBook Pro, 2.5 GHz Intel Core i7,16 GB 1600 MHz DDR3

OS: OS X EI Captain

Go编译器版本:1.6.2

C编译器版本:clang-703.0.29 + LLVM

####GO测试代码:


package main

import(
  "fmt"
  "time"
  "runtime"
)

const TOTAL_COUNTS = 100000000 

func test(c chan int) {        
    count := 0                 

    for {
        count++                
        if count > TOTAL_COUNTS {       
          c <- 1               
          break                
        }

        runtime.Gosched()      
    }
}

func main() {                  
    runtime.GOMAXPROCS(1)      
    c := make(chan int, 0)

    startTime := time.Now()
    go test(c)
    <-c

    fmt.Println("USEAGE TIME:", time.Since(startTime))
}

####Libtask测试代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <task.h>

const int STACK_32K = 32678;
const int TOTAL_COUNTS = 100000000;                                                                                

struct timeval tv_begin, tv_end;                                                                                   

void test()                    
{
    int count = 0;
    while(1) {                 
        count++;
        if (count > TOTAL_COUNTS) {
            gettimeofday(&tv_end, NULL);    
            int time_usage = (tv_end.tv_sec - tv_begin.tv_sec) * 1000 + (tv_end.tv_usec- tv_begin.tv_usec)/1000;   
            printf("USAGE TIME: %d\n", time_usage);
            break;
        }

        taskyield();
    }
}void taskmain(int argc, char** args)
{
    gettimeofday(&tv_begin, NULL);
    taskcreate(test, NULL, STACK_32K);
}

###测试结果 libtask: 10412ms

Goroutine: 10965ms

###结果分析 从结果来看基本相当,Goroutine只是略微慢些,其原因是Go语言有Runtime和GC,执行效率本身就略慢于C语言;另外Goroutine支持多核,有多个处理器,有线程池,那么诸如任务队列、Channel必然会存在竞争,有竞争必然用到锁,这会进一步影响其性能。 综上,Goroutine的协程切换效率非常棒,与单线程的Libtask相差无几,但是Goroutine可以利用多核处理器来提高并发,微量的性能影响也就可以忽略不计了。


有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

5551 次点击  
加入收藏 微博
1 回复  |  直到 2016-06-09 13:37:01
stevewang
stevewang · #1 · 9年之前

这个测试证明不了什么吧,都没有实际的goroutine运行。

添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传