LIBACO-快速且輕巧的c不對稱的Coroutine庫。
該項目的代碼名稱是Arkenstone?
非對稱Coroutine&Arkenstone是其被命名為aco的原因。
目前支持Intel386和X86-64的SYS V ABI。
這是該項目的簡要摘要:
上面的“最快”一詞是指符合Intel386或AMD64的SYS VABI的最快上下文切換實現。
歡迎問題和公關???
注意:請使用版本而不是master構建最終二進製文件。
除了此讀數外,您還可以從https://libaco.org/docs訪問文檔。如果有任何差異,請遵循此讀書文件,因為網站上的文檔可能會落後於此。
準備就緒。
#include "aco.h"
#include <stdio.h>
// this header would override the default C `assert`;
// you may refer the "API : MACROS" part for more details.
#include "aco_assert_override.h"
void foo ( int ct ) {
printf ( "co: %p: yield to main_co: %dn" , aco_get_co (), * (( int * )( aco_get_arg ())));
aco_yield ();
* (( int * )( aco_get_arg ())) = ct + 1 ;
}
void co_fp0 () {
printf ( "co: %p: entry: %dn" , aco_get_co (), * (( int * )( aco_get_arg ())));
int ct = 0 ;
while ( ct < 6 ){
foo ( ct );
ct ++ ;
}
printf ( "co: %p: exit to main_co: %dn" , aco_get_co (), * (( int * )( aco_get_arg ())));
aco_exit ();
}
int main () {
aco_thread_init ( NULL );
aco_t * main_co = aco_create ( NULL , NULL , 0 , NULL , NULL );
aco_share_stack_t * sstk = aco_share_stack_new ( 0 );
int co_ct_arg_point_to_me = 0 ;
aco_t * co = aco_create ( main_co , sstk , 0 , co_fp0 , & co_ct_arg_point_to_me );
int ct = 0 ;
while ( ct < 6 ){
assert ( co -> is_end == 0 );
printf ( "main_co: yield to co: %p: %dn" , co , ct );
aco_resume ( co );
assert ( co_ct_arg_point_to_me == ct );
ct ++ ;
}
printf ( "main_co: yield to co: %p: %dn" , co , ct );
aco_resume ( co );
assert ( co_ct_arg_point_to_me == ct );
assert ( co -> is_end );
printf ( "main_co: destroy and exitn" );
aco_destroy ( co );
co = NULL ;
aco_share_stack_destroy ( sstk );
sstk = NULL ;
aco_destroy ( main_co );
main_co = NULL ;
return 0 ;
} # default build
$ gcc -g -O2 acosw.S aco.c test_aco_synopsis.c -o test_aco_synopsis
$ ./test_aco_synopsis
main_co: yield to co: 0x1887120: 0
co: 0x1887120: entry: 0
co: 0x1887120: yield to main_co: 0
main_co: yield to co: 0x1887120: 1
co: 0x1887120: yield to main_co: 1
main_co: yield to co: 0x1887120: 2
co: 0x1887120: yield to main_co: 2
main_co: yield to co: 0x1887120: 3
co: 0x1887120: yield to main_co: 3
main_co: yield to co: 0x1887120: 4
co: 0x1887120: yield to main_co: 4
main_co: yield to co: 0x1887120: 5
co: 0x1887120: yield to main_co: 5
main_co: yield to co: 0x1887120: 6
co: 0x1887120: exit to main_co: 6
main_co: destroy and exit
# i386
$ gcc -g -m32 -O2 acosw.S aco.c test_aco_synopsis.c -o test_aco_synopsis
# share fpu and mxcsr env
$ gcc -g -D ACO_CONFIG_SHARE_FPU_MXCSR_ENV -O2 acosw.S aco.c test_aco_synopsis.c -o test_aco_synopsis
# with valgrind friendly support
$ gcc -g -D ACO_USE_VALGRIND -O2 acosw.S aco.c test_aco_synopsis.c -o test_aco_synopsis
$ valgrind --leak-check=full --tool=memcheck ./test_aco_synopsis有關更多信息,您可以參考“構建和測試”部分。

普通執行狀態有4個基本元素: {cpu_registers, code, heap, stack} 。
由於代碼信息由({E|R})?IP寄存器指示,並且從堆分配的內存的地址通常直接或間接存儲在堆棧中,因此我們只能將4個元素簡化為其中的2個: {cpu_registers, stack} 。

我們將main co定義為Coroutine,他壟斷了當前線程的默認堆棧。而且,由於Main Co是此堆棧的唯一用戶,因此我們只需要在從/恢復到to(switched out-out/switched-in)時保存/還原所需的CPU寄存器狀態。
接下來, non-main co的定義是coroutine,其執行堆棧是一個堆棧,不是當前線程的默認堆棧,可以與其他非Main Co共享。因此,非MAIN CO必須具有一個private save stack存儲器緩衝區,以保存/還原其執行堆棧時,當它被切換/切換時(因為成功的/先前的CO可能會/使用/使用/使用/使用共享堆棧作為其執行堆棧)。

有一個非梅因公司的特殊情況,那就是我們在libaco中所說的standalone non-main co :非梅因·科魯特(Coroutine)的共享堆棧只有一個CO用戶。因此,當沒有其他CO會觸摸獨立的non-Main Co的執行堆棧,因此無需在切換/切換時保存/還原其私有保存堆棧的內容。

最後,我們得到了Libaco的全局。
如果您想潛入Libaco的內部或想實現自己的Coroutine庫,您可能會發現“正確性證明”部分。
還強烈建議您接下來閱讀教程和基準的源代碼。基準結果非常令人印象深刻,也具有啟發性。
-m32 GCC的-m32選項可以幫助您在X86_64機器上構建Libaco的i386應用。
ACO_CONFIG_SHARE_FPU_MXCSR_ENV您可以定義全局C ACO_CONFIG_SHARE_FPU_MXCSR_ENV如果您的代碼都不會更改FPU和MXCSR的控制詞,則可以稍微加快Coroutines之間上下文切換的性能。如果未定義宏,所有CO將保持其自己的FPU和MXCSR控制單詞的副本。建議始終在全球上定義此宏,因為很少有人需要設置自己的FPU或MXCSR特殊ENV,而不是使用ISO C定義的默認設想。但是,如果您不確定,您可能不需要定義此宏。
ACO_USE_VALGRIND如果您想使用Valgrind的工具備忘錄來測試應用程序,則可能需要定義全局C Macro ACO_USE_VALGRIND ,以啟用Libaco中Valgrind的友好支持。但是,出於性能原因,不建議在最終版本中定義此宏。您可能還需要安裝Valgrind標頭(例如,包裝名稱是CentOS中的“ Valgrind-Devel”),以構建使用CACO ACO_USE_VALGRIND定義的Libaco應用程序。 (Valgrind的Memcheck僅與目前的獨立CO合作。在多個非Main Co使用的共享堆棧中,Valgrind的Memcheck將產生許多誤報報告。有關更多信息,您可能會參考“ test_aco_tutorial_6.c”。)。
ACO_USE_ASAN全球C型ACO_USE_ASAN將在Libaco中友好支持地址消毒劑(支持GCC和Clang)。
建造Libaco的測試套件:
$ mkdir output
$ bash make.shMake.sh中還有一些詳細的選項:
$bash make.sh -h
Usage: make.sh [-o < no-m32 | no-valgrind > ] [-h]
Example:
# default build
bash make.sh
# build without the i386 binary output
bash make.sh -o no-m32
# build without the valgrind supported binary output
bash make.sh -o no-valgrind
# build without the valgrind supported and i386 binary output
bash make.sh -o no-valgrind -o no-m32簡而言之,如果您沒有安裝-O valgrind,則使用-o no-valgrind ,如果您在AMD64主機上沒有安裝32位GCC開發工具, -o no-m32 。
在MacOS上,您需要用GNU sed和grep替換MacOS的默認sed和grep命令,以運行make.sh和test.sh (將來將刪除此要求):
$ brew install grep --with-default-names
$ brew install gnu-sed --with-default-names$ cd output
$ bash ../test.sh該存儲庫中的test_aco_tutorial_0.c顯示了libaco的基本用法。本教程中,只有一個主要CO和一個獨立的非車手CO。源代碼中的評論也非常有幫助。
test_aco_tutorial_1.c顯示了某些統計數據的使用。 aco_t的數據結構非常清晰,並在aco.h中定義。
在test_aco_tutorial_2.c中,有一個主要CO,一個獨立的非梅恩CO和兩個非MAIN CO(指向相同的共享堆棧)。
test_aco_tutorial_3.c顯示瞭如何在多線程過程中使用libaco。基本上,Libaco的一個實例僅是為了在一個特定線程中工作,以獲得Coroutines之間上下文切換的最大性能。如果要在多線程環境中使用libaco,則只需在每個線程中創建一個libaco的實例即可。 Libaco內部的線程沒有數據共享,您必須自己處理多個線程之間的數據競爭(例如gl_race_aco_yield_ct在本教程中所做的事情)。
Libaco中的規則之一是致電aco_exit()終止執行非默恩CO,而不是默認的直接C樣式return ,否則Libaco將把這種行為視為非法的行為並觸發默認保護程序,其作業是將有關犯罪行為的錯誤信息記錄到STDERR並立即放棄該過程。 test_aco_tutorial_4.c顯示了這種“令人討厭的CO”情況。
您還可以定義自己的保護器來替換默認的保護器(做一些自定義的“最後單詞”操作)。但是,無論在什麼情況下,在執行保護者之後,該過程都將被中止。 test_aco_tutorial_5.c顯示瞭如何定義自定義的最後一個單詞函數。
最後一個示例是test_aco_tutorial_6.c中的簡單Coroutine調度程序。
當您閱讀LIBACO的以下API描述時,同時閱讀源代碼中的相應API實現將非常有幫助,因為源代碼非常清晰且易於理解。並且還建議在閱讀API文檔之前閱讀所有教程。
強烈建議在開始編寫Libaco的真實應用之前閱讀最佳實踐部分(除了描述如何真正發布Libaco在您的應用程序中的極端性能外,還有有關Libaco編程的通知)。
注意:Libaco的版本控件遵循規格:語義版本控制2.0.0。因此,以下列表中的API具有兼容性保證。 (請注意,列表中的API沒有任何保證。)
typedef void ( * aco_cofuncp_t )( void );
void aco_thread_init ( aco_cofuncp_t last_word_co_fp );初始化當前線程中的Libaco環境。
它將將FPU和MXCSR的當前控制詞存儲到線程 - 本地全局變量中。
ACO_CONFIG_SHARE_FPU_MXCSR_ENV ,則保存的控制詞將用作建立新CO的FPU和MXCSR的控制單詞的參考值,並且每個CO(在aco_create中)將維護FPU和MXCSR副本的副本,在以後的上下文中。ACO_CONFIG_SHARE_FPU_MXCSR_ENV ,則所有CO都共享FPU和MXCSR的相同控制單詞。您可以將本文檔的“構建和測試”一部分引用,以獲取有關此文檔的更多信息。正如它在“教程”部分的test_aco_tutorial_5.c中所說的那樣,當第一個參數last_word_co_fp並非無效時,那麼last_word_co_fp指向的函數將替換默認保護器來在此過程中刪除該過程之前對犯罪問題進行一些“最後的單詞”。在最後一個單詞函數中,您可以使用aco_get_co獲取有問題的CO的指針。有關更多信息,您可以閱讀test_aco_tutorial_5.c 。
aco_share_stack_t * aco_share_stack_new ( size_t sz );等於aco_share_stack_new2(sz, 1) 。
aco_share_stack_t * aco_share_stack_new2 ( size_t sz , char guard_page_enabled );創建一個新的共享堆棧,其中具有字節中的sz的諮詢內存大小,並且可能具有一個守衛頁面(僅讀取)以檢測堆棧溢出,這取決於第二個參數guard_page_enabled 。
要使用默認的大小值(2MB),如果第一個參數sz等於0。在對齊和儲備進行了一些計算之後,此功能將確保共享堆棧的最終有效長度為回報:
final_valid_sz >= 4096final_valid_sz >= szfinal_valid_sz % page_size == 0 if the guard_page_enabled != 0並儘可能接近sz的值。
當第二個參數的值guard_page_enabled的值為1時,返回的共享堆棧將具有一個僅讀取的保護頁面,用於檢測堆棧溢出,而guard_page_enabled的值為0的值,則無需使用此類保護頁面。
此功能將始終返回有效的共享堆棧。
void aco_share_stack_destroy ( aco_share_stack_t * sstk ); DESSORY share stack sstk 。
確保當您銷毀sstk時,所有共享堆棧是sstk的CO已經被銷毀。
typedef void ( * aco_cofuncp_t )( void );
aco_t * aco_create ( aco_t * main_co , aco_share_stack_t * share_stack ,
size_t save_stack_sz , aco_cofuncp_t co_fp , void * arg );創建一個新公司。
如果是您要創建的main_co,只需致電: aco_create(NULL, NULL, 0, NULL, NULL) 。 Main Co是一個特殊的獨立Coroutine,其共享堆棧是默認線程堆棧。在線程中,Main Co是Coroutine,應該在所有其他非梅恩Coroutine之前就開始執行並開始執行。
否則,這是您要創建的非Main Co:
main_co是Main Co,CO將在將來的上下文切換中aco_yield 。 main_co不得無效;share_stack是您想要創建的非梅因公司將來將其用作其執行堆棧的共享堆棧的地址。 share_stack不得無效;save_stack_sz指定了此CO的私有保存堆棧的初始大小。該單元在字節中。值為0表示使用默認尺寸64字節。由於當私人保存堆棧不夠大時,將發生自動調整大小,以至於在必須產生股份堆棧時將其執行的堆棧佔用,因此您通常不必擔心sz的價值。但是,當大量(例如10,000,000個CO)將其私有保存堆棧的大小調整大大的大小時,它將對內存分配給記憶分配帶來一些影響,因此非常明智,強烈建議將save_stack_sz設置為等於co->save_stack.max_cpsz的最大值的值等於CO的最大值(您可以在CO上運行)(您可以在此文檔中運行),以獲取“最佳實踐”部分的信息。co_fp是CO的輸入函數指針。 co_fp不得為null;arg是指針值,並將設置為CO創建的co->arg 。它可以用作CO的輸入參數。此功能將始終返回有效的CO。如果您是您要創建的非梅因公司,我們將CO的狀態命名為“ Init”。
void aco_resume ( aco_t * co );從呼叫者主公司屈服,並開始或繼續執行co 。
此功能的呼叫者必須是主要CO,並且必須是co->main_co 。第一個參數co必須是非梅因公司。
首次恢復co時,它開始運行由co->fp指向的功能。如果co已經產生, aco_resume重新啟動並繼續執行。
在aco_resume打電話後,我們將呼叫者的狀態稱為“屈服”。
void aco_yield ();產生co的執行,並簡歷co->main_co 。此功能的呼叫者必須是非墨包括co。和co->main_co不得無效。
在aco_yield打電話後,我們將co的狀態稱為“屈服”。
aco_t * aco_get_co ();返回當前非梅因公司的指針。此功能的呼叫者必須是非墨包括co。
void * aco_get_arg ();等於(aco_get_co()->arg) 。而且,此功能的呼叫者必須是非梅因公司。
void aco_exit ();另外,請與aco_yield()相同, aco_exit()還將co->is_end設置為1,以便以“ end”的狀態標記co 。
void aco_destroy ( aco_t * co );摧毀co 。參數co不能無效。如果co是非梅因公司,私人保存堆棧也將被銷毀。
#define ACO_VERSION_MAJOR 1
#define ACO_VERSION_MINOR 2
#define ACO_VERSION_PATCH 4這3個宏位於標題aco.h中,其值遵循規格:語義版本2.0.0。
// provide the compiler with branch prediction information
#define likely ( x ) aco_likely(x)
#define unlikely ( x ) aco_unlikely(x)
// override the default `assert` for convenience when coding
#define assert ( EX ) aco_assert(EX)
// equal to `assert((ptr) != NULL)`
#define assertptr ( ptr ) aco_assertptr(ptr)
// assert the successful return of memory allocation
#define assertalloc_bool ( b ) aco_assertalloc_bool(b)
#define assertalloc_ptr ( ptr ) aco_assertalloc_ptr(ptr)您可以選擇包括標題"aco_assert_override.h"以在諸如test_aco_synopsis.c之類的libaco應用程序中覆蓋默認的c“ servert”(此標頭應該在源文件中的最後一個指令列表中,因為c“ assert也是c” sastert也是C MACRO定義),並在上面的5個MACR中定義了5個MACR。如果要使用默認的c“斷言”,請不要將此標頭包含在應用程序源文件中。
有關更多詳細信息,您可以參考源文件ACO_ASSERT_OVERRIDE.H。
日期:6月30日星期六UTC 2018。
機器:c5d.large在AWS上。
OS:RHEL-7.5(Red Hat Enterprise Linux 7.5)。
這是基準部分的簡短摘要:
$ LD_PRELOAD=/usr/lib64/libtcmalloc_minimal.so.4 ./test_aco_benchmark..no_valgrind.shareFPUenv
+build:x86_64
+build:-DACO_CONFIG_SHARE_FPU_MXCSR_ENV
+build:share fpu & mxcsr control words between coroutines
+build:undefined ACO_USE_VALGRIND
+build:without valgrind memcheck friendly support
sizeof(aco_t)=152:
comment task_amount all_time_cost ns_per_op speed
aco_create/init_save_stk_sz=64B 1 0.000 s 230.00 ns/op 4347824.79 op/s
aco_resume/co_amount=1/copy_stack_size=0B 20000000 0.412 s 20.59 ns/op 48576413.55 op/s
-> acosw 40000000 0.412 s 10.29 ns/op 97152827.10 op/s
aco_destroy 1 0.000 s 650.00 ns/op 1538461.66 op/s
aco_create/init_save_stk_sz=64B 1 0.000 s 200.00 ns/op 5000001.72 op/s
aco_resume/co_amount=1/copy_stack_size=0B 20000000 0.412 s 20.61 ns/op 48525164.25 op/s
-> acosw 40000000 0.412 s 10.30 ns/op 97050328.50 op/s
aco_destroy 1 0.000 s 666.00 ns/op 1501501.49 op/s
aco_create/init_save_stk_sz=64B 2000000 0.131 s 65.50 ns/op 15266771.53 op/s
aco_resume/co_amount=2000000/copy_stack_size=8B 20000000 0.666 s 33.29 ns/op 30043022.64 op/s
aco_destroy 2000000 0.066 s 32.87 ns/op 30425152.25 op/s
aco_create/init_save_stk_sz=64B 2000000 0.130 s 65.22 ns/op 15332218.24 op/s
aco_resume/co_amount=2000000/copy_stack_size=24B 20000000 0.675 s 33.75 ns/op 29630018.73 op/s
aco_destroy 2000000 0.067 s 33.45 ns/op 29898311.36 op/s
aco_create/init_save_stk_sz=64B 2000000 0.131 s 65.42 ns/op 15286937.97 op/s
aco_resume/co_amount=2000000/copy_stack_size=40B 20000000 0.669 s 33.45 ns/op 29891277.59 op/s
aco_destroy 2000000 0.080 s 39.87 ns/op 25084242.29 op/s
aco_create/init_save_stk_sz=64B 2000000 0.224 s 111.86 ns/op 8940010.49 op/s
aco_resume/co_amount=2000000/copy_stack_size=56B 20000000 0.678 s 33.88 ns/op 29515473.53 op/s
aco_destroy 2000000 0.067 s 33.42 ns/op 29922412.68 op/s
aco_create/init_save_stk_sz=64B 2000000 0.131 s 65.74 ns/op 15211896.70 op/s
aco_resume/co_amount=2000000/copy_stack_size=120B 20000000 0.769 s 38.45 ns/op 26010724.94 op/s
aco_destroy 2000000 0.088 s 44.11 ns/op 22669240.25 op/s
aco_create/init_save_stk_sz=64B 10000000 1.240 s 123.97 ns/op 8066542.54 op/s
aco_resume/co_amount=10000000/copy_stack_size=8B 40000000 1.327 s 33.17 ns/op 30143409.55 op/s
aco_destroy 10000000 0.328 s 32.82 ns/op 30467658.05 op/s
aco_create/init_save_stk_sz=64B 10000000 0.659 s 65.94 ns/op 15165717.02 op/s
aco_resume/co_amount=10000000/copy_stack_size=24B 40000000 1.345 s 33.63 ns/op 29737708.53 op/s
aco_destroy 10000000 0.337 s 33.71 ns/op 29666697.09 op/s
aco_create/init_save_stk_sz=64B 10000000 0.654 s 65.38 ns/op 15296191.35 op/s
aco_resume/co_amount=10000000/copy_stack_size=40B 40000000 1.348 s 33.71 ns/op 29663992.77 op/s
aco_destroy 10000000 0.336 s 33.56 ns/op 29794574.96 op/s
aco_create/init_save_stk_sz=64B 10000000 0.653 s 65.29 ns/op 15316087.09 op/s
aco_resume/co_amount=10000000/copy_stack_size=56B 40000000 1.384 s 34.60 ns/op 28902221.24 op/s
aco_destroy 10000000 0.337 s 33.73 ns/op 29643682.93 op/s
aco_create/init_save_stk_sz=64B 10000000 0.652 s 65.19 ns/op 15340872.40 op/s
aco_resume/co_amount=10000000/copy_stack_size=120B 40000000 1.565 s 39.11 ns/op 25566255.73 op/s
aco_destroy 10000000 0.443 s 44.30 ns/op 22574242.55 op/s
aco_create/init_save_stk_sz=64B 2000000 0.131 s 65.61 ns/op 15241722.94 op/s
aco_resume/co_amount=2000000/copy_stack_size=136B 20000000 0.947 s 47.36 ns/op 21114212.05 op/s
aco_destroy 2000000 0.125 s 62.35 ns/op 16039466.45 op/s
aco_create/init_save_stk_sz=64B 2000000 0.131 s 65.71 ns/op 15218784.72 op/s
aco_resume/co_amount=2000000/copy_stack_size=136B 20000000 0.948 s 47.39 ns/op 21101216.29 op/s
aco_destroy 2000000 0.125 s 62.73 ns/op 15941559.26 op/s
aco_create/init_save_stk_sz=64B 2000000 0.131 s 65.49 ns/op 15270258.18 op/s
aco_resume/co_amount=2000000/copy_stack_size=152B 20000000 1.069 s 53.44 ns/op 18714275.17 op/s
aco_destroy 2000000 0.122 s 61.05 ns/op 16378678.85 op/s
aco_create/init_save_stk_sz=64B 2000000 0.132 s 65.91 ns/op 15171336.62 op/s
aco_resume/co_amount=2000000/copy_stack_size=232B 20000000 1.190 s 59.48 ns/op 16813230.99 op/s
aco_destroy 2000000 0.123 s 61.26 ns/op 16324298.25 op/s
aco_create/init_save_stk_sz=64B 2000000 0.131 s 65.68 ns/op 15224361.30 op/s
aco_resume/co_amount=2000000/copy_stack_size=488B 20000000 1.828 s 91.40 ns/op 10941133.56 op/s
aco_destroy 2000000 0.145 s 72.56 ns/op 13781182.82 op/s
aco_create/init_save_stk_sz=64B 2000000 0.132 s 65.80 ns/op 15197461.34 op/s
aco_resume/co_amount=2000000/copy_stack_size=488B 20000000 1.829 s 91.47 ns/op 10932139.32 op/s
aco_destroy 2000000 0.149 s 74.70 ns/op 13387258.82 op/s
aco_create/init_save_stk_sz=64B 1000000 0.067 s 66.63 ns/op 15007426.35 op/s
aco_resume/co_amount=1000000/copy_stack_size=1000B 20000000 4.224 s 211.20 ns/op 4734744.76 op/s
aco_destroy 1000000 0.093 s 93.36 ns/op 10711651.49 op/s
aco_create/init_save_stk_sz=64B 1000000 0.066 s 66.28 ns/op 15086953.73 op/s
aco_resume/co_amount=1000000/copy_stack_size=1000B 20000000 4.222 s 211.12 ns/op 4736537.93 op/s
aco_destroy 1000000 0.094 s 94.09 ns/op 10627664.78 op/s
aco_create/init_save_stk_sz=64B 100000 0.007 s 70.72 ns/op 14139923.59 op/s
aco_resume/co_amount=100000/copy_stack_size=1000B 20000000 4.191 s 209.56 ns/op 4771909.70 op/s
aco_destroy 100000 0.010 s 101.21 ns/op 9880747.28 op/s
aco_create/init_save_stk_sz=64B 100000 0.007 s 66.62 ns/op 15010433.00 op/s
aco_resume/co_amount=100000/copy_stack_size=2024B 20000000 7.002 s 350.11 ns/op 2856228.03 op/s
aco_destroy 100000 0.016 s 159.69 ns/op 6262129.35 op/s
aco_create/init_save_stk_sz=64B 100000 0.007 s 65.76 ns/op 15205994.08 op/s
aco_resume/co_amount=100000/copy_stack_size=4072B 20000000 11.918 s 595.90 ns/op 1678127.54 op/s
aco_destroy 100000 0.019 s 186.32 ns/op 5367189.85 op/s
aco_create/init_save_stk_sz=64B 100000 0.006 s 63.03 ns/op 15865531.37 op/s
aco_resume/co_amount=100000/copy_stack_size=7992B 20000000 21.808 s 1090.42 ns/op 917079.11 op/s
aco_destroy 100000 0.038 s 378.33 ns/op 2643225.42 op/s
$ LD_PRELOAD=/usr/lib64/libtcmalloc_minimal.so.4 ./test_aco_benchmark..no_valgrind.standaloneFPUenv
+build:x86_64
+build:undefined ACO_CONFIG_SHARE_FPU_MXCSR_ENV
+build:each coroutine maintain each own fpu & mxcsr control words
+build:undefined ACO_USE_VALGRIND
+build:without valgrind memcheck friendly support
sizeof(aco_t)=160:
comment task_amount all_time_cost ns_per_op speed
aco_create/init_save_stk_sz=64B 1 0.000 s 273.00 ns/op 3663004.27 op/s
aco_resume/co_amount=1/copy_stack_size=0B 20000000 0.415 s 20.76 ns/op 48173877.75 op/s
-> acosw 40000000 0.415 s 10.38 ns/op 96347755.51 op/s
aco_destroy 1 0.000 s 381.00 ns/op 2624672.26 op/s
aco_create/init_save_stk_sz=64B 1 0.000 s 212.00 ns/op 4716980.43 op/s
aco_resume/co_amount=1/copy_stack_size=0B 20000000 0.415 s 20.75 ns/op 48185455.26 op/s
-> acosw 40000000 0.415 s 10.38 ns/op 96370910.51 op/s
aco_destroy 1 0.000 s 174.00 ns/op 5747123.38 op/s
aco_create/init_save_stk_sz=64B 2000000 0.131 s 65.63 ns/op 15237386.02 op/s
aco_resume/co_amount=2000000/copy_stack_size=8B 20000000 0.664 s 33.20 ns/op 30119155.82 op/s
aco_destroy 2000000 0.065 s 32.67 ns/op 30604542.55 op/s
aco_create/init_save_stk_sz=64B 2000000 0.131 s 65.33 ns/op 15305975.29 op/s
aco_resume/co_amount=2000000/copy_stack_size=24B 20000000 0.675 s 33.74 ns/op 29638360.61 op/s
aco_destroy 2000000 0.067 s 33.31 ns/op 30016633.42 op/s
aco_create/init_save_stk_sz=64B 2000000 0.131 s 65.61 ns/op 15241767.78 op/s
aco_resume/co_amount=2000000/copy_stack_size=40B 20000000 0.678 s 33.88 ns/op 29518648.08 op/s
aco_destroy 2000000 0.079 s 39.74 ns/op 25163018.30 op/s
aco_create/init_save_stk_sz=64B 2000000 0.221 s 110.73 ns/op 9030660.30 op/s
aco_resume/co_amount=2000000/copy_stack_size=56B 20000000 0.684 s 34.18 ns/op 29253416.65 op/s
aco_destroy 2000000 0.067 s 33.40 ns/op 29938840.64 op/s
aco_create/init_save_stk_sz=64B 2000000 0.131 s 65.60 ns/op 15244077.65 op/s
aco_resume/co_amount=2000000/copy_stack_size=120B 20000000 0.769 s 38.43 ns/op 26021228.41 op/s
aco_destroy 2000000 0.087 s 43.74 ns/op 22863987.42 op/s
aco_create/init_save_stk_sz=64B 10000000 1.251 s 125.08 ns/op 7994958.59 op/s
aco_resume/co_amount=10000000/copy_stack_size=8B 40000000 1.327 s 33.19 ns/op 30133654.80 op/s
aco_destroy 10000000 0.329 s 32.85 ns/op 30439787.32 op/s
aco_create/init_save_stk_sz=64B 10000000 0.674 s 67.37 ns/op 14843796.57 op/s
aco_resume/co_amount=10000000/copy_stack_size=24B 40000000 1.354 s 33.84 ns/op 29548523.05 op/s
aco_destroy 10000000 0.339 s 33.90 ns/op 29494634.83 op/s
aco_create/init_save_stk_sz=64B 10000000 0.672 s 67.19 ns/op 14882262.88 op/s
aco_resume/co_amount=10000000/copy_stack_size=40B 40000000 1.361 s 34.02 ns/op 29393520.19 op/s
aco_destroy 10000000 0.338 s 33.77 ns/op 29609577.59 op/s
aco_create/init_save_stk_sz=64B 10000000 0.673 s 67.31 ns/op 14857716.02 op/s
aco_resume/co_amount=10000000/copy_stack_size=56B 40000000 1.371 s 34.27 ns/op 29181897.80 op/s
aco_destroy 10000000 0.339 s 33.85 ns/op 29540633.63 op/s
aco_create/init_save_stk_sz=64B 10000000 0.672 s 67.24 ns/op 14873017.10 op/s
aco_resume/co_amount=10000000/copy_stack_size=120B 40000000 1.548 s 38.71 ns/op 25835542.17 op/s
aco_destroy 10000000 0.446 s 44.61 ns/op 22415961.64 op/s
aco_create/init_save_stk_sz=64B 2000000 0.132 s 66.01 ns/op 15148290.52 op/s
aco_resume/co_amount=2000000/copy_stack_size=136B 20000000 0.944 s 47.22 ns/op 21177946.19 op/s
aco_destroy 2000000 0.124 s 61.99 ns/op 16132721.97 op/s
aco_create/init_save_stk_sz=64B 2000000 0.133 s 66.36 ns/op 15068860.85 op/s
aco_resume/co_amount=2000000/copy_stack_size=136B 20000000 0.944 s 47.20 ns/op 21187541.38 op/s
aco_destroy 2000000 0.124 s 62.21 ns/op 16073322.25 op/s
aco_create/init_save_stk_sz=64B 2000000 0.131 s 65.62 ns/op 15238955.93 op/s
aco_resume/co_amount=2000000/copy_stack_size=152B 20000000 1.072 s 53.61 ns/op 18652789.74 op/s
aco_destroy 2000000 0.121 s 60.42 ns/op 16551368.04 op/s
aco_create/init_save_stk_sz=64B 2000000 0.132 s 66.08 ns/op 15132547.65 op/s
aco_resume/co_amount=2000000/copy_stack_size=232B 20000000 1.198 s 59.88 ns/op 16699389.91 op/s
aco_destroy 2000000 0.121 s 60.71 ns/op 16471465.52 op/s
aco_create/init_save_stk_sz=64B 2000000 0.133 s 66.50 ns/op 15036985.95 op/s
aco_resume/co_amount=2000000/copy_stack_size=488B 20000000 1.853 s 92.63 ns/op 10796126.04 op/s
aco_destroy 2000000 0.146 s 72.87 ns/op 13723559.36 op/s
aco_create/init_save_stk_sz=64B 2000000 0.132 s 66.14 ns/op 15118324.13 op/s
aco_resume/co_amount=2000000/copy_stack_size=488B 20000000 1.855 s 92.75 ns/op 10781572.22 op/s
aco_destroy 2000000 0.152 s 75.79 ns/op 13194130.51 op/s
aco_create/init_save_stk_sz=64B 1000000 0.067 s 66.97 ns/op 14931921.56 op/s
aco_resume/co_amount=1000000/copy_stack_size=1000B 20000000 4.218 s 210.90 ns/op 4741536.66 op/s
aco_destroy 1000000 0.093 s 93.16 ns/op 10734691.98 op/s
aco_create/init_save_stk_sz=64B 1000000 0.066 s 66.49 ns/op 15039274.31 op/s
aco_resume/co_amount=1000000/copy_stack_size=1000B 20000000 4.216 s 210.81 ns/op 4743543.53 op/s
aco_destroy 1000000 0.094 s 93.97 ns/op 10641539.58 op/s
aco_create/init_save_stk_sz=64B 100000 0.007 s 70.95 ns/op 14094724.73 op/s
aco_resume/co_amount=100000/copy_stack_size=1000B 20000000 4.190 s 209.52 ns/op 4772746.50 op/s
aco_destroy 100000 0.010 s 100.99 ns/op 9902271.51 op/s
aco_create/init_save_stk_sz=64B 100000 0.007 s 66.49 ns/op 15040038.84 op/s
aco_resume/co_amount=100000/copy_stack_size=2024B 20000000 7.028 s 351.38 ns/op 2845942.55 op/s
aco_destroy 100000 0.016 s 159.15 ns/op 6283444.42 op/s
aco_create/init_save_stk_sz=64B 100000 0.007 s 65.73 ns/op 15214482.36 op/s
aco_resume/co_amount=100000/copy_stack_size=4072B 20000000 11.879 s 593.95 ns/op 1683636.60 op/s
aco_destroy 100000 0.018 s 184.23 ns/op 5428119.00 op/s
aco_create/init_save_stk_sz=64B 100000 0.006 s 63.41 ns/op 15771072.16 op/s
aco_resume/co_amount=100000/copy_stack_size=7992B 20000000 21.808 s 1090.42 ns/op 917081.56 op/s
aco_destroy 100000 0.038 s 376.78 ns/op 2654073.13 op/s
在開始實施或證明Coroutine庫之前,要非常熟悉Intel386和X86-64的SYS V ABI標準。
下面的證明沒有有關IP(指令指針),SP(堆棧指針)以及私人保存堆棧和共享堆棧之間的保存/恢復的直接描述,因為這些東西在將其與ABI約束內容進行比較時易於理解。
在OS線程中,主要的Coroutine main_co是Coroutine,應該在所有其他非梅恩·克羅伊特(Coroutines)之前首先創建並開始執行。
下圖是main_co和Co之間上下文切換的一個簡單示例。
在此證明中,我們只是假設我們在Intel386的Sys v Abi下,因為Intel386和X86-64之間沒有根本差異。我們還假設沒有任何代碼會更改FPU和MXCSR的控制詞。

下一個圖實際上是對稱的Coroutine運行模型,該模型具有無限數量的非Main Co-S和一個主CO。這很好,因為不對稱的Coroutine只是對稱coroutine的特殊情況。證明對稱的coroutine的正確性比不對稱的Coroutine更具挑戰性,因此會變得更加有趣。 (Libaco僅實施了非對稱Coroutine的API,因為不對稱Coroutine API的語義含義比對稱的Coroutine更容易理解和使用。)

由於主要CO是第一個Coroutine開始運行,因此該操作系統線程中的第一個上下文切換必須以acosw(main_co, co)的形式進行,其中第二個參數co是一個非MAIN CO。
很容易證明上圖中只有兩種狀態轉移:
為了證明void* acosw(aco_t* from_co, aco_t* to_co)的正確性是等效的,以證明所有CO在呼叫acosw之前和之後都不斷符合SYS VABI的約束。我們假設CO中的二進制代碼的另一部分( acosw除外)已經符合ABI(通常由編譯器正確生成)。
以下是登記冊中登記冊的限制的摘要。
Registers' usage in the calling convention of the Intel386 System V ABI:
caller saved (scratch) registers:
C1.0: EAX
At the entry of a function call:
could be any value
After the return of `acosw`:
hold the return value for `acosw`
C1.1: ECX,EDX
At the entry of a function call:
could be any value
After the return of `acosw`:
could be any value
C1.2: Arithmetic flags, x87 and mxcsr flags
At the entry of a function call:
could be any value
After the return of `acosw`:
could be any value
C1.3: ST(0-7)
At the entry of a function call:
the stack of FPU must be empty
After the return of `acosw`:
the stack of FPU must be empty
C1.4: Direction flag
At the entry of a function call:
DF must be 0
After the return of `acosw`:
DF must be 0
C1.5: others: xmm*,ymm*,mm*,k*...
At the entry of a function call:
could be any value
After the return of `acosw`:
could be any value
callee saved registers:
C2.0: EBX,ESI,EDI,EBP
At the entry of a function call:
could be any value
After the return of `acosw`:
must be the same as it is at the entry of `acosw`
C2.1: ESP
At the entry of a function call:
must be a valid stack pointer
(alignment of 16 bytes, retaddr and etc...)
After the return of `acosw`:
must be the same as it is before the call of `acosw`
C2.2: control word of FPU & mxcsr
At the entry of a function call:
could be any configuration
After the return of `acosw`:
must be the same as it is before the call of `acosw`
(unless the caller of `acosw` assume `acosw` may
change the control words of FPU or MXCSR on purpose
like `fesetenv`)
(對於Intel386,寄存器使用量在“ p13-表2.3:寄存器用法”中定義。
證明:

以上圖是第一種情況:“產生狀態CO-> Init State Co”。
約束:C 1.0、1.1、1.2、1.5(滿意✓)
下面的划痕寄存器可以在函數輸入時保持任何值:
EAX,ECX,EDX
XMM*,YMM*,MM*,K*...
status bits of EFLAGS,FPU,MXCSR
約束:C 1.3,1.4(滿意✓)
由於FPU的堆棧必須已經空了,並且在調用acosw(co, to_co)之前,DF必須已經為0(CO的二進制代碼已遵守ABI),因此約束1.3和1.4由acosw遵守。
約束:C 2.0、2.1、2.2(滿意✓)
C 2.0和2.1已經滿足。由於我們已經假設沒有人會更改FPU和MXCSR的控制詞,因此C 2.2也得到滿足。

以上圖是第二種情況:產生狀態co->產生的狀態co。
約束:C 1.0(滿意✓)
當acosw返回到to_co(簡歷)時,EAX已經保持返回值。
約束:C 1.1、1.2、1.5(滿意✓)
下面的划痕寄存器可以在函數輸入和acosw返回之後保持任何值:
ECX,EDX
XMM*,YMM*,MM*,K*...
status bits of EFLAGS,FPU,MXCSR
約束:C 1.3,1.4(滿意✓)
由於FPU的堆棧必須已經空了,並且在調用acosw(co, to_co)之前,DF必須已經為0(CO的二進制代碼已遵守ABI),因此約束1.3和1.4由acosw遵守。
約束:C 2.0、2.1、2.2(滿意✓)
C 2.0&2.1之所以滿足,是因為當撥打/返回acosw時,Callee保存的寄存器保存和還原。由於我們已經假設沒有人會更改FPU和MXCSR的控制詞,因此C 2.2也得到滿足。
線程中的第一個acosw必須是第一種情況:產生狀態CO-> INT state Co,所有下一個acosw必須是上面的2個情況之一。順便說一句,我們可以證明“所有CO都不斷符合acosw呼叫之前和之後的SYS V ABI的約束”。因此,證明完成了。
系統v ABI x86-64中有一個叫做紅色區域的新事物:
超出指向%RSP的位置的128字節區域被認為是保留的,不得通過信號或中斷處理程序進行修改。因此,功能可以將此領域用於跨函數調用中不需要的臨時數據。特別是,葉子功能可以將此區域用於整個堆棧框架,而不是調整序言和結語中的堆棧指針。該區域稱為紅色區域。
由於紅色區域“不是由Callee保存”,因此在Coroutines之間的上下文切換中,我們根本不在乎它(因為acosw是葉子功能)。
輸入參數區域的末端應在16(32或64)上對齊,如果__M256或__M512在堆棧上傳遞)字節邊界。換句話說,當控件傳輸到功能入口點時,值(%ESP + 4)始終是16(32或64)的倍數。堆棧指針%ESP總是指向最新分配的堆棧框架的結束。
- intel386-psabi-1.1:2.2.2堆棧框架
堆棧指針%RSP總是指向最新分配的堆棧框架的末尾。
- SYS V ABI AMD64版本1.0:3.2.2堆棧框架
這是Tencent Libco中的一個錯誤示例。 ABI指出(E|R)SP應始終指向最新分配的堆棧框架的末尾。但是在libco的文件coctx_swap.s中, (E|R)SP已用於解決堆上的內存。
默認情況下,信號處理程序在正常過程堆棧上被調用。可以安排信號處理程序使用替代堆棧;有關如何執行此操作以及何時有用的討論,請參見Sigalstack(2)。
- 男子7信號:信號處置
如果(E|R)SP指向信號時(堆)的數據結構,則可能會發生可怕的事情。 (使用GDB的breakpoint和signal命令可以方便地產生此類錯誤。儘管通過使用sigalstack更改默認信號堆棧可能會減輕問題,但是仍然存在(E|R)SP仍然違反ABI的使用情況。)
總而言之,如果您想獲得Libaco的超級性能,只需在將aco_yield盡可能小的時候保留非標準元非MAIN CO的堆棧使用。如果要將局部變量的地址從一個CO傳遞到另一個CO,請非常小心,因為局部變量通常在共享堆棧上。從堆中分配這種變量始終是更明智的選擇。
詳細說明,有5個提示:
co_fp
/
/
f1 f2
/ /
/ f4
yield f3 f5
aco_yield將其寄回到Main Co)對Coroutines之間上下文切換的性能產生了很大的影響,如基準測試結果所示。在上圖中,功能F2,F3,F4和F5的堆棧使用情況對上下文切換性能沒有直接影響,因為它們執行時沒有aco_yield ,而CO_FP和F1的堆棧使用佔CO_FP和F1的堆棧使用佔co->save_stack.max_cpsz的值,並且在上下文開關上具有很大的影響。保持盡可能低的功能堆棧使用的關鍵是在堆上分配局部變量(尤其是大變量)並手動管理其生命週期,而不是默認情況下在堆棧上分配它們。 GCC的-fstack-usage選項對此非常有幫助。
int * gl_ptr ;
void inc_p ( int * p ){ ( * p ) ++ ; }
void co_fp0 () {
int ct = 0 ;
gl_ptr = & ct ; // line 7
aco_yield ();
check ( ct );
int * ptr = & ct ;
inc_p ( ptr ); // line 11
aco_exit ();
}
void co_fp1 () {
do_sth ( gl_ptr ); // line 16
aco_exit ();
}gl_ptr中保存的地址(第16行)與CO_FP0第7行中的gl_ptr具有完全不同的語義,並且這種代碼可能會損壞CO_FP1的執行堆棧。但是第11行是可以的,因為可變ct和FUNCTION inc_p處於相同的Coroutine上下文中。在堆上分配這種變量(需要與其他協調分享)只會解決此類問題: int * gl_ptr ;
void inc_p ( int * p ){ ( * p ) ++ ; }
void co_fp0 () {
int * ct_ptr = malloc ( sizeof ( int ));
assert ( ct_ptr != NULL );
* ct_ptr = 0 ;
gl_ptr = ct_ptr ;
aco_yield ();
check ( * ct_ptr );
int * ptr = ct_ptr ;
inc_p ( ptr );
free ( ct_ptr );
gl_ptr = NULL ;
aco_exit ();
}
void co_fp1 () {
do_sth ( gl_ptr );
aco_exit ();
}歡迎新想法!
添加一個宏,例如aco_mem_new ,它是p = malloc(sz); assertalloc_ptr(p) 。
添加新的API aco_reset來支持Coroutine對象的可重複使用性。
支持其他平台(尤其是ARM和ARM64)。
v1.2.4 Sun Jul 29 2018
Changed `asm` to `__asm__` in aco.h to support compiler's `--std=c99`
flag (Issue #16, proposed by Theo Schlossnagle @postwait).
v1.2.3 Thu Jul 26 2018
Added support for MacOS;
Added support for shared library build of libaco (PR #10, proposed
by Theo Schlossnagle @postwait);
Added C macro ACO_REG_IDX_BP in aco.h (PR #15, proposed by
Theo Schlossnagle @postwait);
Added global C config macro ACO_USE_ASAN which could enable the
friendly support of address sanitizer (both gcc and clang) (PR #14,
proposed by Theo Schlossnagle @postwait);
Added README_zh.md.
v1.2.2 Mon Jul 9 2018
Added a new option `-o <no-m32|no-valgrind>` to make.sh;
Correction about the value of macro ACO_VERSION_PATCH (issue #1
kindly reported by Markus Elfring @elfring);
Adjusted some noncompliant naming of identifiers (double underscore
`__`) (issue #1, kindly proposed by Markus Elfring @elfring);
Supported the header file including by C++ (issue #4, kindly
proposed by Markus Elfring @elfring).
v1.2.1 Sat Jul 7 2018
Fixed some noncompliant include guards in two C header files (
issue #1 kindly reported by Markus Elfring @elfring);
Removed the "pure" word from "pure C" statement since it is
containing assembly codes (kindly reported by Peter Cawley
@corsix);
Many updates in the README.md document.
v1.2.0 Tue Jul 3 2018
Provided another header named `aco_assert_override.h` so user
could choose to override the default `assert` or not;
Added some macros about the version information.
v1.1 Mon Jul 2 2018
Removed the requirement on the GCC version (>= 5.0).
v1.0 Sun Jul 1 2018
The v1.0 release of libaco, cheers ???
我是專職開源開發人員。任何數量的捐款都將受到高度讚賞,並可能給我帶來極大的鼓勵。
貝寶
paypal.me鏈接
apay(支付(寶|寶))


Libaco的徽標由Peter Bech(Peteck)慷慨捐贈。該徽標是根據CC BY-ND 4.0許可的。 libaco.org的網站也由彼得·貝克(Peter Bech)(Peteck)撰寫。
版權所有(C)2018,由Sen Han [email protected]。
在Apache許可證下,版本2.0。
有關詳細信息,請參見許可證文件。