Libaco-燃えるような高速で軽量のC非対称コルーチンライブラリ。
このプロジェクトのコード名はArkenstoneですか?
非対称Coroutine&Arkenstoneがacoと名付けられた理由です。
現在、Intel386およびX86-64のSYS V ABIをサポートしています。
このプロジェクトの簡単な要約は次のとおりです。
上記の「最速」というフレーズは、Intel386またはAMD64のSYS V ABIに準拠する最速のコンテキストスイッチング実装を意味します。
問題とPRは大歓迎ですか?
注: masterの代わりにリリースを使用して、最終的なバイナリを作成してください。
このREADMEに加えて、https://libaco.org/docsからドキュメントをご覧ください。 WebサイトのドキュメントがこのREADMEから遅れをとっている可能性があるため、違いがある場合は、このREADMEに従ってください。
生産準備完了。
#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として定義します。また、メインCOはこのスタックの唯一のユーザーであるため、必要なCPUレジスタのメインCOの状態を保存/復元する必要があります。
次に、 non-main coの定義は、実行スタックが現在のスレッドのデフォルトスタックではなく、他の非メインCOと共有される可能性のあるスタックであるCoroutineです。したがって、非メインCOには、スイッチアウト/スイッチインのときに実行スタックを保存/復元するためのprivate save stackメモリバッファーが必要です(後続/先行COは、その実行スタックとして共有スタックを使用/使用した可能性があるため)。

非メインCOの特別なケースがあります。これは、Libacoで呼ばれるstandalone non-main coです。非メインコルーチンのシェアスタックには、COユーザーが1人しかいません。したがって、プライベートセーブスタックの保存/復元を行う必要はありません。他のCOがスタンドアロンの非メインCOの実行スタックに触れないため、スイッチアウト/スイッチインしているときにスイッチ/スイッチインします。

最後に、Libacoの全体像が得られます。
Libacoの内部に飛び込みたい、または独自のCoroutine Libraryを実装したい場合、本当に役立つ「正しさの証明」の部分があります。
また、次にチュートリアルとベンチマークのソースコードを読むことを強くお勧めします。ベンチマークの結果は非常に印象的で啓発的です。
-m32 GCCの-m32オプションは、X86_64マシンにI386アプリケーションのLIBACOアプリケーションを構築するのに役立ちます。
ACO_CONFIG_SHARE_FPU_MXCSR_ENVグローバルC Macro ACO_CONFIG_SHARE_FPU_MXCSR_ENVを定義して、コードがFPUとMXCSRの制御単語を変更しない場合、Coroutines間のコンテキストのパフォーマンスをわずかにスピードアップできます。マクロが定義されていない場合、すべてのCOはFPUおよびMXCSR制御ワードの独自のコピーを維持します。 ISO Cで定義されたデフォルトのenvを使用する代わりに、1つの関数がFPUまたはMXCSRの独自の特別なenvを設定する必要があることは非常にまれであるため、このマクロを常にグローバルに定義することをお勧めしますが、わからない場合はこのマクロを定義する必要はないかもしれません。
ACO_USE_VALGRIND ValgrindのツールMemcheckを使用してアプリケーションをテストする場合は、グローバルC Macro ACO_USE_VALGRINDを定義して、LibacoのValgrindの友好的なサポートを有効にする必要がある場合があります。ただし、パフォーマンスの理由でこのマクロを最終リリースビルドで定義することはお勧めしません。また、C Macro ACO_USE_VALGRINDを定義したLIBACOアプリケーションを構築するには、Valgrindヘッダー(たとえば、パッケージ名はCentosで「Valgrind-Devel」)をインストールする必要があります。 (ValgrindのMemcheckは現在、スタンドアロンCoとのみうまく機能します。複数の非メインCoが使用する共有スタックの場合、ValgrindのMemcheckは多くの誤った陽性レポートを生成します。詳細については、「test_aco_tutorial_6.c」を参照できます。)
ACO_USE_ASANグローバルCマクロACO_USE_ASAN 、Libacoの住所消毒剤の友好的なサポートを可能にします(GCCとClangの両方をサポート)。
リバコのテストスイートを構築するには:
$ 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要するに、valgrindヘッダーがインストールされていない場合は-o no-valgrindを使用します。 -o no-m32 AMD64ホストに32ビットGCC開発ツールがインストールされていない場合。
MacOSでは、MACOSのデフォルトのsedおよびgrepコマンドmake.sh GNU sedおよびgrep 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と1つのスタンドアロンの非メインCOのみがあります。ソースコードのコメントも非常に役立ちます。
test_aco_tutorial_1.c 、非メインcoのいくつかの統計の使用を示しています。 aco_tのデータ構造は非常に明確で、 aco.hで定義されています。
1つのメインCO、1つのスタンドアロンの非メインCOと2つの非メインCO(同じ共有スタックを指す)がtest_aco_tutorial_2.cにあります。
test_aco_tutorial_3.c 、マルチスレッドプロセスでlibacoの使用方法を示しています。基本的に、LIBACOの1つのインスタンスは、1つの特定のスレッド内で作業するように設計されており、コルーチン間のコンテキストスイッチの最大パフォーマンスを獲得します。マルチスレッド環境でLibacoを使用したい場合は、各スレッドにLibacoの1つのインスタンスを作成するだけです。 Libaco内のスレッド全体にデータ共有はありません。複数のスレッド間のデータ競合に自分で対処する必要があります(このチュートリアルでgl_race_aco_yield_ctが行うことなど)。
Libacoのルールの1つは、 aco_exit()を呼び出して、デフォルトの直接Cスタイルのreturnではなく非メインCOの実行を終了することです。そうしないと、Libacoは違法な動作を扱い、攻撃COに関するエラー情報をSTDERRに記録し、プロセスをすぐに中止するデフォルトプロテクターを引き起こします。 test_aco_tutorial_4.c 、このような「問題のある」状況を示しています。
また、デフォルトのプロテクターをデフォルトのプロテクターに置き換える(カスタマイズされた「最後の単語」のものを実行する)こともできます。しかし、どのような場合でも、プロテクターが実行された後、プロセスは中止されます。 test_aco_tutorial_5.c 、カスタマイズされた最後の単語関数を定義する方法を示しています。
最後の例は、 test_aco_tutorial_6.cのシンプルなCoroutineスケジューラです。
ソースコードはかなり明確で理解しやすいため、LIBACOの次のAPI説明を読んでいる場合、ソースコードの対応するAPI実装を同時に読み取ると非常に役立ちます。また、APIドキュメントを読む前に、すべてのチュートリアルを読むこともお勧めします。
リバコの実際のアプリケーションを書き始める前に、ベストプラクティスの部分を読むことを強くお勧めします(アプリケーションでリバコの極端なパフォーマンスを真にリリースする方法を説明することに加えて、Libacoのプログラミングについても通知があります)。
注:Libacoのバージョン制御は、Spec:Semanticバージョン2.0.0に従います。したがって、次のリストのAPIには互換性の保証があります。 (リスト内のAPI NOにそのような保証はないことに注意してください。)
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の制御単語( aco_create )を設定し、各COはFPUおよびMXCSRコントロールワードのコピーを維持します。ACO_CONFIG_SHARE_FPU_MXCSR_ENVが定義されている場合、すべてのCOはFPUとMXCSRの同じ制御単語を共有します。このドキュメントの「ビルドとテスト」の部分を参照して、これに関する詳細については、参照できます。また、「チュートリアル」パートのtest_aco_tutorial_5.cで述べたように、最初の引数last_word_co_fpがnullでない場合、 last_word_co_fpで指摘された関数は、プロセスが中止される前に違反Coについて「最後の単語」を行うためにデフォルトのプロテクターを置き換えます。このような最後の単語関数では、 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のアドバイザリーメモリサイズをバイトの新しい共有スタックを作成し、2番目の引数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の価値に近い。
2番目の引数guard_page_enabledの値が1の場合、Share Stackには、Stack Overflowを検出するための1つの読み取り専用ガードページがあり、 guard_page_enabledの値0はそのようなガードページなしで意味します。
この関数は常に有効な共有スタックを返します。
void aco_share_stack_destroy ( aco_share_stack_t * sstk );シェアスタック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 );新しいcoを作成します。
作成するMain_Coの場合は、 aco_create(NULL, NULL, 0, NULL, NULL)に電話してください。 Main Coは、共有スタックがデフォルトのスレッドスタックである特別なスタンドアロンコルーチンです。スレッドでは、Main Coは、他のすべての非メインコルーチンが行う前に作成され、実行を開始する必要があるコルーチンです。
そうでなければ、それはあなたが作成したい非メインCOです:
main_coは、COが将来のコンテキストの切り替えにaco_yieldを行う主なCOです。 main_co nullでなければなりません。share_stack 、作成したい非メインCOが将来実行するスタックとして使用する共有スタックのアドレスです。 share_stack nullでなければなりません。save_stack_sz 、このcoのプライベートセーブスタックのinitサイズを指定します。ユニットはバイトです。 0の値は、デフォルトのサイズ64バイトを使用することを意味します。プライベートセーブスタックがCOの実行スタックを保持するのに十分な大きさでない場合、別のCOに占有しているシェアスタックを生成する必要がある場合、通常、 szの値をまったく心配する必要はありません。しかし、Coの膨大な量(たとえば10,000,000)がプライベートセーブスタックを継続的にサイズ化すると、メモリアロケーターにパフォーマンスの影響を与えるため、 co->save_stack.max_cpszの最大値でsave_stack_szを設定することを非常に賢明で強くお勧めします(Coが実行している場合は、「Best Practice」を参照する場合があります)。co_fpは、coのエントリ関数ポインターです。 co_fp nullであってはなりません。argはポインター値であり、作成するco->argに設定されます。 Coの入力引数として使用できます。この関数は常に有効なcoを返します。そして、私たちは、あなたが作成したい非メインCOである場合、見返りにCOの状態を「init」と名付けます。
void aco_resume ( aco_t * co ); Caller Main Coからの利回り、およびcoの実行を開始または継続するため。
この関数の発信者はメインCOでなければならず、 co->main_coでなければなりません。そして、最初の議論co非メインCoでなければなりません。
co初めて再開したとき、 co->fpで指し示す関数の実行を開始します。 coがすでに譲られている場合、 aco_resumeそれを再起動し、実行を継続します。
aco_resumeの呼び出しの後、私たちは発信者の状態に名前を付けます。
void aco_yield (); coおよびResume co->main_coの実行を生成します。この関数の発信者は、非メインCOでなければなりません。 co->main_co nullでなければなりません。
aco_yieldの呼び出しの後、私たちは発信coの状態に名前を付けます。
aco_t * aco_get_co ();現在の非メインcoのポインターを返します。この関数の発信者は、非メインCOでなければなりません。
void * aco_get_arg (); (aco_get_co()->arg)に等しい。また、この関数の発信者は、非メインCOでなければなりません。
void aco_exit ();さらに、 aco_yield()と同じを実行します。aco_exit aco_exit()は、 co->is_endを1に設定して、「end」のステータスでcoをマークします。
void aco_destroy ( aco_t * co ); coを破壊します。議論coヌルであってはなりません。 co非メインCOである場合、プライベートセーブスタックも破壊されます。
#define ACO_VERSION_MAJOR 1
#define ACO_VERSION_MINOR 2
#define ACO_VERSION_PATCH 4これらの3つのマクロはヘッダーaco.hで定義されており、それらの値はSpec:Semanticバージョン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 "" "" "" "" "" "" "" "" "をオーバーライドすることを選択できます(C" Assert "はCマクロ定義であるため、ソースファイルの最後のディレクティブリストに含まれる必要があります)。デフォルトのc "assert"を使用する場合は、アプリケーションソースファイルにこのヘッダーを含めないでください。
詳細については、ソースファイルaco_assert_override.hを参照できます。
日付:2018年6月30日SAT UTC。
マシン:C5D.AWSのLarge。
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(スタックポインター)、およびプライベートセーブスタックとシェアスタックの間の保存/復元に関する直接的な説明はありません。
OSスレッドでは、メインのCoroutine main_co 、他のすべての非メインコルーチンが行う前に、最初に実行され始めたコロウチンです。
次の図は、MAIN_COとCOの間をコンテキストを切り替える簡単な例です。
この証拠では、Intel386とX86-64のSYS V ABIの間に根本的な違いがないため、Intel386のSYS V ABIの下にいると仮定します。また、FPUとMXCSRの制御語を変更しないと仮定します。

次の図は、実際には対称Coroutineの実行モデルであり、無制限の数の非MAIN CO-Sと1つのメインCOを備えています。非対称コルーチンは対称コルーチンの特別なケースであるため、これは問題ありません。対称コルーチンの正しさを証明することは、非対称のコルーチンよりも少し挑戦的であり、したがって、それはより楽しくなります。 (Libacoは、非対称Coroutine APIの意味的な意味が対称Coroutineよりもはるかに理解し、使用できるため、非対称コルーチンのAPIのみを実装しました。)

メインCOは最初のCoroutineの実行を開始するため、このOSスレッドの1番目のコンテキストスイッチングは、2番目の引数coが非メインCOであるacosw(main_co, co)の形式でなければなりません。
上記の図には2種類の状態転送しか存在しないことを証明するのは簡単です。
void* acosw(aco_t* from_co, aco_t* to_co)の正確性を証明するために、すべてのCOがacoswの呼び出しの前後にSYS v ABIの制約に常に準拠していることを証明することと同等です。 COのバイナリコードの他の部分( acoswを除く)は、すでにABIに準拠していたと仮定します(通常、コンパイラによって正しく生成されます)。
以下は、INTEL386 SYS V 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の場合、レジスタの使用量は、sys v abi intel386 v1.1の「p13-表2.3:レジスタ使用」で定義され、AMD64は「p23-図3.4:SYS V ABI AMD64 V1.0の登録使用法」にあります。)
証拠:

上記の図は、最初のケースのためのものです。「State 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も満足しています。

上記の図は、2番目のケースのためのものです。
制約: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 、最初のケースでなければなりません。StateCo-> init State Coを獲得し、次のすべてのacosw上記の2つのケースの1つでなければなりません。順番に、「すべてのCOは、 acoswの呼び出しの前後にSYS v ABIの制約に常に準拠している」ことを証明できました。したがって、証明は終了します。
システムV ABI X86-64には、レッドゾーンと呼ばれる新しいものがあります。
RSPで指された場所を越えた128バイトの領域は、予約されていると見なされ、信号または割り込みハンドラーによって変更されません。したがって、関数は、関数呼び出し全体で不要な一時的なデータにこの領域を使用する場合があります。特に、葉の機能は、プロローグとエピローグのスタックポインターを調整するのではなく、スタックフレーム全体にこの領域を使用する場合があります。このエリアはレッドゾーンとして知られています。
レッドゾーンは「Calleeによって保存されていない」ため、Coroutinesを切り替えるコンテキストではまったく気にしません( acosw葉の機能であるため)。
入力引数領域の終わりは、__m256または__m512がスタックで渡される場合、16(32または64、)バイト境界で整列するものとします。言い換えれば、コントロールが関数エントリポイントに転送されると、値(%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)を参照してください。
- MAN 7シグナル:信号処分
(E|R)SP信号が来るときにヒープ上のデータ構造を指している場合、ひどいことが起こる可能性があります。 (GDBのbreakpointとsignalコマンドを使用すると、このようなバグが便利に生成される可能性があります。 sigalstackを使用してデフォルトの信号スタックを変更すると問題が軽減される可能性がありますが、それでも(E|R)SPのそのような使用はABIに違反します。)
要約すると、Libacoのウルトラパフォーマンスを獲得したい場合は、 aco_yield可能な限り小さいと呼ぶポイントで、非スタンダローン非メインCOのスタック使用を維持してください。ローカル変数は通常共有スタック上にあるため、ローカル変数のアドレスをあるCOから別のCOに渡す場合は非常に注意してください。この種の変数をヒープから割り当てることは、常に賢明な選択です。
詳細には、5つのヒントがあります。
co_fp
/
/
f1 f2
/ /
/ f4
yield f3 f5
aco_yieldメインCOに屈するように呼び出す)のパフォーマンスに大きな影響を与えることに大きな影響があります。上記の図では、機能F2、F3、F4、およびF5のスタック使用量は、実行中のaco_yieldがないため、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を保持するアドレスは、CO_FP0の行7のgl_ptrとはまったく異なるセマンティクスを持っています。ただし、可変ctおよび関数inc_p同じコルーチンコンテキストにあるため、行11は問題ありません。この種の変数(他のコルーチンと共有する必要がある)をヒープに割り当てると、そのような問題が解決するだけです。 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 ();
}新しいアイデアは大歓迎です!
p = malloc(sz); assertalloc_ptr(p)のようなものの組み合わせである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
PayPal.meリンク
Alipay(支付(宝|寶))


Libacoのロゴは、Peter Bech(Peteck)からgeneしみなく寄付されています。ロゴは、CC by-nd 4.0でライセンスされています。 libaco.orgのウェブサイトは、Peter Bech(Peteck)から親切に貢献しています。
Copyright(c)2018、by sen han [email protected]。
Apacheライセンスの下で、バージョン2.0。
詳細については、ライセンスファイルを参照してください。