前段时间实现的C协程依赖栈传递参数,在开启优化时会导致错误,于是实现了一个ucontext的版本,但ucontext的切换效率太差了,

在我的机器上执行4000W次切换需要11秒左右,这达不到我的要求,所以重新设计了实现,使得在开启优化时也能得到正确的结果.

并且效率也令人满意,4000W次切换仅需要730ms左右,足足比ucontext的实现快乐近15倍。

下面贴出实现:

 #include "uthread.h"
#include <stdlib.h>
#include <ucontext.h>
#include <pthread.h>
#include "link_list.h" struct uthread
{
int32_t reg[];//0:esp,1:ebp,2:eax,3:ebx,4:ecx,5:edx,6:edi,7:esi
void *para;
uthread_t parent;
void*(*main_fun)(void*);
void *stack;
int32_t ssize;
int8_t first_run;
}; #ifdef _DEBUG
//for debug version
void uthread_main_function()
{
int32_t arg;
__asm__ volatile(
"movl %%eax,%0\t\n"
:
:"m"(arg)
); uthread_t u = (uthread_t)arg;
void *ret = u->main_fun(u->para);
if(u->parent)
uthread_switch(u,u->parent,ret);
else
exit();
}
#else
//for release version
void __attribute__((regparm())) uthread_main_function(void *arg)
{
uthread_t u = (uthread_t)arg;
void *ret = u->main_fun(u->para);
if(u->parent)
uthread_switch(u,u->parent,ret);
else
exit();
}
#endif
uthread_t uthread_create(uthread_t parent,void*stack,uint32_t stack_size,void*(*fun)(void*))
{
uthread_t u = (uthread_t)calloc(,sizeof(*u));
u->parent = parent;
u->main_fun = fun;
u->stack = stack;
u->ssize = stack_size;
if(stack)
{
u->reg[] = (int32_t)stack+stack_size-;
u->reg[] = (int32_t)stack+stack_size-;
}
if(u->main_fun)
u->first_run = ;
return u;
} void uthread_destroy(uthread_t *u)
{
free(*u);
*u = NULL;
} #ifdef _DEBUG
void* __attribute__((regparm())) uthread_switch(uthread_t from,uthread_t to,void *para)
{
if(!from)
return NULL;
to->para = para;
int32_t esp,ebp,eax,ebx,ecx,edx,edi,esi;
//save current registers
//the order is important
__asm__ volatile(
"movl %%eax,%2\t\n"
"movl %%ebx,%3\t\n"
"movl %%ecx,%4\t\n"
"movl %%edx,%5\t\n"
"movl %%edi,%6\t\n"
"movl %%esi,%7\t\n"
"movl %%ebp,%1\t\n"
"movl %%esp,%0\t\n"
:
:"m"(esp),"m"(ebp),"m"(eax),"m"(ebx),"m"(ecx),"m"(edx),"m"(edi),"m"(esi)
);
from->reg[] = esp;
from->reg[] = ebp;
from->reg[] = eax;
from->reg[] = ebx;
from->reg[] = ecx;
from->reg[] = edx;
from->reg[] = edi;
from->reg[] = esi;
if(to->first_run)
{
to->first_run = ;
esp = to->reg[];
//use eax to pass arg
eax = (int32_t)to;
__asm__ volatile (
"movl %1,%%eax\t\n"
"movl %0,%%ebp\t\n"
"movl %%ebp,%%esp\t\n"
:
:"m"(esp),"m"(eax)
);
uthread_main_function();
}
else
{
esp = to->reg[];
ebp = to->reg[];
eax = to->reg[];
ebx = to->reg[];
ecx = to->reg[];
edx = to->reg[];
edi = to->reg[];
esi = to->reg[];
//the order is important
__asm__ volatile (
"movl %2,%%eax\t\n"
"movl %3,%%ebx\t\n"
"movl %4,%%ecx\t\n"
"movl %5,%%edx\t\n"
"movl %6,%%edi\t\n"
"movl %7,%%esi\t\n"
"movl %1,%%ebp\t\n"
"movl %0,%%esp\t\n"
:
:"m"(esp),"m"(ebp),"m"(eax),"m"(ebx),"m"(ecx),"m"(edx),"m"(edi),"m"(esi)
);
}
return from->para;
}
#else
void* __attribute__((regparm())) uthread_switch(uthread_t from,uthread_t to,void *para)
{
if(!from)
return NULL;
to->para = para;
int32_t esp,ebp,edi,esi;
//save current registers
//the order is important
__asm__ volatile(
"movl %%eax,%2\t\n"
"movl %%ebx,%3\t\n"
"movl %%ecx,%4\t\n"
"movl %%edx,%5\t\n"
"movl %%edi,%6\t\n"
"movl %%esi,%7\t\n"
"movl %%ebp,%1\t\n"
"movl %%esp,%0\t\n"
:
:"m"(from->reg[]),"m"(from->reg[]),"m"(from->reg[]),"m"(from->reg[])
,"m"(from->reg[]),"m"(from->reg[]),"m"(from->reg[]),"m"(from->reg[])
);
if(to->first_run)
{
to->first_run = ;
//change stack
//the order is important
__asm__ volatile (
"movl %0,%%ebp\t\n"
"movl %%ebp,%%esp\t\n"
:
:"m"(to->reg[])
);
uthread_main_function((void*)to);
}
else
{
esp = to->reg[];
ebp = to->reg[];
edi = to->reg[];
esi = to->reg[];
//the order is important
__asm__ volatile (
"movl %2,%%eax\t\n"
"movl %3,%%ebx\t\n"
"movl %4,%%ecx\t\n"
"movl %5,%%edx\t\n"
"movl %6,%%edi\t\n"
"movl %7,%%esi\t\n"
"movl %1,%%ebp\t\n"
"movl %0,%%esp\t\n"
:
:"m"(esp),"m"(ebp),"m"(to->reg[]),"m"(to->reg[])
,"m"(to->reg[]),"m"(to->reg[]),"m"(edi),"m"(esi)
);
}
return from->para;
}
#endif

test.c

 #include <stdio.h>
#include "uthread.h"
#include "SysTime.h"
#include <stdlib.h>
void* ufun2(void *arg)
{
printf("ufun2\n");
char **tmp = (char**)arg;
uthread_t self = (uthread_t)tmp[];
uthread_t parent = (uthread_t)tmp[];
volatile void *ptr = self;
while(ptr)
{
ptr = uthread_switch(self,parent,NULL);
}
return NULL;
} char *stack1;
char *stack2; void* ufun1(void *arg)
{
uthread_t self = (uthread_t)arg;
uthread_t u = uthread_create(self,stack2,,ufun2);
char* _arg[];
_arg[] = (char*)u;
_arg[] = (char*)self;
int i = ;
uint32_t tick = GetSystemMs();
for( ; i < ; ++i)
{
uthread_switch(self,u,&_arg[]);
}
printf("%d\n",GetSystemMs()-tick);
uthread_switch(self,u,NULL);
return arg;
} int main()
{
stack1 = (char*)malloc();
stack2 = (char*)malloc();
/*
* if use ucontext version
char dummy_stack[4096];
uthread_t p = uthread_create(NULL,dummy_stack,0,NULL);
*/
uthread_t p = uthread_create(NULL,NULL,,NULL);
uthread_t u = uthread_create(p,stack1,,ufun1);
uthread_switch(p,u,u);
printf("main end\n");
return ;
};

转自:https://www.cnblogs.com/sniperHW/archive/2012/08/05/2624334.html

(转)C协程实现的效率对比的更多相关文章

  1. python3 - 多线程和协程速率测试对比

    多线程和协程都属于IO密集型,我通过以下用例测试多线程和协程的实际速率对比. 实例:通过socket客户端以多线程并发模式请求不同服务器端(这里服务器端分2种写法:第一种服务器通过协程实现,第二种服务 ...

  2. 初学Python——协程

    进程.线程和协程区分 我们通常所说的协程Coroutine其实是corporate routine的缩写,直接翻译为协同的例程,一般我们都简称为协程. 在linux系统中,线程就是轻量级的进程,而我们 ...

  3. {python之协程}一 引子 二 协程介绍 三 Greenlet 四 Gevent介绍 五 Gevent之同步与异步 六 Gevent之应用举例一 七 Gevent之应用举例二

    python之协程 阅读目录 一 引子 二 协程介绍 三 Greenlet 四 Gevent介绍 五 Gevent之同步与异步 六 Gevent之应用举例一 七 Gevent之应用举例二 一 引子 本 ...

  4. python2.0_s12_day9_协程&Gevent协程

    Python之路,Day9 - 异步IO\数据库\队列\缓存 本节内容 Gevent协程 Select\Poll\Epoll异步IO与事件驱动 Python连接Mysql数据库操作 协程 1.协程,又 ...

  5. python爬虫——多线程+协程(threading+gevent)

    上一篇博客中我介绍了如何将爬虫改造为多进程爬虫,但是这种方法对爬虫效率的提升不是非常明显,而且占用电脑cpu较高,不是非常适用于爬虫.这篇博客中,我将介绍在爬虫中广泛运用的多线程+协程的解决方案,亲测 ...

  6. Python多线程、多进程和协程的实例讲解

    线程.进程和协程是什么 线程.进程和协程的详细概念解释和原理剖析不是本文的重点,本文重点讲述在Python中怎样实际使用这三种东西 参考: 进程.线程.协程之概念理解 进程(Process)是计算机中 ...

  7. 百万年薪python之路 -- 并发编程之 协程

    协程 一. 协程的引入 本节的主题是基于单线程来实现并发,即只用一个主线程(很明显可利用的cpu只有一个)情况下实现并发,为此我们需要先回顾下并发的本质:切换+保存状态 cpu正在运行一个任务,会在两 ...

  8. tbox新增stackless协程支持

    tbox之前提供的stackfull协程库,虽然切换效率已经非常高了,但是由于每个协程都需要维护一个独立的堆栈, 内存空间利用率不是很高,在并发量非常大的时候,内存使用量会相当大. 之前考虑过采用st ...

  9. Python 原生协程------asyncio

    协程 在python3.5以前,写成的实现都是通过生成器的yield from原理实现的, 这样实现的缺点是代码看起来会很乱,于是3.5版本之后python实现了原生的协程,并且引入了async和aw ...

随机推荐

  1. Composer的Autoload源码实现1——启动与初始化

    前言 上一篇文章,我们讨论了 PHP 的自动加载原理.PHP 的命名空间.PHP 的 PSR0 与 PSR4 标准,有了这些知识,其实我们就可以按照 PSR4 标准写出可以自动加载的程序了.然而我们为 ...

  2. git介绍和常用指令

    Git介绍和常用指令 介绍:Git和SVN一样都是版本控制工具.不同的是Git是分布式的,SVN是集中式的.Git开始用可能感觉难点,等你用习惯了你就会觉得svn是有点恐怖.(如果一个项目有好多人一起 ...

  3. centos7.0 安装php

    1:去php官网下载对应版本的php包 2:解压php包 3:进入解压后的php包 ./configure --with-apxs2=/usr/local/apache2/bin/apxs --wit ...

  4. 【BZOJ4548】小奇的糖果 set(链表)+树状数组

    [BZOJ4548]小奇的糖果 Description 有 N 个彩色糖果在平面上.小奇想在平面上取一条水平的线段,并拾起它上方或下方的所有糖果.求出最多能够拾起多少糖果,使得获得的糖果并不包含所有的 ...

  5. ubuntu 安装wine

    笔记 1.安装源 sudo add-apt-repository ppa:wine/wine-builds sudo apt-get update 2.安装wine sudo apt-get inst ...

  6. 九度OJ 1207:质因数的个数 (质数)

    时间限制:1 秒 内存限制:32 兆 特殊判题:否 提交:5939 解决:1926 题目描述: 求正整数N(N>1)的质因数的个数. 相同的质因数需要重复计算.如120=2*2*2*3*5,共有 ...

  7. Arrays数组工具类中存在的坑!

    以下是一个坑: 看到使用asList时候,可以返回一个集合,当然集合拥有CRUD的特性: 代码中使用 了add和remove时候均报错:Exception in thread "main&q ...

  8. python基础-第五篇-5.2递归

    又是一个阳光明媚的日子,小白看着刚刚从东边升起的太阳,感觉太阳爷爷也在向她打招呼,小白就不经的微笑起来!心想:今天又会学到什么有趣的东西呢?有些小期待,也有些小激动! 小刘来得比小白还早,两辆相视而笑 ...

  9. BZOJ1791: [Ioi2008]Island 岛屿

    BZOJ1791: [Ioi2008]Island 岛屿 Description 你将要游览一个有N个岛屿的公园. 从每一个岛i出发,只建造一座桥. 桥的长度以Li表示. 公园内总共有N座桥. 尽管每 ...

  10. 我的Android进阶之旅------>如何将Android源码导入Eclipse中来查看(非常实用)

    Android源码下载完成的目录结构如如所示: step1:将.classpath文件拷贝到源代码的根目录 Android源码支持多种IDE,如果是针对APP层做开发的话,建议大家使用Eclipse开 ...