Libaco - ห้องสมุด Coroutine แบบไม่สมมาตรและน้ำหนักเบา
ชื่อรหัสของโครงการนี้คือ Arkenstone?
Asymmetric Coroutine & Arkenstone เป็นเหตุผลว่าทำไมจึงได้รับการตั้งชื่อว่า aco
ปัจจุบันรองรับ SYS V ABI ของ Intel386 และ x86-64
นี่คือสรุปสั้น ๆ ของโครงการนี้:
วลี " เร็วที่สุด " ในด้านบนหมายถึงการใช้การสลับบริบทที่เร็วที่สุดซึ่งเป็นไปตาม SYS v ABI ของ Intel386 หรือ AMD64
ยินดีต้อนรับปัญหาและ PRS ???
หมายเหตุ: โปรดใช้รุ่นแทน master เพื่อสร้างไบนารีสุดท้าย
นอกจาก readme นี้คุณยังสามารถเยี่ยมชมเอกสารจาก https://libaco.org/docs โปรดติดตาม 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 register และที่อยู่ของหน่วยความจำที่จัดสรรจากกองมักจะถูกเก็บไว้ในสแต็กโดยตรงหรือโดยอ้อมดังนั้นเราจึงสามารถทำให้องค์ประกอบ 4 อย่างง่ายขึ้นเป็นเพียง 2 ของพวกเขา: {cpu_registers, stack}

เรากำหนด main co เป็น coroutine ที่ผูกขาดสแต็กเริ่มต้นของเธรดปัจจุบัน และเนื่องจาก CO หลักเป็นผู้ใช้เพียงคนเดียวของสแต็กนี้เราจึงต้องบันทึก/กู้คืนสถานะของ CPU ที่จำเป็นของ CPU ของ CO หลักเมื่อได้รับจากการกลับมาใช้ใหม่/กลับมาใช้ใหม่
ถัดไปคำจำกัดความของ non-main co คือ coroutine ที่มีการดำเนินการสแต็กเป็นสแต็กซึ่งไม่ใช่สแต็กเริ่มต้นของเธรดปัจจุบันและอาจแชร์กับ CO ที่ไม่ใช่หลักอื่น ๆ ดังนั้น CO ที่ไม่ได้มีการหลักจะต้องมีบัฟเฟอร์หน่วยความจำ private save stack เพื่อบันทึก/กู้คืนสแต็กการดำเนินการเมื่อมันถูกสลับออก/สลับเข้า (เพราะ CO ที่ประสบความสำเร็จ/ก่อนหน้านี้อาจ/ใช้/ใช้สแต็กแชร์เป็นสแต็กการดำเนินการ)

มีกรณีพิเศษของ CO ที่ไม่ได้อยู่ในแนวหลักนั่นคือ standalone non-main co สิ่งที่เราเรียกใน Libaco: สแต็กหุ้นของ coroutine ที่ไม่ได้หลักมีผู้ใช้ CO เพียงคนเดียว ดังนั้นจึงไม่จำเป็นต้องทำการออม/กู้คืนสิ่งของที่บันทึกส่วนตัวสแต็กเมื่อมันถูกสลับออก/สลับเข้าด้วยกันเนื่องจากไม่มี CO อื่น ๆ ที่จะสัมผัสสแต็กการดำเนินการของ CO ที่ไม่ได้อยู่ในระดับสแตนด์อโลนยกเว้นตัวเอง

ในที่สุดเราได้ภาพรวมของ Libaco
มีส่วน "พิสูจน์ความถูกต้อง" ที่คุณอาจพบว่ามีประโยชน์จริง ๆ หากคุณต้องการดำน้ำใน Libaco ภายในหรือต้องการใช้ห้องสมุด coroutine ของคุณเอง
ขอแนะนำอย่างยิ่งให้อ่านซอร์สโค้ดของบทช่วยสอนและเกณฑ์มาตรฐานถัดไป ผลลัพธ์มาตรฐานก็น่าประทับใจและให้ความกระจ่างเช่นกัน
-m32 ตัวเลือก -m32 ของ GCC สามารถช่วยคุณสร้างแอปพลิเคชัน I386 ของ Libaco บนเครื่อง X86_64
ACO_CONFIG_SHARE_FPU_MXCSR_ENV คุณสามารถกำหนด Global C Macro ACO_CONFIG_SHARE_FPU_MXCSR_ENV เพื่อเพิ่มประสิทธิภาพการทำงานของการสลับบริบทระหว่าง coroutines เล็กน้อยหากไม่มีรหัสใดที่จะเปลี่ยนคำควบคุมของ FPU และ MXCSR หากแมโครไม่ได้กำหนด CO ทั้งหมดจะเก็บสำเนาคำควบคุม FPU และ MXCSR ของตัวเอง ขอแนะนำให้กำหนดมาโครนี้ทั่วโลกเสมอเพราะเป็นเรื่องยากมากที่ฟังก์ชั่นหนึ่งต้องตั้งค่า Env พิเศษของ FPU หรือ MXCSR แทนที่จะใช้ Env เริ่มต้นที่กำหนดโดย ISO C แต่คุณอาจไม่จำเป็นต้องกำหนดแมโครนี้หากคุณไม่แน่ใจ
ACO_USE_VALGRIND หากคุณต้องการใช้ Memcheck เครื่องมือของ Valgrind เพื่อทดสอบแอปพลิเคชันคุณอาจต้องกำหนด M Macro ACO_USE_VALGRIND ทั่วโลกเพื่อเปิดใช้งานการสนับสนุนที่เป็นมิตรของ Valgrind ใน Libaco แต่ไม่แนะนำให้กำหนดแมโครนี้ในการสร้างรุ่นสุดท้ายเพื่อเหตุผลด้านประสิทธิภาพ คุณอาจต้องติดตั้งส่วนหัวของ Valgrind (ชื่อแพ็คเกจคือ "Valgrind-Devel" ใน Centos) เพื่อสร้างแอปพลิเคชัน Libaco ด้วย C Macro ACO_USE_VALGRIND ที่กำหนดไว้ (Memcheck of Valgrind ทำงานได้ดีกับ CO แบบสแตนด์อโลนในขณะนี้ในกรณีของสแต็กที่ใช้ร่วมกันที่ใช้โดย CO ที่ไม่ใช่หลักมากกว่าหนึ่งตัว Memcheck ของ Valgrind จะสร้างรายงานเชิงบวกที่ผิดพลาดจำนวนมากสำหรับข้อมูลเพิ่มเติมที่คุณอาจอ้างถึง "Test_ACO_TUTORIAL_6.C"
ACO_USE_ASAN Global C Macro ACO_USE_ASAN จะเปิดใช้งานการสนับสนุนที่เป็นมิตรของผู้ฆ่าเชื้อที่อยู่ใน LIBACO (สนับสนุนทั้ง GCC และ Clang)
เพื่อสร้างชุดทดสอบของ Libaco:
$ mkdir output
$ bash make.shนอกจากนี้ยังมีตัวเลือกโดยละเอียดบางอย่างใน 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 กล่าวโดยย่อคือการใช้ -o no-valgrind หากคุณไม่มีการติดตั้งส่วนหัวของ Valgrind, -o no-m32 หากคุณไม่มีเครื่องมือพัฒนา GCC 32 บิตติดตั้งบนโฮสต์ AMD64
บน MacOS คุณต้องแทนที่คำสั่ง sed และ grep เริ่มต้นของ macOS ด้วย gnu 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 แสดงการใช้สถิติบางส่วนของ CO ที่ไม่ได้อยู่หลัก โครงสร้างข้อมูลของ aco_t นั้นชัดเจนมากและกำหนดไว้ใน aco.h
มีหนึ่ง CO หลักหนึ่งตัวที่ไม่ได้เป็นหลักหนึ่ง CO และ CO ที่ไม่ใช่ MAIN สองตัว (ชี้ไปที่สแต็กแชร์เดียวกัน) ใน test_aco_tutorial_2.c
test_aco_tutorial_3.c แสดงวิธีการใช้ libaco ในกระบวนการมัลติเธรด โดยทั่วไปหนึ่งอินสแตนซ์ของ Libaco ได้รับการออกแบบมาเพื่อทำงานภายในเธรดหนึ่งที่แน่นอนเพื่อให้ได้ประสิทธิภาพสูงสุดของการสลับบริบทระหว่าง coroutines หากคุณต้องการใช้ libaco ในสภาพแวดล้อมแบบมัลติเธรดเพียงเพื่อสร้างหนึ่งอินสแตนซ์ของ libaco ในแต่ละเธรด ไม่มีการแบ่งปันข้อมูลข้ามเธรดภายใน Libaco และคุณต้องจัดการกับการแข่งขันข้อมูลระหว่างหลายเธรดด้วยตัวคุณเอง (เช่นสิ่งที่ gl_race_aco_yield_ct ทำในบทช่วยสอนนี้)
หนึ่งในกฎใน LIBACO คือการเรียก aco_exit() เพื่อยุติการดำเนินการของ CO ที่ไม่ได้อยู่ในระดับสูงแทน return รูปแบบ C โดยตรงเริ่มต้นมิฉะนั้น Libaco จะปฏิบัติต่อพฤติกรรมเช่นนั้นผิดกฎหมาย test_aco_tutorial_4.c แสดงสถานการณ์ "Co" ที่กระทำผิด
คุณสามารถกำหนดตัวป้องกันของคุณเองเพื่อแทนที่คำเริ่มต้น (เพื่อทำสิ่งสุดท้าย "คำสุดท้าย" ที่กำหนดเอง) แต่ไม่ว่าในกรณีใดกระบวนการจะถูกยกเลิกหลังจากดำเนินการป้องกัน test_aco_tutorial_5.c แสดงวิธีกำหนดฟังก์ชันคำสุดท้ายที่กำหนดเอง
ตัวอย่างสุดท้ายคือตัวกำหนดตารางเวลา coroutine อย่างง่ายใน test_aco_tutorial_6.c
การอ่านการใช้งาน API ที่เกี่ยวข้องในซอร์สโค้ดพร้อมกันเมื่อคุณอ่านคำอธิบาย API ของ Libaco ต่อไปนี้เนื่องจากซอร์สโค้ดค่อนข้างชัดเจนและเข้าใจง่าย และขอแนะนำให้อ่านบทเรียนทั้งหมดก่อนที่จะอ่านเอกสาร API
ขอแนะนำอย่างยิ่งให้อ่านส่วนแนวปฏิบัติที่ดีที่สุดก่อนที่จะเริ่มเขียนแอปพลิเคชันที่แท้จริงของ Libaco (นอกเหนือจากการอธิบายวิธีการปล่อยประสิทธิภาพสุดขั้วของ Libaco อย่างแท้จริงในแอปพลิเคชันของคุณแล้วยังมีการแจ้งให้ทราบล่วงหน้าเกี่ยวกับการเขียนโปรแกรมของ Libaco)
หมายเหตุ: การควบคุมเวอร์ชันของ Libaco เป็นไปตามข้อมูลจำเพาะ: ความหมายเวอร์ชัน 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 ไม่ได้กำหนดคำควบคุมที่บันทึกไว้จะถูกใช้เป็นค่าอ้างอิงเพื่อตั้งค่าคำควบคุมของ FPU และ MXCSR ของ CO ใหม่ (ใน aco_create )ACO_CONFIG_SHARE_FPU_MXCSR_ENV ถูกกำหนดไว้แล้ว CO ทั้งหมดจะแบ่งปันคำควบคุมเดียวกันของ FPU และ MXCSR คุณสามารถอ้างอิงส่วน "build and test" ของเอกสารนี้สำหรับข้อมูลเพิ่มเติมเกี่ยวกับเรื่องนี้ และอย่างที่กล่าวไว้ในส่วน test_aco_tutorial_5.c ของส่วน "บทช่วยสอน" เมื่ออาร์กิวเมนต์ที่ 1 last_word_co_fp ไม่ได้เป็นโมฆะจากนั้นฟังก์ชั่นที่ชี้ไปที่ last_word_co_fp จะแทนที่ตัวป้องกันเริ่มต้นเพื่อทำ "คำสุดท้าย" ในฟังก์ชั่นคำสุดท้ายคุณสามารถใช้ aco_get_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) หากอาร์กิวเมนต์ที่ 1 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 สแต็กแชร์กลับจะมีหน้าป้องกันแบบอ่านอย่างเดียวหนึ่งหน้าสำหรับการตรวจจับสแต็กล้นในขณะที่ค่า 0 ของ guard_page_enabled โดยไม่มีหน้าป้องกันดังกล่าว
ฟังก์ชั่นนี้จะส่งคืนสแต็คแชร์ที่ถูกต้องเสมอ
void aco_share_stack_destroy ( aco_share_stack_t * sstk ); Destory The Share Stack sstk
ตรวจสอบให้แน่ใจว่า CO ทั้งหมดที่มีสแต็คแชร์คือ sstk ถูกทำลายไปแล้วเมื่อคุณทำลาย sstk
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 อื่น ๆ ทั้งหมดจะทำ
มิฉะนั้นจะเป็น บริษัท ที่ไม่ได้อยู่ในระดับสูงที่คุณต้องการสร้าง:
main_co เป็นหลักหลักที่ CO จะ aco_yield เป็นในการสลับบริบทในอนาคต main_co ต้องไม่เป็นโมฆะshare_stack เป็นที่อยู่ของสแต็คหุ้นซึ่ง บริษัท ที่ไม่ได้หลักที่คุณต้องการสร้างจะใช้เป็นสแต็กการดำเนินการในอนาคต share_stack ต้องไม่เป็นโมฆะsave_stack_sz ระบุขนาดเริ่มต้นของสแต็กบันทึกส่วนตัวของ บริษัท นี้ หน่วยอยู่ในไบต์ ค่า 0 หมายถึงการใช้ขนาดเริ่มต้น 64 ไบต์ เนื่องจากการปรับขนาดโดยอัตโนมัติจะเกิดขึ้นเมื่อสแต็กบันทึกส่วนตัวไม่ใหญ่พอที่จะถือสแต็คการดำเนินการของ CO เมื่อต้องให้หุ้นสแต็กที่อยู่ใน CO อื่นคุณมักจะไม่ต้องกังวลเกี่ยวกับมูลค่าของ sz เลย แต่มันจะนำผลกระทบด้านประสิทธิภาพบางอย่างมาสู่การจัดสรรหน่วยความจำเมื่อจำนวนมาก (พูด 10,000,000) ของ CO ปรับขนาดสแต็ก co->save_stack.max_cpsz ส่วนตัวของพวกเขาอย่างต่อเนื่องดังนั้นจึงฉลาดและแนะนำอย่างยิ่งให้ตั้งค่า save_stack_sz ด้วยค่าเท่ากับค่าสูงสุดco_fp เป็นตัวชี้ฟังก์ชั่นรายการของ CO co_fp จะต้องไม่เป็นโมฆะarg เป็นค่าตัวชี้และจะตั้งค่าเป็น co->arg ของ CO เพื่อสร้าง มันสามารถใช้เป็นอาร์กิวเมนต์อินพุตสำหรับ COฟังก์ชั่นนี้จะส่งคืน CO ที่ถูกต้องเสมอ และเราตั้งชื่อสถานะของ CO ในทางกลับกันว่า "init" ถ้าเป็น CO ที่ไม่ได้หลักคุณต้องการสร้าง
void aco_resume ( aco_t * co ); ผลผลิตจากผู้โทรหลักผู้โทรและเพื่อเริ่มต้นหรือดำเนินการดำเนินการของ co
ผู้โทรของฟังก์ชั่นนี้จะต้องเป็น CO หลักและต้องเป็น co->main_co และ co อาร์กิวเมนต์ที่ 1 จะต้องเป็น บริษัท ที่ไม่ได้อยู่ด้วย
ครั้งแรกที่คุณกลับมาทำงาน co มันจะเริ่มใช้ฟังก์ชั่นที่ชี้ด้วย co->fp หาก co ได้รับแล้ว aco_resume จะรีสตาร์ทและดำเนินการต่อ
หลังจากการเรียกของ aco_resume เราตั้งชื่อสถานะของผู้โทร - CO หลักในฐานะ "ให้ผล"
void aco_yield (); ให้ผลการดำเนินการของ co และ Resume co->main_co ผู้โทรของฟังก์ชั่นนี้จะต้องเป็น CO ที่ไม่ได้อยู่ในระดับสูง และ co->main_co ต้องไม่เป็นโมฆะ
หลังจากการเรียกของ 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() ยังตั้งค่า co->is_end เป็น 1 ดังนั้นเพื่อทำเครื่องหมาย co ที่สถานะของ "end"
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" เพื่อแทนที่ C เริ่มต้น "ยืนยัน" ในแอปพลิเคชัน LIBACO เช่น TEST_ACO_SYNOPSIS.C ทำ (ส่วนหัวนี้รวมถึงควรเป็นรายการสุดท้ายที่รวมอยู่ในไฟล์ต้นฉบับ โปรดอย่ารวมส่วนหัวนี้ไว้ในไฟล์แหล่งที่มาของแอปพลิเคชันหากคุณต้องการใช้ C "ยืนยัน" เริ่มต้น
สำหรับรายละเอียดเพิ่มเติมคุณอาจอ้างถึงไฟล์ต้นฉบับ ACO_ASSERT_OVERRIDE.H
วันที่: วันเสาร์ที่ 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
จำเป็นอย่างยิ่งที่จะต้องคุ้นเคยกับมาตรฐานของ SYS v ABI ของ Intel386 และ x86-64 ก่อนที่คุณจะเริ่มใช้หรือพิสูจน์ห้องสมุด coroutine
หลักฐานด้านล่างไม่มีคำอธิบายโดยตรงเกี่ยวกับ IP (ตัวชี้คำสั่ง), SP (ตัวชี้สแต็ก) และการออม/การกู้คืนระหว่างสแต็กบันทึกส่วนตัวและสแต็กแชร์เนื่องจากสิ่งเหล่านี้ค่อนข้างน่าสนใจและง่ายต่อการเข้าใจเมื่อเปรียบเทียบกับข้อ จำกัด ABI
ในเธรด OS, coroutine main_co หลักคือ coroutine ที่ควรสร้างและเริ่มดำเนินการก่อนก่อนที่ coroutines ที่ไม่ใช่หลักอื่น ๆ ทั้งหมดจะทำ
แผนภาพถัดไปเป็นตัวอย่างง่ายๆของการสลับบริบทระหว่าง Main_co และ CO
ในการพิสูจน์นี้เราเพิ่งคิดว่าเราอยู่ภายใต้ SYS v ABI ของ Intel386 เนื่องจากไม่มีความแตกต่างพื้นฐานระหว่าง SYS v ABI ของ Intel386 และ x86-64 นอกจากนี้เรายังสมมติว่าไม่มีรหัสใดที่จะเปลี่ยนคำควบคุมของ FPU และ MXCSR

แผนภาพถัดไปเป็นแบบจำลองการทำงานของ Coroutine แบบสมมาตรซึ่งมี CO-S ที่ไม่ จำกัด จำนวนไม่ จำกัด และหนึ่ง CO หลัก นี่เป็นสิ่งที่ดีเพราะ coroutine แบบไม่สมมาตรเป็นเพียงกรณีพิเศษของ coroutine สมมาตร เพื่อพิสูจน์ความถูกต้องของ coroutine แบบสมมาตรนั้นมีความท้าทายมากกว่าเล็กน้อยของ coroutine ที่ไม่สมมาตรและทำให้สนุกยิ่งขึ้น (Libaco ใช้ API ของ coroutine แบบอสมมาตรในปัจจุบันเนื่องจากความหมายเชิงความหมายของ Coroutine API แบบอสมมาตรนั้นง่ายต่อการเข้าใจและใช้งานมากกว่า coroutine แบบสมมาตร)

เนื่องจาก CO หลักคือ coroutine ที่ 1 เริ่มทำงานการสลับบริบทที่ 1 ในเธรดระบบปฏิบัติการนี้จะต้องอยู่ในรูปแบบของ acosw(main_co, co) ที่ co อาร์กิวเมนต์ที่ 2 เป็น CO ที่ไม่ได้หลัก
เป็นเรื่องง่ายที่จะพิสูจน์ว่ามีเพียงสองประเภทของการถ่ายโอนสถานะในแผนภาพด้านบน:
เพื่อพิสูจน์ความถูกต้องของ void* acosw(aco_t* from_co, aco_t* to_co) การใช้งานนั้นเทียบเท่ากันเพื่อพิสูจน์ CO ทั้งหมดที่ปฏิบัติตามข้อ จำกัด ของ SYS V ABI ก่อนและหลังการเรียกของ acosw เราคิดว่าอีกส่วนหนึ่งของรหัสไบนารี (ยกเว้น acosw ) ใน CO ได้ปฏิบัติตาม 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 การใช้การลงทะเบียนถูกกำหนดไว้ใน "P13 - ตารางที่ 2.3: การลงทะเบียนการใช้งาน" ของ Sys v ABI Intel386 v1.1 และสำหรับ AMD64 อยู่ใน "P23 - รูปที่ 3.4: การใช้งานลงทะเบียน" ของ SYS v ABI AMD64 V1.0)
การพิสูจน์:

แผนภาพด้านบนสำหรับกรณีที่ 1: "รัฐให้ผลรัฐ -> เริ่มต้นสถานะ 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 จะต้องว่างเปล่าแล้วและ DF จะต้องเป็น 0 ก่อนที่ acosw(co, to_co) ถูกเรียก (รหัสไบนารีของ 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 ( พอใจ ✓)
EAX ถือค่าคืนสินค้าแล้วเมื่อ acosw กลับไปที่ TO_CO (ประวัติย่อ)
ข้อ จำกัด : 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 จะต้องว่างเปล่าแล้วและ DF จะต้องเป็น 0 ก่อนที่ acosw(co, to_co) ถูกเรียก (รหัสไบนารีของ CO ได้ปฏิบัติตาม ABI แล้ว) ข้อ จำกัด 1.3 และ 1.4 เป็นไปตาม acosw
ข้อ จำกัด : C 2.0, 2.1, 2.2 ( พอใจ ✓)
C 2.0 & 2.1 เป็นที่พอใจเนื่องจากมีการออมและการกู้คืนของการลงทะเบียน Callee ที่บันทึกไว้เมื่อ acosw ถูกเรียก/ส่งคืน เนื่องจากเราสันนิษฐานว่าไม่มีใครจะเปลี่ยนคำควบคุมของ FPU และ MXCSR ทำให้ C 2.2 พอใจเช่นกัน
acosw ที่ 1 ในเธรดจะต้องเป็นกรณีที่ 1: ให้รัฐ Co -> init State Co และ acosw ถัดไปทั้งหมดจะต้องเป็นหนึ่งใน 2 กรณีด้านบน ตามลำดับเราสามารถพิสูจน์ได้ว่า "CO ทั้งหมดปฏิบัติตามข้อ จำกัด ของ SYS V ABI ก่อนและหลังการเรียกของ acosw " ดังนั้นหลักฐานเสร็จสิ้น
มีสิ่งใหม่ที่เรียกว่า Red Zone ใน System V ABI X86-64:
พื้นที่ 128 ไบต์ที่อยู่นอกเหนือสถานที่ที่ชี้ไปที่ %RSP ถือว่าถูกสงวนไว้และจะไม่ถูกแก้ไขโดยสัญญาณหรือตัวจัดการขัดจังหวะ ดังนั้นฟังก์ชั่นอาจใช้พื้นที่นี้สำหรับข้อมูลชั่วคราวที่ไม่จำเป็นในการเรียกใช้ฟังก์ชัน โดยเฉพาะอย่างยิ่งฟังก์ชั่นใบไม้อาจใช้พื้นที่นี้สำหรับเฟรมสแต็กทั้งหมดของพวกเขาแทนที่จะปรับตัวชี้สแต็กในอารัมภบทและบทส่งท้าย พื้นที่นี้เรียกว่าโซนสีแดง
เนื่องจากโซนสีแดงคือ "ไม่ได้รับการเก็บรักษาโดย 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 กรอบสแต็ก
นี่คือตัวอย่างข้อผิดพลาดใน Libco ของ Tencent ABI ระบุว่า (E|R)SP ควรชี้ไปที่จุดสิ้นสุดของกรอบสแต็กที่จัดสรรล่าสุด แต่ในไฟล์ coctx_swap.s ของ libco, (E|R)SP ถูกใช้เพื่อจัดการกับหน่วยความจำบนกอง
โดยค่าเริ่มต้นตัวจัดการสัญญาณจะถูกเรียกใช้ในสแต็กกระบวนการปกติ เป็นไปได้ที่จะจัดว่าตัวจัดการสัญญาณใช้สแต็กสำรอง ดู Sigalstack (2) สำหรับการอภิปรายเกี่ยวกับวิธีการทำสิ่งนี้และเมื่อใดที่มันอาจจะเป็นประโยชน์
- Man 7 Signal: การส่งสัญญาณ
สิ่งที่น่ากลัวอาจเกิดขึ้นได้หาก (E|R)SP ชี้ไปที่โครงสร้างข้อมูลบนกองเมื่อสัญญาณมาถึง (การใช้คำสั่ง breakpoint และ signal ของ GDB สามารถสร้างข้อผิดพลาดดังกล่าวได้อย่างสะดวกแม้ว่าการใช้ sigalstack เพื่อเปลี่ยนสแต็กสัญญาณเริ่มต้นสามารถบรรเทาปัญหาได้ แต่ก็ยังการใช้งาน (E|R)SP ยังคงละเมิด ABI)
โดยสรุปหากคุณต้องการได้รับประสิทธิภาพพิเศษของ Libaco เพียงแค่ให้การใช้งานสแต็กของ CO ที่ไม่ใช่มาตรฐานที่ไม่ใช่มาตรฐาน ณ จุดที่เรียก aco_yield ให้เล็กที่สุดเท่าที่จะทำได้ และระวังให้มากหากคุณต้องการส่งที่อยู่ของตัวแปรท้องถิ่นจาก CO หนึ่งไปยัง CO อื่นเนื่องจากตัวแปรท้องถิ่นมักจะอยู่ในสแต็ก หุ้น การจัดสรรตัวแปรประเภทนี้จากกองเป็นตัวเลือกที่ฉลาดเสมอ
โดยละเอียดมี 5 เคล็ดลับ:
co_fp
/
/
f1 f2
/ /
/ f4
yield f3 f5
aco_yield เพื่อให้ได้กลับไปยัง CO หลัก) มีผลกระทบอย่างมากต่อประสิทธิภาพของการสลับบริบทระหว่าง coroutines ตามที่ระบุไว้โดยมาตรฐาน ในแผนภาพด้านบนการใช้สแต็กของฟังก์ชั่น F2, F3, F4 และ F5 ไม่มีอิทธิพลโดยตรงต่อประสิทธิภาพการสลับบริบทเนื่องจากไม่มี aco_yield เมื่อพวกเขากำลังดำเนินการในขณะที่การใช้งานสแต็กของ co->save_stack.max_cpsz และ F1 นั้นมีมูลค่ามากกว่า กุญแจสำคัญในการรักษาสแต็กของฟังก์ชั่นให้ต่ำที่สุดเท่าที่จะเป็นไปได้คือการจัดสรรตัวแปรท้องถิ่น (โดยเฉพาะอย่างยิ่งตัวใหญ่) บนกองและจัดการวงจรชีวิตของพวกเขาด้วยตนเองแทนที่จะจัดสรรให้กับสแต็กโดยค่าเริ่มต้น ตัวเลือก -fstack-usage ของ GCC นั้นมีประโยชน์มากเกี่ยวกับเรื่องนี้
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_FP1 (บรรทัดที่ 16) มีความหมายที่แตกต่างกันโดยสิ้นเชิงกับ gl_ptr ในบรรทัดที่ 7 ของ CO_FP0 และรหัสประเภทนั้นอาจเสียหายสแต็กการดำเนินการของ CO_FP1 แต่บรรทัดที่ 11 นั้นใช้ได้เพราะตัวแปร ct และฟังก์ชั่น inc_p อยู่ในบริบท coroutine เดียวกัน การจัดสรรตัวแปรชนิดนั้น (จำเป็นต้องแบ่งปันกับ coroutines อื่น ๆ ) บนกองจะช่วยแก้ปัญหาดังกล่าว: 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
ลิงค์ paypal.me
Alipay (支付 (宝 | 寶))


โลโก้ของ Libaco ได้รับการบริจาคอย่างไม่เห็นแก่ตัวโดย Peter Bech (Peteck) โลโก้ได้รับใบอนุญาตภายใต้ CC BY-ND 4.0 เว็บไซต์ของ libaco.org ยังได้รับการสนับสนุนจาก Peter Bech (Peteck)
ลิขสิทธิ์ (c) 2018 โดย Sen Han [email protected]
ภายใต้ใบอนุญาต Apache เวอร์ชัน 2.0
ดูไฟล์ใบอนุญาตสำหรับรายละเอียด