Singkatnya:
- Buktinya sudah diimplementasikan dalam C ++ , JS dan PHP , cocok untuk Java .
- Lebih cepat dari coroutine dan Promise, lebih banyak fitur.
- Itu tidak memerlukan tumpukan perangkat lunak terpisah.
- Berteman dengan semua alat keamanan dan debugging.
- Ia bekerja pada arsitektur apa pun dan tidak memerlukan flag kompiler khusus.
Lihatlah ke belakang
Pada awal komputer, ada aliran kontrol tunggal dengan memblokir input-output. Kemudian interupsi besi ditambahkan ke dalamnya. Sekarang Anda dapat secara efektif menggunakan perangkat yang lambat dan tidak dapat diprediksi.
Dengan pertumbuhan kemampuan besi dan ketersediaannya yang rendah, menjadi perlu untuk melakukan beberapa tugas secara bersamaan, yang memberikan dukungan perangkat keras. Jadi ada proses terisolasi dengan interupsi yang disarikan dari besi dalam bentuk sinyal.
Tahap evolusi berikutnya adalah multithreading, yang diimplementasikan atas dasar proses yang sama, tetapi dengan akses bersama ke memori dan sumber daya lainnya. Pendekatan ini memiliki keterbatasan dan overhead yang signifikan untuk beralih ke OS yang aman.
Untuk komunikasi antara proses dan bahkan mesin yang berbeda, abstraksi Janji / Masa Depan diusulkan 40+ tahun yang lalu.
Antarmuka pengguna dan masalah klien 10K yang saat ini menggelikan telah menyebabkan masa kejayaan pendekatan Event Loop, Reactor, dan Proactor, yang lebih berorientasi pada peristiwa daripada logika bisnis yang jelas dan konsisten.
Akhirnya, kami sampai pada coroutine modern (coroutine), yang pada dasarnya adalah emulasi aliran di atas abstraksi yang dijelaskan di atas dengan keterbatasan teknis yang sesuai dan transfer kontrol deterministik.
Untuk menyampaikan peristiwa, hasil, dan pengecualian, semuanya kembali ke konsep Janji / Masa Depan yang sama. Beberapa kantor memutuskan untuk memberi nama sedikit berbeda - "Tugas".
Pada akhirnya, mereka menyembunyikan semuanya dalam paket async/await
indah, yang membutuhkan dukungan kompiler atau penerjemah tergantung pada teknologinya.
Masalah dengan situasi logika bisnis asinkron saat ini
Anggap saja coroutine dan Promise, yang dihiasi async/await
, sebagai keberadaan masalah dalam pendekatan yang lebih lama menegaskan proses evolusi itu sendiri.
Kedua istilah ini tidak identik. Sebagai contoh, dalam ECMAScript tidak ada coroutine, tetapi ada bantuan sintaksis untuk menggunakan Promise
, yang pada gilirannya hanya mengatur pekerjaan dengan neraka panggilan balik. Bahkan, mesin scripting seperti V8 melangkah lebih jauh dan melakukan optimasi khusus untuk fungsi dan panggilan async/await
murni.
Para ahli co_async/co_await
yang tidak termasuk dalam C ++ 17 di sini tentang sumber daya , tetapi tekanan dari coroutine raksasa perangkat lunak dapat muncul dalam standar persis dalam bentuknya. Sementara itu, solusi yang diakui secara tradisional adalah Boost.Context , Boost.Fiber dan Boost.Coroutine2 .
Di Jawa, masih belum ada async/await
di tingkat bahasa, tetapi ada solusi seperti EA Async , yang, seperti Boost.Context, perlu disesuaikan untuk setiap versi JVM dan byte kode.
Go memiliki coroutine sendiri, tetapi jika Anda hati-hati melihat artikel dan laporan bug proyek terbuka, ternyata di sini semuanya tidak begitu lancar. Mungkin kehilangan antarmuka coroutine sebagai entitas yang dikelola bukanlah ide yang baik.
Opini Penulis: coroutine bare-metal berbahaya
Secara pribadi, penulis memiliki sedikit terhadap coroutine dalam bahasa yang dinamis, tetapi ia sangat waspada terhadap godaan dengan tumpukan di tingkat kode mesin.
Beberapa poin:
- Diperlukan tumpukan:
- tumpukan di heap memiliki sejumlah kelemahan: masalah penentuan tepat waktu meluap, kerusakan oleh tetangga dan masalah keandalan / keamanan lainnya,
- tumpukan aman memerlukan setidaknya satu halaman memori fisik, satu halaman bersyarat dan overhead tambahan untuk setiap panggilan ke fungsi
async
: 4 + KB (minimum) + peningkatan batas sistem, - pada akhirnya, mungkin sebagian besar memori yang dialokasikan untuk tumpukan tidak digunakan selama downtime coroutine.
- Kita perlu menerapkan logika kompleks untuk menyelamatkan, memulihkan, dan menghapus status coroutine:
- untuk setiap kasus arsitektur prosesor (bahkan model) dan antarmuka biner (ABI): contoh ,
- fitur arsitektur baru atau opsional menimbulkan masalah yang berpotensi laten (misalnya, Intel TSX, co-prosesor ARM atau MIPS),
- potensi masalah lain karena sistem tertutup dokumentasi tertutup (Dokumentasi Boost merujuk pada ini).
- Masalah potensial dengan alat analisis dinamis dan keamanan secara umum:
- misalnya, integrasi dengan Valgrind diperlukan semua karena tumpukan lompatan yang sama,
- sulit untuk berbicara tentang antivirus, tetapi mungkin mereka tidak begitu suka pada contoh masalah dengan JVM di masa lalu,
- Saya yakin jenis serangan baru akan muncul dan kerentanan yang terkait dengan penerapan coroutine akan terungkap.
Pendapat penulis: generator dan yield
kejahatan mendasar
Tema pihak ketiga yang tampaknya terkait langsung dengan konsep coroutine dan properti "terus".
Singkatnya, iterator lengkap harus ada untuk koleksi apa pun. Mengapa membuat masalah iterator-generator yang dipangkas tidak jelas. Misalnya, case dengan range()
dalam Python lebih merupakan pamer eksklusif daripada alasan untuk komplikasi teknis.
Jika case adalah generator tanpa batas, maka logika implementasinya adalah elementer. Mengapa membuat kesulitan teknis tambahan untuk mendorong siklus berkelanjutan tanpa akhir.
Satu-satunya pembenaran yang masuk akal yang kemudian muncul yang diberikan oleh pendukung coroutine adalah segala macam pengurai aliran dengan kontrol terbalik. Bahkan, ini adalah kasus khusus yang sempit untuk memecahkan masalah tunggal di tingkat perpustakaan, bukan logika bisnis aplikasi. Pada saat yang sama ada solusi yang elegan, sederhana dan lebih deskriptif melalui mesin negara yang terbatas. Area masalah teknis ini jauh lebih kecil daripada area logika bisnis biasa.
Bahkan, masalah yang harus dipecahkan diperoleh dari jari dan membutuhkan upaya yang relatif serius untuk implementasi awal dan dukungan jangka panjang. Sedemikian rupa sehingga beberapa proyek dapat memperkenalkan larangan penggunaan coroutine tingkat kode mesin mengikuti contoh larangan goto
atau penggunaan alokasi memori dinamis dalam industri individu.
Pendapat penulis: Model Promyn async/await
ECMAScript lebih dapat diandalkan, tetapi membutuhkan adaptasi
Tidak seperti melanjutkan coroutine, dalam model ini potongan-potongan kode diam-diam dibagi menjadi blok non-interruptible yang dirancang sebagai fungsi anonim. Dalam C ++, ini tidak sepenuhnya cocok karena kekhasan manajemen memori, contoh:
struct SomeObject { using Value = std::vector<int>; Promise funcPromise() { return Promise.resolved(value_); } void funcCallback(std::function<void()> &&cb, const Value& val) { somehow_call_later(cb); } Value value_; }; Promise example() { SomeObject some_obj; return some_obj.funcPromise() .catch([](const std::exception &e){
Pertama, some_obj
akan dihancurkan ketika keluar dari example()
dan sebelum memanggil fungsi lambda.
Kedua, fungsi lambda dengan menangkap variabel atau referensi adalah objek dan diam-diam menambahkan copy / move, yang dapat secara negatif mempengaruhi kinerja dengan sejumlah besar tangkapan dan kebutuhan untuk mengalokasikan memori pada heap selama penghapusan tipe pada std::function
biasa.
Ketiga, antarmuka Promise
itu sendiri dikandung pada konsep "janji" dari hasil, daripada pelaksanaan logika bisnis yang konsisten.
Solusi skema TIDAK optimal mungkin terlihat seperti ini:
Promise example() { struct LocalContext { SomeObject some_obj; }; auto ctx = std::make_shared<LocalContext>(); return some_obj.funcPromise() .catch([](const std::exception &e){
Catatan: std::move
bukannya std::shared_ptr
tidak cocok karena ketidakmampuan untuk mentransfer ke beberapa lambda sekaligus dan pertumbuhan ukurannya.
Dengan tambahan async/await
kengerian asinkron datang dalam kondisi yang dapat dicerna:
async void example() { SomeObject some_obj; try { SomeObject::Value val = await some_obj.func(); } catch (const std::exception& e) (
Pendapat penulis: perencana coroutine adalah sebuah kegagalan
Beberapa kritik menyebut kurangnya penjadwal dan penggunaan sumber daya prosesor yang βtidak adilβ merupakan masalah. Mungkin masalah yang lebih serius adalah lokalitas data dan penggunaan cache prosesor yang efisien.
Pada masalah pertama: penentuan prioritas pada tingkat masing-masing coroutine terlihat seperti biaya overhead yang besar. Sebaliknya, mereka dapat dioperasikan pada kesamaan untuk tugas terpadu tertentu. Inilah yang dilakukan arus lalu lintas.
Ini dimungkinkan dengan membuat instance Event Loop terpisah dengan utas "besi" sendiri dan perencanaan di tingkat OS. Opsi kedua adalah menyinkronkan coroutine dengan primitif (Mutex, Throttle) yang relatif primitif dalam hal kompetisi dan / atau kinerja.
Pemrograman asinkron tidak membuat sumber daya prosesor kenyal dan membutuhkan batasan yang benar-benar normal pada jumlah tugas yang diproses secara bersamaan dan batas waktu eksekusi total.
Perlindungan terhadap pemblokiran yang lama pada satu coroutine memerlukan langkah-langkah yang sama dengan panggilan balik - untuk menghindari pemblokiran panggilan sistem dan siklus pemrosesan data yang panjang.
Masalah kedua membutuhkan penelitian, tetapi setidaknya coroutine menumpuk sendiri dan rincian implementasi Future / Promise sudah melanggar lokalitas data. Ada peluang untuk mencoba melanjutkan eksekusi coroutine yang sama jika Future sudah penting. Diperlukan mekanisme tertentu untuk menghitung waktu eksekusi atau jumlah kelanjutan tersebut untuk mencegah satu coroutine menangkap seluruh waktu prosesor. Ini mungkin tidak memberikan hasil, atau memberikan hasil yang sangat berlipat ganda tergantung pada ukuran cache prosesor dan jumlah utas.
Ada juga poin ketiga - banyak implementasi penjadwalan coroutine memungkinkan mereka untuk dijalankan pada core prosesor yang berbeda, yang sebaliknya menambah masalah karena sinkronisasi wajib ketika mengakses sumber daya bersama. Dalam kasus aliran Peristiwa tunggal, sinkronisasi seperti itu hanya diperlukan pada tingkat logis, sejak itu Setiap blok panggilan balik yang sinkron dijamin bekerja tanpa balapan dengan yang lain.
Pendapat penulis: semuanya baik-baik saja
Kehadiran utas dalam sistem operasi modern tidak meniadakan penggunaan proses individu. Juga, memproses sejumlah besar klien di Event Loop tidak meniadakan penggunaan benang "besi" yang terisolasi untuk kebutuhan lain.
Bagaimanapun, coroutine dan berbagai varian Event Loops menyulitkan proses debugging tanpa dukungan yang diperlukan dalam alat, dan dengan variabel lokal pada tumpukan coroutine, semuanya menjadi lebih sulit - praktis tidak ada cara untuk mendapatkannya.
FutoIn AsyncSteps - sebuah alternatif untuk coroutine
Kami mengambil sebagai dasar pola Perulangan Kejadian dan organisasi skema panggilan balik yang sudah mapan sesuai dengan tipe Janji ECMAScript (JavaScript).
Dalam hal perencanaan eksekusi, kami tertarik pada kegiatan berikut dari Perulangan Kejadian:
Handle immediate(callack)
membutuhkan tumpukan panggilan yang bersih.- Callback
Handle deferred(delay, callback)
. - Batalkan callback
handle.cancel()
.
Jadi kita mendapatkan antarmuka yang disebut AsyncTool
, yang dapat diimplementasikan dalam banyak cara, termasuk di atas perkembangan yang sudah terbukti. Dia tidak memiliki hubungan langsung dengan penulisan logika bisnis, jadi kami tidak akan membahas lebih jauh.
Pohon langkah:
Dalam konsep AsyncSteps, pohon abstrak langkah sinkron berbaris dan dieksekusi dengan masuk jauh ke dalam urutan pembuatan. Langkah-langkah dari setiap level yang lebih dalam ditetapkan secara dinamis saat bagian tersebut selesai.
Semua interaksi terjadi melalui antarmuka AsyncSteps
tunggal, yang, menurut AsyncSteps
, dilewatkan sebagai parameter pertama untuk setiap langkah. Sesuai konvensi, nama parameternya asi
atau tidak digunakan lagi. Pendekatan ini memungkinkan Anda untuk memutus koneksi antara implementasi tertentu dan menulis logika bisnis di plugin dan pustaka.
Dalam implementasi kanonik, setiap langkah menerima instance sendiri dari objek yang mengimplementasikan AsyncSteps
, yang memungkinkan pelacakan tepat waktu kesalahan logis dalam menggunakan antarmuka.
Contoh abstrak:
asi.add( // Level 0 step 1 func( asi ){ print( "Level 0 func" ) asi.add( // Level 1 step 1 func( asi ){ print( "Level 1 func" ) asi.error( "MyError" ) }, onerror( asi, error ){ // Level 1 step 1 catch print( "Level 1 onerror: " + error ) asi.error( "NewError" ) } ) }, onerror( asi, error ){ // Level 0 step 1 catch print( "Level 0 onerror: " + error ) if ( error strequal "NewError" ) { asi.success( "Prm", 123, [1, 2, 3], true) } } ) asi.add( // Level 0 step 2 func( asi, str_param, int_param, array_param ){ print( "Level 0 func2: " + param ) } )
Hasil Eksekusi:
Level 0 func 1 Level 1 func 1 Level 1 onerror 1: MyError Level 0 onerror 1: NewError Level 0 func 2: Prm
Dalam sinkronisasi, akan terlihat seperti ini:
str_res, int_res, array_res, bool_res // undefined try { // Level 0 step 1 print( "Level 0 func 1" ) try { // Level 1 step 1 print( "Level 1 func 1" ) throw "MyError" } catch( error ){ // Level 1 step 1 catch print( "Level 1 onerror 1: " + error ) throw "NewError" } } catch( error ){ // Level 0 step 1 catch print( "Level 0 onerror 1: " + error ) if ( error strequal "NewError" ) { str_res = "Prm" int_res = 123 array_res = [1, 2, 3] bool_res = true } else { re-throw } } { // Level 0 step 2 print( "Level 0 func 2: " + str_res ) }
Mimikri maksimum dari kode sinkron tradisional segera terlihat, yang seharusnya membantu keterbacaan.
Dari sudut pandang logika bisnis, sejumlah besar persyaratan tumbuh seiring waktu, tetapi kita dapat membaginya menjadi bagian-bagian yang mudah dipahami. Dijelaskan di bawah ini, hasil lari dalam praktik selama empat tahun.
API Core Runtime:
add(func[, onerror])
- imitasi try-catch
.success([args...])
- indikasi eksplisit penyelesaian yang berhasil:
- tersirat secara default
- dapat meneruskan hasilnya ke langkah berikutnya.
error(code[, reason)
- gangguan eksekusi dengan kesalahan:
code
- memiliki tipe string yang lebih baik diintegrasikan dengan protokol jaringan dalam arsitektur layanan mikro,reason
- penjelasan sewenang-wenang bagi seseorang.
state()
- analog dari Penyimpanan Lokal Utas. Kunci asosiatif yang telah ditentukan:
error_info
- penjelasan tentang kesalahan terakhir untuk seseorang,last_exception
- penunjuk ke objek pengecualian terakhir,async_stack
- setumpuk panggilan asinkron async_stack
teknologi memungkinkan,- sisanya diatur oleh pengguna.
Contoh sebelumnya sudah dengan kode C ++ nyata dan beberapa fitur tambahan:
#include <futoin/iasyncsteps.hpp> using namespace futoin; void some_api(IAsyncSteps& asi) { asi.add( [](IAsyncSteps& asi) { std::cout << "Level 0 func 1" << std::endl; asi.add( [](IAsyncSteps& asi) { std::cout << "Level 1 func 1" << std::endl; asi.error("MyError"); }, [](IAsyncSteps& asi, ErrorCode code) { std::cout << "Level 1 onerror 1: " << code << std::endl; asi.error("NewError", "Human-readable description"); } ); }, [](IAsyncSteps& asi, ErrorCode code) { std::cout << "Level 0 onerror 1: " << code << std::endl; if (code == "NewError") { // Human-readable error info assert(asi.state().error_info == "Human-readable description"); // Last exception thrown is also available in state std::exception_ptr e = asi.state().last_exception; // NOTE: smart conversion of "const char*" asi.success("Prm", 123, std::vector<int>({1, 2, 3}, true)); } } ); asi.add( [](IAsyncSteps& asi, const futoin::string& str_res, int int_res, std::vector<int>&& arr_res) { std::cout << "Level 0 func 2: " << str_res << std::endl; } ); }
API untuk membuat loop:
loop( func, [, label] )
- langkah dengan tubuh yang berulang berulang.forEach( map|list, func [, label] )
- langkah-iterasi dari objek koleksi.repeat( count, func [, label] )
- langkah-iterasi yang ditentukan berapa kali.break( [label] )
adalah analog dari interupsi loop tradisional.continue( [label] )
adalah analog dari kelanjutan loop tradisional dengan iterasi baru.
Spesifikasi ini menawarkan nama-nama alternatif breakLoop
, continueLoop
dan lainnya jika ada konflik dengan kata-kata yang dipesan.
Contoh C ++:
asi.loop([](IAsyncSteps& asi) {
API untuk integrasi dengan acara eksternal:
setTimeout( timeout_ms )
- melempar kesalahan Timeout
setelah timeout jika langkah dan subtree-nya belum menyelesaikan eksekusi.setCancel( handler )
- mengatur cancel handler, yang dipanggil ketika utas dibatalkan sama sekali dan ketika tumpukan langkah asinkron diperluas selama pemrosesan kesalahan.waitExternal()
- tunggu sederhana untuk acara eksternal.
- Catatan: Aman untuk digunakan hanya dalam teknologi dengan pengumpul sampah.
Panggilan ke salah satu dari fungsi-fungsi ini membuat panggilan eksplisit untuk success()
diperlukan.
Contoh C ++:
asi.add([](IAsyncSteps& asi) { auto handle = schedule_external_callback([&](bool err) { if (err) { try { asi.error("ExternalError"); } catch (...) {
Contoh ECMAScript:
asi.add( (asi) => { asi.waitExternal();
API Integrasi Masa Depan / Janji:
await(promise_future[, on_error])
- menunggu Masa Depan / Janji sebagai langkah.promise()
- mengubah seluruh aliran eksekusi ke Masa Depan / Janji, digunakan alih-alih execute()
.
Contoh C ++:
[](IAsyncSteps& asi) { // Proper way to create new AsyncSteps instances // without hard dependency on implementation. auto new_steps = asi.newInstance(); new_steps->add([](IAsyncSteps& asi) {}); // Can be called outside of AsyncSteps event loop // new_steps.promise().wait(); // or // new_steps.promise<int>().get(); // Proper way to wait for standard std::future asi.await(new_steps->promise()); // Ensure instance lifetime asi.state()["some_obj"] = std::move(new_steps); };
API Kontrol Alur Logika Bisnis:
AsyncSteps(AsyncTool&)
adalah konstruktor yang mengikat utas eksekusi ke Loop Acara tertentu.execute()
- memulai utas eksekusi.cancel()
- membatalkan utas eksekusi.
Implementasi antarmuka spesifik sudah diperlukan di sini.
Contoh C ++:
#include <futoin/ri/asyncsteps.hpp> #include <futoin/ri/asynctool.hpp> void example() { futoin::ri::AsyncTool at; futoin::ri::AsyncSteps asi{at}; asi.loop([&](futoin::IAsyncSteps &asi){ // Some infinite loop logic }); asi.execute(); std::this_thread::sleep_for(std::chrono::seconds{10}); asi.cancel(); // called in d-tor by fact }
API lain:
newInstance()
- memungkinkan Anda membuat utas eksekusi baru tanpa ketergantungan langsung pada implementasinya.sync(object, func, onerror)
- sama, tetapi dengan sinkronisasi relatif terhadap objek yang mengimplementasikan antarmuka yang sesuai.parallel([on_error])
- add()
khusus add()
, subteps di antaranya adalah aliran AsyncSteps yang terpisah:
- semua utas memiliki
state()
umum state()
, - utas induk melanjutkan eksekusi setelah semua anak selesai
- kesalahan tanpa tertangkap pada anak mana pun segera membatalkan semua utas anak lainnya.
Contoh C ++:
#include <futoin/ri/mutex.hpp> using namespace futoin; ri::Mutex mtx_a; void sync_example(IAsyncSteps& asi) { asi.sync(mtx_a, [](IAsyncSteps& asi) { // synchronized section asi.add([](IAsyncSteps& asi) { // inner step in the section // This synchronization is NOOP for already // acquired Mutex. asi.sync(mtx_a, [](IAsyncSteps& asi) { }); }); }); } void parallel_example(IAsyncSteps& asi) { using OrderVector = std::vector<int>; asi.state("order", OrderVector{}); auto& p = asi.parallel([](IAsyncSteps& asi, ErrorCode) { // Overall error handler asi.success(); }); p.add([](IAsyncSteps& asi) { // regular flow asi.state<OrderVector>("order").push_back(1); asi.add([](IAsyncSteps& asi) { asi.state<OrderVector>("order").push_back(4); }); }); p.add([](IAsyncSteps& asi) { asi.state<OrderVector>("order").push_back(2); asi.add([](IAsyncSteps& asi) { asi.state<OrderVector>("order").push_back(5); asi.error("SomeError"); }); }); p.add([](IAsyncSteps& asi) { asi.state<OrderVector>("order").push_back(3); asi.add([](IAsyncSteps& asi) { asi.state<OrderVector>("order").push_back(6); }); }); asi.add([](IAsyncSteps& asi) { asi.state<OrderVector>("order"); // 1, 2, 3, 4, 5 }); };
Primitif standar untuk sinkronisasi
Mutex
- membatasi eksekusi simultan N
thread dengan antrian di Q
, secara default N=1, Q=unlimited
.Throttle
- membatasi jumlah input N
dalam periode P
dengan antrian di Q
, secara default N=1, P=1s, Q=0
.Limiter
adalah kombinasi dari Mutex
dan Throttle
, yang biasanya digunakan pada input pemrosesan permintaan eksternal dan ketika memanggil sistem eksternal untuk tujuan operasi yang stabil di bawah beban.
Dalam hal DefenseRejected
batas antrian, kesalahan DefenseRejected
, artinya jelas dari deskripsi Limiter
.
Manfaat Utama
Konsep AsyncSteps bukanlah tujuan itu sendiri, tetapi lahir dari kebutuhan untuk eksekusi program asinkron yang lebih terkontrol dalam hal batas waktu, pembatalan dan konektivitas keseluruhan dari callback individu. Tidak ada solusi universal pada waktu itu dan sekarang menyediakan fungsionalitas yang sama. Oleh karena itu:
FTN12 β .
setCancel()
β . , . RAII atexit()
.
cancel()
β , . SIGTERM
pthread_cancel()
, .
setTimeout()
β . , "Timeout".
β FutoIn AsyncSteps .
β ABI , . Embedded MMU.
Intel Xeon E3-1245v2/DDR1333 Debian Stretch .
:
- Boost.Fiber
protected_fixedsize_stack
. - Boost.Fiber
pooled_fixedsize_stack
. - FutoIn AsyncSteps .
- FutoIn AsyncSteps (
FUTOIN_USE_MEMPOOL=false
).
- FutoIn NitroSteps<> β .
Boost.Fiber :
- 1 . .
- 30 . 1 . .
- 30 .
mmap()/mprotect()
boost::fiber::protected_fixedsize_stack
. - .
- 30 . 10 . .
"" , .. , . . .
GCC 6.3.0. lang tcmalloc , .
GitHub GitLab .
1.
| | |
---|
Boost.Fiber protected | 4.8s | 208333.333Hz |
Boost.Fiber pooled | 0.23s | 4347826.086Hz |
FutoIn AsyncSteps | 0.21s | 4761904.761Hz |
FutoIn AsyncSteps no mempool | 0.31s | 3225806.451Hz |
FutoIn NitroSteps | 0.255s | 3921568.627Hz |
β .
Boost.Fiber - , pooled_fixedsize_stack
, AsyncSteps.
2.
| | |
---|
Boost.Fiber protected | 6.31s | 158478.605Hz |
Boost.Fiber pooled | 1.558s | 641848.523Hz |
FutoIn AsyncSteps | 1.13s | 884955.752Hz |
FutoIn AsyncSteps no mempool | 1.353s | 739098.300Hz |
FutoIn NitroSteps | 1.43s | 699300.699Hz |
β .
, . , β .
3.
| | |
---|
Boost.Fiber protected | 5.096s | 1962323.390Hz |
Boost.Fiber pooled | 5.077s | 1969667.126Hz |
FutoIn AsyncSteps | 5.361s | 1865323.633Hz |
FutoIn AsyncSteps no mempool | 8.288s | 1206563.706Hz |
FutoIn NitroSteps | 3.68s | 2717391.304Hz |
β .
, Boost.Fiber AsyncSteps, NitroSteps.
| |
---|
Boost.Fiber protected | 124M |
Boost.Fiber pooled | 505M |
FutoIn AsyncSteps | 124M |
FutoIn AsyncSteps no mempool | 84M |
FutoIn NitroSteps | 115M |
β .
, Boost.Fiber .
: Node.js
- Promise
: + 10 . . 10 . JIT NODE_ENV=production
, @futoin/optihelp
.
GitHub GitLab . Node.js v8.12.0 v10.11.0, FutoIn CID .
Tech | Simple | Loop |
---|
Node.js v10 | | |
FutoIn AsyncSteps | 1342899.520Hz | 587.777Hz |
async/await | 524983.234Hz | 630.863Hz |
Node.js v8 | | |
FutoIn AsyncSteps | 682420.735Hz | 588.336Hz |
async/await | 365050.395Hz | 400.575Hz |
β .
async/await
? , V8 Node.js v10 .
, Promise async/await
Node.js Event Loop. ( ), FutoIn AsyncSteps .
AsyncSteps Node.js Event Loop async/await
- Node.js v10.
, ++ β . , Node.js 10 .
Kesimpulan
C++, FutoIn AsyncSteps Boost.Fiber , Boost.Fiber mmap()/mprotect
.
, - , . .
FutoIn AsyncSteps JavaScript async/await
Node.js v10.
, -, . .
- "" . β API.
Kesimpulan
, FutoIn AsyncSteps , "" async/await
. , . Promise
ECMAScript, AsyncSteps "" .
. AsyncSteps NitroSteps .
, - .
Java/JVM β . .
, GitHub / GitLab .