Libaco - مكتبة Coroutine سريعة الخفيفة وخفيفة الوزن.
اسم رمز هذا المشروع هو Arkenstone؟
كوروتين وأركنستون غير المتماثل هو السبب وراء تسمية aco .
يدعم حاليًا SYS V ABI من Intel386 و X86-64.
فيما يلي ملخص موجز لهذا المشروع:
تعني عبارة " أسرع " في أعلاه تطبيق تبديل السياق الأسرع الذي يتوافق مع SYS V ABI من Intel386 أو AMD64.
القضايا و PRS موضع ترحيب ؟؟؟
ملاحظة: يرجى استخدام الإصدارات بدلاً من master لبناء الثنائي النهائي.
إلى جانب هذه القراءة ، يمكنك أيضًا زيارة الوثائق من 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 ، ويتم تخزين عنوان الذاكرة المخصصة من الكومة بشكل مباشر في المكدس بشكل مباشر أو غير مباشر ، وبالتالي يمكننا تبسيط العناصر الأربعة إلى 2 منها فقط: {cpu_registers, stack} .

نحدد main co بأنه Coroutine الذي يحتكر المكدس الافتراضي للخيط الحالي. ونظرًا لأن CO الرئيسي هو المستخدم الوحيد لهذا المكدس ، فنحن بحاجة فقط إلى حفظ/استعادة حالة سجلات وحدة المعالجة المركزية اللازمة لشركة CO الرئيسية عندما تم إنتاجها من/استئناف (تم تبديلها/التبديل).
بعد ذلك ، فإن تعريف non-main co هو Coroutine الذي يعد مكدس التنفيذ عبارة عن كومة ليست المكدس الافتراضي للخيط الحالي ويمكن مشاركتها مع شركة غير Main الأخرى. وبالتالي ، يجب أن يكون لدى CO غير Main مخزن مؤقت لذاكرة private save stack لحفظ/استعادة مكدس التنفيذ الخاص به عند التبديل/التبديل (لأن شركة Co الناجحة/السابقة قد تستخدم/استخدمت مكدس المشاركة كمكدس التنفيذ الخاص به).

هناك حالة خاصة من شركة غير Main ، وهي standalone non-main co لما أطلقنا عليه في Libaco: مكدس المشاركة من Coroutine غير المون لديه مستخدم واحد فقط. وبالتالي ، ليست هناك حاجة إلى توفير/استعادة الأشياء الخاصة بـ Save Save الخاصة بها عند التبديل/التبديل نظرًا لأنه لا يوجد أي شركة أخرى ستلمس كومة التنفيذ الخاصة بـ 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 of Valgrind لاختبار التطبيق ، فقد تحتاج إلى تحديد Global C Macro ACO_USE_VALGRIND لتمكين الدعم الودي لـ Valgrind في LIBACO. ولكن لا ينصح بتحديد هذا الماكرو في بناء الإصدار النهائي لسبب الأداء. قد تحتاج أيضًا إلى تثبيت رؤوس Valgrind (اسم الحزمة هو "Valgrind-Devel" في CentOS على سبيل المثال) لإنشاء تطبيق Libaco مع C Macro ACO_USE_VALGRIND المحددة. (يعمل Memcheck of Valgrind بشكل جيد فقط مع CO المستقلة حاليًا. في حالة المكدس المشترك الذي يستخدمه أكثر من شركة غير مين ، فإن Memcheck of 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. لا يوجد سوى شركة رئيسية واحدة وشركة مستقلة غير مستقلة في هذا البرنامج التعليمي. التعليقات في الكود المصدري مفيدة للغاية.
يوضح test_aco_tutorial_1.c استخدام بعض إحصائيات شركة غير Main. بنية بيانات aco_t واضحة للغاية ويتم تعريفها في aco.h
هناك شركة رئيسية واحدة ، وشركة واحدة غير مستقلة غير متين وشركان غير متين (يشيرون إلى نفس مكدس المشاركة) في 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 غير MAIN بدلاً من return النمط المباشر الافتراضي ، وإلا فإن Libaco سوف يتعامل مع السلوك على أنه غير قانوني وتشغيل الحامي الافتراضي الذي تتمثل مهمته في تسجيل معلومات الخطأ حول COR الجريمة إلى STDERR وتهدئة العملية على الفور. يُظهر 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 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 الجديدة و MXCSR (في aco_create ) وسيحافظ كل ثاني أكسيد الكربون على نسخته الخاصة بـ FPU و MXCSR.ACO_CONFIG_SHARE_FPU_MXCSR_ENV ، فإن جميع CO تشارك نفس كلمات التحكم في FPU و MXCSR. يمكنك إحالة جزء "الإنشاء والاختبار" من هذا المستند لمزيد من المعلومات حول هذا الموضوع. last_word_co_fp قيل في last_word_co_fp test_aco_tutorial_5.c في وظيفة الكلمة الأخيرة هذه ، يمكنك استخدام 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 في بايت وقد يكون له صفحة حراسة (للقراءة فقط) للكشف عن سعة مكدس التي يعتمد على الوسيطة الثانية guard_page_enabled .
لاستخدام قيمة الحجم الافتراضي (2 ميجابايت) إذا كانت الوسيطة الأولى sz تساوي 0. بعد بعض حساب المحاذاة والاحتياطي ، ستضمن هذه الوظيفة الطول النهائي الصحيح لمكدس الأسهم في المقابل:
final_valid_sz >= 4096final_valid_sz >= szfinal_valid_sz % page_size == 0 if the guard_page_enabled != 0 وقرب من قيمة sz قدر الإمكان.
عندما تكون قيمة الوسيطة الثانية guard_page_enabled هي 1 ، سيكون لمكدس المشاركة في المقابل صفحة حراسة واحدة فقط للكشف عن سعة مكدس بينما قيمة 0 من guard_page_enabled تعني بدون هذه صفحة الحراسة.
ستعود هذه الوظيفة دائمًا إلى مكدس مشاركة صالح.
void aco_share_stack_destroy ( aco_share_stack_t * sstk ); مصير حصة مكدس sstk .
تأكد من أن جميع CO التي تم تدمير مكدس Share 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_co هي CO الرئيسية التي سيقوم CO aco_yield في تبديل السياق المستقبلي. يجب ألا يكون main_co فارغًا ؛share_stack هي عنوان مكدس الأسهم الذي ستستخدمه شركة غير Main التي تريد إنشاؤها كمكدس تنفيذها في المستقبل. يجب ألا يكون share_stack فارغًا ؛save_stack_sz حجم init لمكدس حفظ الخاص من هذه شركة. الوحدة في بايت. قيمة 0 تعني استخدام الحجم الافتراضي 64 بايت. نظرًا لأن الحجم التلقائي سيحدث عندما لا تكون مكدس الحفظ الخاص بدرجة كافية للاحتفاظ بمجموعة تنفيذ من شركة 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 في مقابل "init" إذا كانت شركة غير ماء تريد إنشاؤها.
void aco_resume ( aco_t * co ); العائد من شركة المتصل الرئيسية وبدء أو متابعة تنفيذ co .
يجب أن يكون المتصل في هذه الوظيفة شركة رئيسية ويجب أن يكون co->main_co . ويجب أن تكون co الوسيطة الأولى شركة غير مان.
في المرة الأولى التي تستأنف فيها co ، تبدأ في تشغيل الوظيفة التي تشير إلى co->fp . إذا تم بالفعل الحصول على co ، aco_resume يعيد تشغيله ويواصل التنفيذ.
بعد نداء aco_resume ، نسمي حالة المتصل - MAIN CO كـ "محول".
void aco_yield (); العائد على تنفيذ co واستئناف co->main_co . يجب أن يكون المتصل في هذه الوظيفة شركة غير مان. و co->main_co يجب ألا يكون فارغًا.
بعد نداء aco_yield ، نسمي حالة المتصل - co على أنها "محوّلة".
aco_t * aco_get_co ();إرجاع مؤشر شركة غير مان الحالية. يجب أن يكون المتصل في هذه الوظيفة شركة غير مان.
void * aco_get_arg (); يساوي (aco_get_co()->arg) . وأيضًا ، يجب أن يكون المتصل في هذه الوظيفة شركة غير مان.
void aco_exit (); بالإضافة إلى ذلك ، قم بنفس الشيء مثل aco_yield() ، aco_exit() أيضًا تعيين co->is_end إلى 1 وبالتالي لتمييز co في حالة "النهاية".
void aco_destroy ( aco_t * co ); تدمير co . يجب ألا تكون حجة co خالية. سيتم أيضًا تدمير المكدس الخاص إذا كان co هو شركة غير مان.
#define ACO_VERSION_MAJOR 1
#define ACO_VERSION_MINOR 2
#define ACO_VERSION_PATCH 4 يتم تعريف هذه الماكرو الثلاثة في الرأس 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" لتجاوز "التأكيد" الافتراضي "في تطبيق Libaco مثل Test_aco_synopsis.c (يجب أن يكون هذا الرأس بما في ذلك في قائمة التوجيهات الأخيرة في الملف المصدر لأن C" Assert "هو تعريف ماكرو C أيضًا) وتحديد 5 وحشية أخرى في المائة أعلاه. يرجى عدم تضمين هذا الرأس في ملف مصدر التطبيق إذا كنت ترغب في استخدام "تأكيد" الافتراضي.
لمزيد من التفاصيل ، يمكنك الرجوع إلى الملف المصدر 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 غير MAIN وشركاه رئيسي واحد. هذا جيد لأن كوروتين غير متماثل هو مجرد حالة خاصة من coroutine المتماثل. لإثبات صحة coroutine المتماثلة أكثر تحديا من الكوروتين غير المتماثل وبالتالي أكثر متعة سيصبح. (قام Libaco بتنفيذ واجهة برمجة تطبيقات Coroutine غير المتماثلة حاليًا لأن المعنى الدلالي لمواجهة واجهة برمجة تطبيقات Coroutine غير المتماثلة هو أكثر سهولة فهمه واستخدامه من Coroutine المتماثل.)

نظرًا لأن CO الرئيسي هو بدء التشغيل الأول من Coroutine ، يجب أن يكون التبديل الأول للسياق في مؤشر ترابط OS في شكل acosw(main_co, co) حيث 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.)
دليل:

الرسم البياني أعلاه هو للقضية الأولى: "تم العائد على شركة 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 يجب أن تكون فارغة بالفعل ويجب أن تكون 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 راضٍ أيضًا.

الرسم البياني أعلاه هو للقضية الثانية: تم العائد على شركة State Co ->
القيود: 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 راضٍ لأن هناك توفير واستعادة السجلات المحفوظة عند استدعاء/إرجاع acosw . نظرًا لأننا افترضنا بالفعل أن لا أحد سيغير كلمات التحكم في FPU و MXCSR ، فإن C 2.2 راضٍ أيضًا.
يجب أن تكون acosw الأولى في الخيط هي الحالة الأولى: تم العائد على CO -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 إطار المكدس
فيما يلي مثال على خطأ في Tencent's Libco. ينص ABI على أن (E|R)SP يجب أن يشير دائمًا إلى نهاية إطار المكدس المخصص. ولكن في ملف coctx_swap.s من libco ، تم استخدام (E|R)SP لمعالجة الذاكرة على الكومة.
بشكل افتراضي ، يتم استدعاء معالج الإشارة على مكدس العملية العادية. من الممكن ترتيب أن معالج الإشارة يستخدم مكدسًا بديلًا ؛ انظر Sigalstack (2) للاطلاع على مناقشة حول كيفية القيام بذلك ومتى قد يكون مفيدًا.
- MAN 7 إشارة: تصرفات الإشارة
قد تحدث الأشياء الرهيبة إذا كان (E|R)SP يشير إلى بنية البيانات على الكومة عندما تأتي الإشارة. (باستخدام أوامر breakpoint signal في GDB يمكن أن ينتج مثل هذا الخطأ بسهولة. على الرغم من استخدام sigalstack لتغيير مكدس الإشارة الافتراضي يمكن أن يخفف من المشكلة ، ولكن هذا النوع من استخدام (E|R)SP لا يزال ينتهك ABI.)
باختصار ، إذا كنت ترغب في الحصول على الأداء المتطرف لـ Libaco ، فما عليك سوى الحفاظ على استخدام المكدس لشركة Non-Main Non-Main في نقطة استدعاء aco_yield صغيرة قدر الإمكان. وكن حذرًا جدًا إذا كنت ترغب في تمرير عنوان متغير محلي من شركة إلى شركة أخرى لأن المتغير المحلي عادة ما يكون على مكدس المشاركة . إن تخصيص هذا النوع من المتغيرات من الكومة هو دائمًا الخيار الأكثر حكمة.
بالتفصيل ، هناك 5 نصائح:
co_fp
/
/
f1 f2
/ /
/ f4
yield f3 f5
aco_yield للإعداد إلى Main Co) له تأثير كبير على أداء تبديل السياق بين coroutines ، كما هو موضح بالفعل في نتائج القياس. في الرسم البياني أعلاه ، لا يكون لاستخدام المكدس للوظيفة F2 و F3 و F4 و F5 أي تأثير مباشر على أداء تبديل السياق نظرًا لعدم وجود aco_yield عند تنفيذها ، في حين أن استخدام المكدس لـ CO_FP و F1 يهيمن على قيمة co->save_stack.max_cpsz وله تأثير كبير على أداء التحميل. إن مفتاح الحفاظ على استخدام المكدس لوظيفة منخفضة قدر الإمكان هو تخصيص المتغيرات المحلية (وخاصة المواقع الكبيرة) على الكومة وإدارة دورة حياتهم يدويًا بدلاً من تخصيصها على المكدس افتراضيًا. خيار -fstack-usage of 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 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 بسخاء من قبل بيتر بيش (Peteck). الشعار مرخص تحت CC BY-ND 4.0. كما أن موقع libaco.org قد ساهم أيضًا من قبل بيتر بيش (بيتيك).
حقوق الطبع والنشر (C) 2018 ، بقلم Sen Han [email protected].
تحت رخصة Apache ، الإصدار 2.0.
انظر ملف الترخيص للحصول على التفاصيل.