Libaco - Eine loderne schnelle und leichte C -asymmetrische Coroutine -Bibliothek.
Der Codename dieses Projekts ist Arkenstone?
Asymmetrischer Coroutine & Arkenstone ist der Grund, warum es aco heißt.
Unterstützt derzeit SYS V ABI von Intel386 und x86-64.
Hier ist eine kurze Zusammenfassung dieses Projekts:
Der Ausdruck " schnellste " in oben bedeutet die schnellste Implementierung von Kontextschaltungen, die dem SYS V ABI von Intel386 oder AMD64 entspricht.
Probleme und PRs sind willkommen ???
HINWEIS: Bitte verwenden Sie Veröffentlichungen anstelle des master , um die endgültige Binärdatei aufzubauen.
Neben diesem Readme können Sie auch die Dokumentation von https://libaco.org/docs besuchen. Bitte folgen Sie diesem Readme, wenn es Unterschiede gibt, da die Dokumentation auf der Website möglicherweise von diesem Readme zurückbleibt.
Produktion fertig.
#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_synopsisWeitere Informationen finden Sie im Teil "Build and Test".

Es gibt 4 grundlegende Elemente eines gewöhnlichen Ausführungszustands: {cpu_registers, code, heap, stack} .
Da die Codeinformationen durch ({E|R})?IP -Register angezeigt werden und die Adresse des von Heaps zugewiesenen Speichers normalerweise direkt oder indirekt im Stapel gespeichert ist, können wir die 4 Elemente nur in 2 von ihnen vereinfachen: {cpu_registers, stack} .

Wir definieren die main co als Coroutine, die den Standardstapel des aktuellen Threads monopolisiert. Und da der Haupt-CO der einzige Benutzer dieses Stapels ist, müssen wir nur den erforderlichen Zustand der CPU-Register des Haupt-CO speichern/wiederherstellen, wenn er von der/wieder aufgenommenen (geschalteten/ausgeschalteten) erhalten wurde.
Als nächstes ist die Definition des non-main co die Coroutine, deren Ausführungsstapel ein Stapel ist, der nicht der Standardstapel des aktuellen Threads ist und mit dem anderen Nicht-Main-CO geteilt werden kann. Daher muss der Nicht-Main-CO einen private save stack haben, um seinen Ausführungsstapel zu speichern/wiederherzustellen, wenn er ausgeschaltet/eingeschaltet wurde (da der nachfolgende/vorhergehende Co May/Gebrauch den Share-Stapel als Ausführungsstapel verwendet/verwendet hätte).

Es gibt einen Sonderfall von Non-Main Co, dh standalone non-main co was wir in Libaco bezeichnet haben: Der Share-Stapel der Nicht-Main-Coroutine hat nur einen CO-Benutzer. Daher müssen Sie keine Save-Sachen seines privaten Save-Stacks sparen/restaurieren, wenn er ausgeschaltet/eingeschaltet wurde, da es keinen anderen CO gibt, der den Ausführungsstapel des eigenständigen Nicht-Main-CO außer sich berührt.

Schließlich machen wir das große Ganze von Libaco.
Es gibt einen Teil der Korrektheit von "Korrektheit", das Sie möglicherweise sehr hilfreich finden, wenn Sie in den Internal von Libaco eintauchen oder Ihre eigene Coroutine -Bibliothek implementieren möchten.
Es wird auch dringend empfohlen, den Quellcode der Tutorials und als Benchmark als nächstes zu lesen. Das Benchmark -Ergebnis ist auch sehr beeindruckend und aufschlussreich.
-m32 Die Option -m32 -Option von GCC könnte Ihnen helfen, die i386 -Anwendung von Libaco auf einer X86_64 -Maschine zu erstellen.
ACO_CONFIG_SHARE_FPU_MXCSR_ENV Sie können das globale C -Makro ACO_CONFIG_SHARE_FPU_MXCSR_ENV definieren, um die Leistung des Kontext -Schaltens zwischen Coroutinen geringfügig zu beschleunigen, wenn keiner Ihrer Code die Steuerwörter von FPU und MXCSR ändern würde. Wenn das Makro nicht definiert ist, würde alle CO seine eigene Kopie der FPU- und MXCSR -Kontrollwörter beibehalten. Es wird empfohlen, dieses Makro immer global zu definieren, da es sehr selten ist, dass eine Funktion eine eigene Umwelt für FPU oder MXCSR festlegen muss, anstatt die vom ISO C definierte Standard -Umgebung zu verwenden, aber Sie müssen dieses Makro möglicherweise nicht definieren, wenn Sie sich nicht sicher sind.
ACO_USE_VALGRIND Wenn Sie den Werkzeugmemcheck von Valgrind verwenden möchten, um die Anwendung zu testen, müssen Sie möglicherweise die globale C -Makro ACO_USE_VALGRIND definieren, um die freundliche Unterstützung von Valgrind in Libaco zu ermöglichen. Es wird jedoch nicht empfohlen, dieses Makro im endgültigen Release -Build aus dem Leistungsgrund zu definieren. Möglicherweise müssen Sie auch die Valgrind-Header (zum Beispiel "Valgrind-Devel" in CentOS installieren (Packungsname ", um die Libaco-Anwendung mit C-Macro ACO_USE_VALGRIND zu erstellen. (Der Memcheck von Valgrind funktioniert derzeit nur gut mit dem Standalone Co. Im Fall des gemeinsamen Stacks, der von mehr als einem Nicht-Main-Co verwendet wird, würde der Memcheck von Valgrind viele falsche positive Berichte generieren. Weitere Informationen finden Sie in "test_aco_tutorial_6.c".)
ACO_USE_ASAN Der globale C -Makro ACO_USE_ASAN würde die freundliche Unterstützung von Adresseinrichtungen in Libaco (Unterstützung sowohl GCC als auch Clang) ermöglichen.
Um die Testsuiten von Libaco zu bauen:
$ mkdir output
$ bash make.shEs gibt auch einige detaillierte Optionen in make.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 Kurz gesagt, mit -o no-valgrind , wenn Sie keine Valgrind-Header installiert haben, -o no-m32 , wenn Sie keine 32-Bit-GCC-Entwicklungstools auf einem AMD64-Host installiert haben.
Bei macOS müssen Sie die Standard sed und grep -Befehle von macOS durch die Gnu sed und grep zum Ausführen make.sh und test.sh ersetzen (diese Anforderung würde in Zukunft entfernt):
$ brew install grep --with-default-names
$ brew install gnu-sed --with-default-names$ cd output
$ bash ../test.sh Der test_aco_tutorial_0.c in diesem Repository zeigt die grundlegende Verwendung von Libaco. In diesem Tutorial gibt es nur einen HauptcO und einen eigenständigen Nicht-Main-Co. Die Kommentare im Quellcode sind ebenfalls sehr hilfreich.
Der test_aco_tutorial_1.c zeigt die Verwendung einiger Statistiken von Non-Main Co. Die Datenstruktur von aco_t ist sehr klar und in aco.h
Es gibt einen HauptcO, einen eigenständigen Nicht-Main-Co und zwei Nicht-Main-CO (auf denselben Share-Stack) in test_aco_tutorial_2.c .
Der test_aco_tutorial_3.c zeigt, wie Libaco in einem Multithread -Prozess verwendet wird. Grundsätzlich ist eine Instanz von Libaco nur so konzipiert, dass sie in einem bestimmten Faden arbeitet, um die maximale Leistung des Kontextwechsels zwischen Coroutinen zu erzielen. Wenn Sie Libaco in einer multitHhread -Umgebung verwenden möchten, um in jedem der Threads eine Instanz von Libaco zu erstellen. Es gibt keine Datenaustausch über Threads im Libaco, und Sie müssen sich mit dem Datenwettbewerb zwischen mehreren Threads selbst befassen (wie das, was gl_race_aco_yield_ct in diesem Tutorial tut).
Eine der Regeln in Libaco ist es, aco_exit() aufzurufen, um die Ausführung des Nicht-Main-CO anstelle der Standard-Direktrendite für den Direkter C-Stil zu return . Andernfalls behandelt Libaco das Verhalten als illegal und löst den Standardschutz aus, dessen Aufgabe darin besteht, die Fehlerinformationen über die Auslagerung von CO nach STDERR zu protokollieren und den Prozess sofort abzusetzen. Die test_aco_tutorial_4.c zeigt eine solche Situation "Straftat CO".
Sie können Ihren eigenen Beschützer auch definieren, um die Standardeinstellung zu ersetzen (um einige angepasste "letzte Wörter" zu erstellen). Unabhängig davon, in welchem Fall der Prozess nach der Ausführung des Beschützers abgebrochen wird. Der test_aco_tutorial_5.c zeigt, wie Sie die angepasste Last Word -Funktion definieren.
Das letzte Beispiel ist ein einfacher Coroutine -Scheduler in test_aco_tutorial_6.c .
Es wäre sehr hilfreich, die entsprechende API -Implementierung im Quellcode gleichzeitig zu lesen, wenn Sie die folgende API -Beschreibung von Libaco lesen, da der Quellcode ziemlich klar und leicht zu verstehen ist. Es wird auch empfohlen, alle Tutorials vor dem Lesen des API -Dokuments zu lesen.
Es wird dringend empfohlen, den Best -Practice -Teil zu lesen, bevor Sie die tatsächliche Anwendung von Libaco schreiben (zusätzlich zur Beschreibung, wie Libacos extreme Leistung in Ihrer Anwendung wirklich veröffentlicht wird, gibt es auch eine Mitteilung über die Programmierung von Libaco).
Hinweis: Die Versionskontrolle von Libaco folgt der Spezifikation: Semantic Versioning 2.0.0. Die API in der folgenden Liste hat also die Kompatibilitätsgarantie. (Bitte beachten Sie, dass die API NEIN in der Liste keine solche Garantie gibt.)
typedef void ( * aco_cofuncp_t )( void );
void aco_thread_init ( aco_cofuncp_t last_word_co_fp );Initialisiert die Libaco -Umgebung im aktuellen Thread.
Es wird die aktuellen Kontrollwörter von FPU und MXCSR in eine globale Thread-lokale Variable gespeichert.
ACO_CONFIG_SHARE_FPU_MXCSR_ENV nicht definiert ist, werden die gespeicherten Kontrollwörter als Referenzwert verwendet, um die Kontrollwörter der neuen CO -FPU- und MXCSR (im aco_create ) des neuen CO zu errichten, und jedes CO würde seine eigene Kopie von FPU- und MXCSR -Kontrollwörtern während späterer Kontextwörter während des späteren Kontextwörters während des späteren Wechsels aufrechterhalten.ACO_CONFIG_SHARE_FPU_MXCSR_ENV definiert ist, teilt alle CO die gleichen Steuerwörter von FPU und MXCSR. Sie können den Teil dieses Dokuments "Build and Test" verweisen, um weitere Informationen dazu zu erhalten. Und wie es im Teil test_aco_tutorial_5.c des "Tutorials" -Teils erwähnt wird, wird die Funktion, die von last_word_co_fp , nicht null ist, wenn das erste Argument last_word_co_fp ist, ersetzt den Standardschutz, um einige "letzte Wörter" über das beleidigtes CO zu machen, bevor der Prozess abgebrochen wird. In einer solchen Last Word -Funktion können Sie mit aco_get_co den Zeiger der beleidigenden co erhalten. Für weitere Informationen können Sie test_aco_tutorial_5.c lesen.
aco_share_stack_t * aco_share_stack_new ( size_t sz ); Gleich wie aco_share_stack_new2(sz, 1) .
aco_share_stack_t * aco_share_stack_new2 ( size_t sz , char guard_page_enabled ); Erstellt einen neuen Share-Stack mit einer beratenden Speichergröße von sz in Bytes und kann eine Wachseite (schreibgeschützt) zur Erkennung von Stapelüberlauf haben, der vom 2. Argument guard_page_enabled abhängt.
Um den Standardgrößenwert (2 MB) zu verwenden, wenn das 1. Argument sz gleich 0 entspricht. Nach einer Berechnung von Ausrichtung und Reserve stellt diese Funktion die endgültige gültige Länge des Freigabestapels als Gegenleistung sicher:
final_valid_sz >= 4096final_valid_sz >= szfinal_valid_sz % page_size == 0 if the guard_page_enabled != 0 Und so nah am Wert von sz wie möglich.
Wenn der Wert des zweiten Arguments guard_page_enabled 1 beträgt, würde der Share-Stack im Gegenzug eine schreibgeschützte Wachseite für die Erkennung des Stapelüberlaufs haben, während ein Wert 0 von guard_page_enabled ohne solche Schutzseite bedeutet.
Diese Funktion gibt immer einen gültigen Aktienstack zurück.
void aco_share_stack_destroy ( aco_share_stack_t * sstk ); Zählen Sie den Share Stack sstk .
Stellen Sie sicher, dass der gesamte CO, dessen Share -Stack sstk ist, bereits zerstört ist, wenn Sie die sstk zerstören.
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 );Erstellen Sie eine neue co.
Wenn es sich um einen Main_co handelt, den Sie erstellen möchten, rufen Sie einfach an: aco_create(NULL, NULL, 0, NULL, NULL) . Main CO ist eine spezielle eigenständige Coroutine, deren Share -Stack der Standard -Thread -Stapel ist. Im Thread ist Main Co die Coroutine, die erstellt und vor der Ausführung vor allen anderen Nicht-Main-Coroutine ausführen sollte.
Andernfalls ist es ein Nicht-Main-Co, den Sie erstellen möchten:
main_co ist der HauptcO. Der CO wird im zukünftigen Kontextschalter aco_yield . main_co darf nicht null sein;share_stack ist die Adresse eines Share-Stacks, den die von Ihnen erstellte Nicht-Main-CO als ausführende Stack in Zukunft verwenden wird. share_stack darf nicht null sein;save_stack_sz gibt die Init -Größe des privaten Save -Stapels dieser co an. Das Gerät ist in Bytes. Ein Wert von 0 bedeutet, die Standardgröße 64 Bytes zu verwenden. Da die automatische Änderung der Größenänderung passieren würde, wenn der private Save -Stack nicht groß genug ist, um den ausführenden Stapel des CO zu halten, wenn er den Aktienstapel ergeben muss, den er für einen anderen CO besetzt, sollten Sie sich normalerweise überhaupt nicht um den Wert von sz kümmern. Aber es wird einige Leistungseinflüsse auf den Speicherallocator bringen, wenn eine große Menge (z. B. 10.000.000) des CO ihren privaten Save-Stack kontinuierlich ändert. Daher ist es sehr klug und dringend empfohlen, den save_stack_sz mit einem Wert zu setzen, der gleich dem Maximalwert von co->save_stack.max_cpsz ist, wenn der CO-optimiert werden.co_fp ist der Zeiger der Eingabefunktion des Co. co_fp darf nicht null sein;arg ist ein Zeigerwert und wird auf co->arg Erstellung des CO eingestellt. Es könnte als Eingabeargument für die co verwendet werden.Diese Funktion gibt immer eine gültige Co zurück. Und wir nennen den Zustand des CO als Gegenleistung als "init", wenn es sich um einen Nicht-Main-CO handelt, den Sie erstellen möchten.
void aco_resume ( aco_t * co ); Ausbeute vom Caller Main Co und um die Ausführung von co zu beginnen oder fortzusetzen.
Der Anrufer dieser Funktion muss ein HauptcO sein und muss co->main_co sein. Und das 1. Argument co muss ein Nicht-Main-Co sein.
Wenn Sie zum ersten Mal einen co wieder aufnehmen, wird die Funktion mit co->fp ausgeführt. Wenn co bereits nachgegeben wurde, startet aco_resume es neu und setzt die Ausführung fort.
Nach dem Ruf von aco_resume nennen wir den Zustand des Anrufers - Main Co als "geboten".
void aco_yield (); Ergeben Sie die Ausführung von co und Lebenslauf co->main_co . Der Anrufer dieser Funktion muss ein Nicht-Main-Co sein. Und co->main_co darf nicht null sein.
Nach dem Ruf von aco_yield nennen wir den Zustand des Anrufers - co als "nachgegeben".
aco_t * aco_get_co ();Geben Sie den Zeiger des aktuellen Nicht-Main-Co. zurück. Der Anrufer dieser Funktion muss ein Nicht-Main-Co sein.
void * aco_get_arg (); Gleich (aco_get_co()->arg) . Und auch der Anrufer dieser Funktion muss ein Nicht-Main-Co sein.
void aco_exit (); Darüber hinaus setzen ACO_EXIT () das Gleiche wie aco_yield() , aco_exit() auch co->is_end auf 1, um den co im Status von "Ende" zu markieren.
void aco_destroy ( aco_t * co ); Zerstöre die co . Das Argument co darf nicht null sein. Der private Save Stack würde auch zerstört worden, wenn der co ein Nicht-Main-Co ist.
#define ACO_VERSION_MAJOR 1
#define ACO_VERSION_MINOR 2
#define ACO_VERSION_PATCH 4 Diese 3 Makros sind im Header aco.h definiert, und der Wert von ihnen folgt der Spezifikation: Semantic Versioning 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) Sie könnten den Header "aco_assert_override.h" in die Libaco -Anwendung wie test_aco_Synopsis.c überschreiben (dieser Header, einschließlich sollte auch die letzte der Einfügungsanweisungen in der Quelldatei in der oben genannten Makros sind, um das Standard -C "Assert" in die Libaco -Anwendung zu überschreiben. Bitte geben Sie diesen Header nicht in die Anwendungsquelldatei ein, wenn Sie die Standard -C "Assert" verwenden möchten.
Weitere Informationen finden Sie in der Quelldatei aco_assert_override.h.
Datum: Sa 30. Juni UTC 2018.
Maschine: C5d.Large auf AWS.
OS: RHEL-7.5 (Red Hat Enterprise Linux 7.5).
Hier ist eine kurze Zusammenfassung des Benchmark -Teils:
$ 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
Es ist wichtig, mit dem Standard von SYS V ABI von Intel386 und X86-64 vertraut zu sein, bevor Sie eine Coroutine-Bibliothek implementieren oder beweisen.
Der folgende Beweis enthält keine direkte Beschreibung über die IP (Anweisungszeiger), SP (Stack Zeiger) und das Speichern/Wiederherstellen zwischen dem privaten Save Stack und dem Share -Stack, da diese Dinge ziemlich trivial und leicht zu verstehen sind, wenn sie mit den ABI -Einschränkungen verglichen werden.
Im OS-Thread ist die Hauptkoroutine main_co die Coroutine, die erstellt und zuerst ausgeführt werden sollte, bevor alle anderen Nicht-Main-Coroutinen dies tun.
Das nächste Diagramm ist ein einfaches Beispiel für den Kontextwechsel zwischen Main_co und Co.
In diesem Beweis nehmen wir nur an, dass wir unter sys v abi von intel386 stehen, da es keine grundlegenden Unterschiede zwischen dem sys v abi von Intel386 und x86-64 gibt. Wir gehen auch davon aus, dass keiner der Code die Kontrollwörter von FPU und MXCSR ändern würde.

Das nächste Diagramm ist eigentlich ein symmetrisches Coroutine-Laufmodell, das eine unbegrenzte Anzahl von Nicht-Main-Co-S und One Main Co aufweist. Dies ist in Ordnung, da die asymmetrische Korutine nur ein Sonderfall der symmetrischen Korutine ist. Die Richtigkeit der symmetrischen Coroutine zu beweisen, ist etwas schwieriger als die asymmetrische Korutine und somit mehr Spaß machen. (Libaco hat derzeit nur die API von asymmetrischer Coroutine implementiert, da die semantische Bedeutung der asymmetrischen Coroutine -API weitaus einfacher ist als die symmetrische Coroutine.)

Da der Haupt-CO die 1. Coroutine ist, die beginnt zu laufen, muss der 1. Kontextschalter in diesem OS-Thread in Form von acosw(main_co, co) sein, wobei das 2. Argument co ein Nicht-Main-CO ist.
Es ist leicht zu beweisen, dass es im obigen Diagramm nur zwei Arten von Staatsübertragungen gibt:
Um die Richtigkeit von void* acosw(aco_t* from_co, aco_t* to_co) zu beweisen, ist gleichwertig, um alle COs zu beweisen, die die Einschränkungen von SYS V ABI vor und nach dem Ruf von acosw ständig entsprechen. Wir gehen davon aus, dass der andere Teil des Binärcode (außer acosw ) im CO bereits dem ABI entspricht (sie werden normalerweise vom Compiler korrekt erzeugt).
Hier finden Sie eine Zusammenfassung der Einschränkungen der Register in der Funktion, die die Konvention von Intel386 SYS V ABI bezeichnet:
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`)
(Für Intel386 ist die Registerverwendung in der "P13 - Tabelle 2.3: Registrierungsnutzung" von SYS V ABI INTEL386 V1.1 definiert, und für AMD64 befindet sich in "P23 - Abbildung 3.4: Register -Nutzung" von SYS V ABI AMD64 V1.0.)
Nachweisen:

Das obige Diagramm ist für den 1. Fall: "ergab State Co -> Init State Co".
Einschränkungen: C 1,0, 1.1, 1,2, 1,5 ( erfüllt ✓)
Die folgenden Kratzerregister können bei der Eingabe einer Funktion einen Wert halten:
EAX,ECX,EDX
XMM*,YMM*,MM*,K*...
status bits of EFLAGS,FPU,MXCSR
Einschränkungen: C 1.3, 1.4 ( erfüllt ✓)
Da der Stapel der FPU bereits leer sein muss und der DF bereits 0 sein muss, bevor acosw(co, to_co) aufgerufen wurde (der Binärcode von CO ist bereits dem ABI eingehalten), wird die Einschränkung 1.3 und 1.4 von acosw eingehalten.
Einschränkungen: C 2.0, 2.1, 2.2 ( erfüllt ✓)
C 2.0 & 2.1 ist bereits zufrieden. Da wir bereits angenommen haben, dass niemand die Kontrollwörter von FPU und MXCSR ändern wird, ist auch C 2.2 zufrieden.

Das obige Diagramm ist für den 2. Fall: State Co -> State Co.
Einschränkungen: C 1.0 ( zufrieden ✓)
EAX hält den Rückgabewert bereits, wenn acosw zurück zu to_co (Lebenslauf) zurückkehrt.
Einschränkungen: C 1.1, 1.2, 1.5 ( erfüllt ✓)
Die folgenden Kratzerregister können bei der Eingabe einer Funktion und nach der Rückgabe von acosw jeden Wert halten:
ECX,EDX
XMM*,YMM*,MM*,K*...
status bits of EFLAGS,FPU,MXCSR
Einschränkungen: C 1.3, 1.4 ( erfüllt ✓)
Da der Stapel der FPU bereits leer sein muss und der DF bereits 0 sein muss, bevor acosw(co, to_co) aufgerufen wurde (der Binärcode von CO ist bereits dem ABI eingehalten), wird die Einschränkung 1.3 und 1.4 von acosw eingehalten.
Einschränkungen: C 2.0, 2.1, 2.2 ( erfüllt ✓)
C 2.0 & 2.1 ist zufrieden, da die Callee -geretteten Register retten und restaurieren, wenn acosw gerufen/zurückgegeben wurde. Da wir bereits angenommen haben, dass niemand die Kontrollwörter von FPU und MXCSR ändern wird, ist auch C 2.2 zufrieden.
Der 1. acosw im Thread muss der 1. Fall sein: State Co -> Init State Co, und der gesamte nächste acosw muss einer der 2 Fälle oben sein. Nacheinander konnten wir beweisen, dass "alle CO den Einschränkungen von SYS v Abi vor und nach dem Ruf von acosw ständig entsprechen". Somit ist der Beweis fertig.
Es gibt eine neue Sache namens Red Zone in System v ABI X86-64:
Der 128-Byte-Bereich über den von %RSP über den Ort hingewiesenen Ort wird als reserviert angesehen und darf nicht durch Signal- oder Interrupt-Handler geändert werden. Daher können Funktionen diesen Bereich für temporäre Daten verwenden, die über Funktionsaufrufe hinweg nicht in Bezug auf Funktionen benötigt werden. Insbesondere können Blattfunktionen diesen Bereich für ihren gesamten Stapelrahmen verwenden, anstatt den Stapelzeiger im Prolog und im Epilog anzupassen. Dieser Bereich ist als rote Zone bekannt.
Da die rote Zone "nicht von der Callee erhalten" ist, kümmern wir uns im Zusammenhang zwischen Coroutinen (weil die acosw eine Blattfunktion ist).
Das Ende der Eingabebereich wird auf einer Bytegrenze von __m256 oder __m512 an einem 16 (32 oder 64, wenn __m256 oder __m512 übergeben werden. Mit anderen Worten, der Wert (%ESP + 4) ist immer ein Vielfaches von 16 (32 oder 64), wenn die Kontrolle auf den Funktionseintrittspunkt übertragen wird. Der Stapelzeiger %ESP zeigt immer auf das Ende des neuesten zugewiesenen Stapelrahmens.
-Intel386-psabi-1.1: 2.2.2 Der Stapelrahmen
Der Stapelzeiger %RSP verweist immer auf das Ende des neuesten zugewiesenen Stapelrahmens.
- SYS V ABI AMD64 Version 1.0: 3.2.2 Der Stapelrahmen
Hier ist ein Fehlerbeispiel in Tencents Libco. Der ABI gibt an, dass der (E|R)SP immer auf das Ende des neuesten zugewiesenen Stapelrahmens hinweisen sollte. In der Datei cocTX_SWAP.S von Libco war die (E|R)SP verwendet worden, um den Speicher auf dem Haufen zu adressieren.
Standardmäßig wird der Signalhandler auf dem normalen Prozessstapel aufgerufen. Es ist möglich zu arrangieren, dass der Signalhandler einen alternativen Stapel verwendet. Sigalstack (2) finden Sie in einer Diskussion darüber, wie dies zu tun ist und wann dies nützlich sein könnte.
- Mann 7 Signal: Signaldispositionen
Schreckliche Dinge können passieren, wenn der (E|R)SP auf die Datenstruktur auf dem Haufen zeigt, wenn das Signal kommt. (Die Verwendung der breakpoint und signal von GDB könnte einen solchen Fehler bequem erzeugen. Obwohl mit sigalstack , um den Standardsignalstapel zu ändern, das Problem lindern könnte, aber trotzdem verletzt diese Art von Nutzung von (E|R)SP immer noch gegen das Abi.)
Zusammenfassend, wenn Sie die Ultra-Leistung von Libaco erzielen möchten, halten Sie einfach die Stapelverwendung des Nicht-Standalone-Non-Main-Co zum Zeitpunkt des Aufrufens aco_yield so klein wie möglich. Und seien Sie sehr vorsichtig, wenn Sie die Adresse einer lokalen Variablen von einem CO zu einem anderen CO übergeben möchten, da sich die lokale Variable normalerweise auf dem Share -Stapel befindet. Die Zuweisung dieser Art von Variablen aus dem Haufen ist immer die klügere Wahl.
Im Detail gibt es 5 Tipps:
co_fp
/
/
f1 f2
/ /
/ f4
yield f3 f5
aco_yield , um an den Main CO zurückzugeben), hat einen großen Einfluss auf die Leistung des Kontextwechsels zwischen Coroutinen, wie bereits durch die Benchmark-Ergebnisse angezeigt. Im obigen Diagramm hat die Stapelverwendung der Funktion F2, F3, F4 und F5 keinen direkten Einfluss auf die Kontextschaltleistung, da es bei der Ausführung kein aco_yield gibt, während die Stapelverwendung von CO_FP und F1 den Wert der co->save_stack.max_cpsz dominiert und einen großen Einfluss über die Konzentration des Kontextes dominiert. Der Schlüssel, um die Stapelverwendung einer Funktion so niedrig wie möglich zu halten, besteht darin, die lokalen Variablen (insbesondere die großen) auf dem Haufen zuzuordnen und ihren Lebenszyklus manuell zu verwalten, anstatt sie standardmäßig auf dem Stapel zuzuweisen. Die Option -fstack-usage -Option von GCC ist dazu sehr hilfreich.
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 in CO_FP1 (Zeile 16) eine völlig andere Semantik mit dem gl_ptr in Zeile 7 von CO_FP0, und diese Art von Code würde wahrscheinlich den Ausführungsstapel von CO_FP1 beschädigen. Die Zeile 11 ist jedoch in Ordnung, da sich die variable ct und Funktion inc_p im selben Coroutine -Kontext befinden. Wenn Sie diese Art von Variablen (mit anderen Coroutinen teilen) auf dem Haufen zuzuweisen, lösen Sie einfach solche Probleme: 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 ();
}Neue Ideen sind willkommen!
Fügen Sie ein Makro wie aco_mem_new hinzu, was die Kombination aus so etwas wie p = malloc(sz); assertalloc_ptr(p) .
Fügen Sie eine neue API aco_reset hinzu, um die Wiederverwendbarkeit der Coroutine -Objekte zu unterstützen.
Unterstützen Sie andere Plattformen (insbesondere 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 ???
Ich bin ein Vollzeit-Open-Source-Entwickler. Jede Menge der Spenden wird sehr geschätzt und könnte mir große Ermutigung bringen.
Paypal
paypal.me link
Alipay (支付 (宝 | 寶))


Das Logo von Libaco wird großzügig von Peter Bech (Peteck) gespendet. Das Logo ist unter CC BY-ND 4.0 lizenziert. Die Website von libaco.org wird ebenfalls freundlicherweise von Peter Bech (Peteck) beigetragen.
Copyright (c) 2018, von sen han [email protected].
Unter der Apache -Lizenz, Version 2.0.
Weitere Informationen finden Sie in der Lizenzdatei.