(转)C协程实现的效率对比

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

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

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

下面贴出实现:

  1 #include "uthread.h"
  2 #include <stdlib.h>
  3 #include <ucontext.h>
  4 #include <pthread.h>
  5 #include "link_list.h"
  6 
  7 struct uthread
  8 {
  9     int32_t reg[8];//0:esp,1:ebp,2:eax,3:ebx,4:ecx,5:edx,6:edi,7:esi
 10     void *para;
 11     uthread_t parent;
 12     void*(*main_fun)(void*);
 13     void *stack;
 14     int32_t ssize;
 15     int8_t first_run;
 16 };
 17 
 18 #ifdef _DEBUG
 19 //for debug version
 20 void uthread_main_function()
 21 {
 22     int32_t arg;
 23      __asm__ volatile(
 24         "movl %%eax,%0	
"
 25         :
 26         :"m"(arg)
 27     );    
 28     
 29     uthread_t u = (uthread_t)arg;
 30     void *ret = u->main_fun(u->para);
 31     if(u->parent)
 32         uthread_switch(u,u->parent,ret);
 33     else
 34         exit(0); 
 35 }
 36 #else
 37 //for release version
 38 void __attribute__((regparm(1))) uthread_main_function(void *arg)
 39 {
 40     uthread_t u = (uthread_t)arg;
 41     void *ret = u->main_fun(u->para);
 42     if(u->parent)
 43         uthread_switch(u,u->parent,ret);
 44     else
 45         exit(0);
 46 }
 47 #endif
 48 uthread_t uthread_create(uthread_t parent,void*stack,uint32_t stack_size,void*(*fun)(void*))
 49 {
 50     uthread_t u = (uthread_t)calloc(1,sizeof(*u));
 51     u->parent = parent;
 52     u->main_fun = fun;
 53     u->stack = stack;
 54     u->ssize = stack_size;
 55     if(stack)
 56     {
 57         u->reg[0] = (int32_t)stack+stack_size-4;
 58         u->reg[1] = (int32_t)stack+stack_size-4;
 59     }
 60     if(u->main_fun)
 61         u->first_run = 1;
 62     return u;
 63 }
 64 
 65 void uthread_destroy(uthread_t *u)
 66 {
 67     free(*u);
 68     *u = NULL;
 69 }
 70 
 71 #ifdef _DEBUG
 72 void* __attribute__((regparm(3))) uthread_switch(uthread_t from,uthread_t to,void *para)
 73 {
 74     if(!from)
 75         return NULL;
 76     to->para = para;
 77     int32_t esp,ebp,eax,ebx,ecx,edx,edi,esi;
 78     //save current registers
 79     //the order is important    
 80      __asm__ volatile(
 81         "movl %%eax,%2	
"
 82         "movl %%ebx,%3	
"
 83         "movl %%ecx,%4	
"
 84         "movl %%edx,%5	
"
 85         "movl %%edi,%6	
"
 86         "movl %%esi,%7	
"     
 87         "movl %%ebp,%1	
"
 88         "movl %%esp,%0	
"
 89         :
 90         :"m"(esp),"m"(ebp),"m"(eax),"m"(ebx),"m"(ecx),"m"(edx),"m"(edi),"m"(esi)
 91     );
 92     from->reg[0] = esp;
 93     from->reg[1] = ebp;
 94     from->reg[2] = eax;
 95     from->reg[3] = ebx;
 96     from->reg[4] = ecx;
 97     from->reg[5] = edx;
 98     from->reg[6] = edi;
 99     from->reg[7] = esi;    
100     if(to->first_run)
101     {
102        to->first_run = 0;
103        esp = to->reg[0];
104        //use eax to pass arg
105        eax = (int32_t)to;
106         __asm__ volatile (
107             "movl %1,%%eax	
"
108             "movl %0,%%ebp	
"
109             "movl %%ebp,%%esp	
"
110             :
111             :"m"(esp),"m"(eax)
112         );       
113        uthread_main_function();
114     }
115     else
116     {
117         esp = to->reg[0];
118         ebp = to->reg[1];
119         eax = to->reg[2];
120         ebx = to->reg[3];
121         ecx = to->reg[4];
122         edx = to->reg[5];
123         edi = to->reg[6];
124         esi = to->reg[7];
125         //the order is important
126         __asm__ volatile (
127             "movl %2,%%eax	
"
128             "movl %3,%%ebx	
"
129             "movl %4,%%ecx	
"
130             "movl %5,%%edx	
"
131             "movl %6,%%edi	
"
132             "movl %7,%%esi	
"        
133             "movl %1,%%ebp	
"
134             "movl %0,%%esp	
"
135             :
136             :"m"(esp),"m"(ebp),"m"(eax),"m"(ebx),"m"(ecx),"m"(edx),"m"(edi),"m"(esi)
137         );
138     }    
139     return from->para;
140 }
141 #else
142 void* __attribute__((regparm(3))) uthread_switch(uthread_t from,uthread_t to,void *para)
143 {
144     if(!from)
145         return NULL;
146     to->para = para;
147     int32_t esp,ebp,edi,esi;
148     //save current registers
149     //the order is important    
150      __asm__ volatile(
151         "movl %%eax,%2	
"
152         "movl %%ebx,%3	
"
153         "movl %%ecx,%4	
"
154         "movl %%edx,%5	
"
155         "movl %%edi,%6	
"
156         "movl %%esi,%7	
"     
157         "movl %%ebp,%1	
"
158         "movl %%esp,%0	
"
159         :
160         :"m"(from->reg[0]),"m"(from->reg[1]),"m"(from->reg[2]),"m"(from->reg[3])
161         ,"m"(from->reg[4]),"m"(from->reg[5]),"m"(from->reg[6]),"m"(from->reg[7])
162     );
163     if(to->first_run)
164     {
165        to->first_run = 0;   
166        //change stack
167        //the order is important
168         __asm__ volatile (
169             "movl %0,%%ebp	
"
170             "movl %%ebp,%%esp	
"
171             :
172             :"m"(to->reg[0])
173         );               
174        uthread_main_function((void*)to);
175     }
176     else
177     {
178         esp = to->reg[0];
179         ebp = to->reg[1];
180         edi = to->reg[6];
181         esi = to->reg[7];
182         //the order is important
183         __asm__ volatile (
184             "movl %2,%%eax	
"
185             "movl %3,%%ebx	
"
186             "movl %4,%%ecx	
"
187             "movl %5,%%edx	
"
188             "movl %6,%%edi	
"
189             "movl %7,%%esi	
"        
190             "movl %1,%%ebp	
"
191             "movl %0,%%esp	
"
192             :
193             :"m"(esp),"m"(ebp),"m"(to->reg[2]),"m"(to->reg[3])
194             ,"m"(to->reg[4]),"m"(to->reg[5]),"m"(edi),"m"(esi)
195         );
196     }    
197     return from->para;
198 }
199 #endif

test.c

 1 #include <stdio.h>
 2 #include "uthread.h"
 3 #include "SysTime.h"
 4 #include <stdlib.h>
 5 void* ufun2(void *arg)
 6 {
 7     printf("ufun2
");
 8     char **tmp = (char**)arg;
 9     uthread_t self = (uthread_t)tmp[0];
10     uthread_t parent = (uthread_t)tmp[1];
11     volatile void *ptr = self;
12     while(ptr)
13     {
14         ptr = uthread_switch(self,parent,NULL);
15     }
16     return NULL;
17 }
18 
19 char *stack1;
20 char *stack2;
21 
22 void* ufun1(void *arg)
23 {
24     uthread_t self = (uthread_t)arg;
25     uthread_t u = uthread_create(self,stack2,4096,ufun2);
26     char* _arg[2];
27     _arg[0] = (char*)u;
28     _arg[1] = (char*)self;
29     int i = 0;
30     uint32_t tick = GetSystemMs();
31     for( ; i < 20000000; ++i)
32     {
33         uthread_switch(self,u,&_arg[0]);
34     }
35     printf("%d
",GetSystemMs()-tick);
36     uthread_switch(self,u,NULL);
37     return arg;
38 }
39 
40 int main()
41 {
42     stack1 = (char*)malloc(4096);
43     stack2 = (char*)malloc(4096);
44     /*
45      * if use ucontext version
46     char dummy_stack[4096];
47     uthread_t p = uthread_create(NULL,dummy_stack,0,NULL);
48     */
49     uthread_t p = uthread_create(NULL,NULL,0,NULL);
50     uthread_t u = uthread_create(p,stack1,4096,ufun1);
51     uthread_switch(p,u,u);
52     printf("main end
");
53     return 0;
54 };

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

原文地址:https://www.cnblogs.com/heluan/p/9899994.html