Design: Proposal: Menunggu

Dibuat pada 18 Mei 2020  ·  96Komentar  ·  Sumber: WebAssembly/design

@rreverser dan saya ingin mengusulkan proposal baru untuk WebAssembly: Await .

Motivasi proposal adalah untuk membantu kode " sinkron " yang dikompilasi ke WebAssembly, yang melakukan sesuatu seperti membaca dari file:

fread(buffer, 1, num, file);
// the data is ready to be used right here, synchronously

Kode ini tidak dapat dengan mudah diimplementasikan di lingkungan host yang utamanya asinkron , dan yang akan mengimplementasikan "baca dari file" secara asinkron, misalnya di Web,

const result = fetch("http://example.com/data.dat");
// result is a Promise; the data is not ready yet!

Dengan kata lain, tujuannya adalah untuk membantu masalah sinkronisasi/asinkron yang sangat umum dengan wasm di Web.

Masalah sinkronisasi/asinkron adalah masalah serius. Sementara kode baru dapat ditulis dengan mengingatnya, basis kode besar yang ada seringkali tidak dapat difaktorkan ulang untuk mengatasinya, yang berarti mereka tidak dapat berjalan di Web. Kami memiliki Asyncify yang instrumen file wasm untuk memungkinkan jeda dan melanjutkan, dan yang telah memungkinkan beberapa basis kode untuk porting, jadi kami tidak sepenuhnya diblokir di sini. Namun, instrumentasi wasm memiliki overhead yang signifikan, seperti peningkatan 50% untuk ukuran kode dan 50% pelambatan rata-rata (tapi kadang-kadang jauh lebih buruk), karena kami menambahkan instruksi untuk menulis / membaca kembali di negara bagian lokal dan memanggil tumpukan dan sebagainya. Overhead itu adalah batasan besar dan mengesampingkan Asyncify dalam banyak kasus!

Tujuan proposal ini adalah untuk memungkinkan jeda dan melanjutkan eksekusi dengan cara yang efisien (khususnya, tanpa overhead seperti yang dimiliki Asyncify) sehingga semua aplikasi yang mengalami masalah sinkronisasi/asinkron dapat dengan mudah menghindarinya. Secara pribadi kami bermaksud ini terutama untuk Web, yang dapat membantu WebAssembly berintegrasi lebih baik dengan API Web, tetapi kasus penggunaan di luar Web mungkin juga relevan.

Idenya secara singkat

Masalah inti di sini adalah antara kode wasm yang sinkron dan lingkungan host yang tidak sinkron. Oleh karena itu, pendekatan kami difokuskan pada batas contoh wasm dan luar. Secara konseptual, ketika instruksi await baru dijalankan, instance wasm "menunggu" sesuatu dari luar. Apa artinya "tunggu" akan berbeda pada platform yang berbeda, dan mungkin tidak relevan di semua platform (seperti tidak semua platform mungkin menganggap proposal atom wasm relevan), tetapi pada platform Web secara khusus, instance wasm akan menunggu Janji dan jeda sampai yang menyelesaikan atau menolak. Misalnya, instance wasm dapat berhenti pada operasi jaringan fetch , dan ditulis seperti ini di .wat :

;; call an import which returns a promise
call $do_fetch
;; wait for the promise just pushed to the stack
await
;; do stuff with the result just pushed to the stack

Perhatikan kesamaan umum dengan await di JS dan bahasa lainnya. Meskipun ini tidak identik dengan mereka (lihat detail di bawah), manfaat utamanya adalah memungkinkan penulisan kode yang tampak sinkron (atau lebih tepatnya, untuk mengkompilasi kode yang tampak sinkron menjadi wasm).

Rinciannya

Spesifikasi inti wasm

Perubahan pada spesifikasi inti wasm sangat minim:

  • Tambahkan jenis waitref .
  • Tambahkan instruksi await .

Sebuah tipe ditentukan untuk setiap instruksi await (seperti call_indirect ), misalnya:

;; elaborated wat from earlier, now with full types

(type $waitref_=>_i32 (func (param waitref) (result i32)))
(import "env" "do_fetch" (func $do_fetch (result waitref)))

;; call an import which returns a promise
call $do_fetch
;; wait for the promise just pushed to the stack
await (type $waitref_=>_i32)
;; do stuff with the result just pushed to the stack

Jenis harus menerima waitref , dan dapat mengembalikan jenis apa pun (atau tidak sama sekali).

await hanya didefinisikan dalam hal membuat lingkungan host melakukan sesuatu. Hal ini mirip dalam pengertian itu dengan instruksi unreachable , yang di Web membuat Host melempar RuntimeError , tetapi itu tidak ada dalam spesifikasi inti. Demikian juga, spesifikasi inti wasm hanya mengatakan await dimaksudkan untuk menunggu sesuatu dari lingkungan host, tetapi bukan bagaimana kita akan melakukannya, yang mungkin sangat berbeda di lingkungan host yang berbeda.

Itu saja untuk spesifikasi inti wasm!

Spesifikasi Wasm JS

Perubahan pada spesifikasi JS wasm (yang hanya memengaruhi lingkungan JS seperti Web) lebih menarik:

  • Nilai waitref yang valid adalah Janji JS.
  • Ketika await dieksekusi pada Promise, seluruh instance wasm berhenti sejenak dan menunggu Promise tersebut diselesaikan atau ditolak.
  • Jika Promise terselesaikan, instance melanjutkan eksekusi setelah mendorong ke tumpukan nilai yang diterima dari Promise (jika ada)
  • Jika Promise menolak, kami melanjutkan eksekusi dan melemparkan pengecualian wasm dari lokasi await .

Dengan "seluruh instance wasm dijeda" yang kami maksud adalah semua status lokal dipertahankan (tumpukan panggilan, nilai lokal, dll.) sehingga kami dapat melanjutkan eksekusi saat ini nanti, seolah-olah kami tidak pernah berhenti (tentu saja status global mungkin telah berubah, seperti Memori mungkin telah ditulis). Sementara kita menunggu, JS event loop berfungsi secara normal, dan hal-hal lain dapat terjadi. Ketika kami melanjutkan nanti (jika kami tidak menolak Janji, dalam hal ini pengecualian akan dilemparkan) kami melanjutkan persis di mana kami tinggalkan, pada dasarnya seolah-olah kami tidak pernah berhenti (tetapi sementara hal-hal lain telah terjadi, dan keadaan global mungkin telah diubah, dll).

Seperti apa JS saat memanggil instance wasm yang kemudian berhenti? Untuk menjelaskannya, pertama-tama mari kita lihat contoh umum yang ditemui saat mem-porting aplikasi asli ke wasm, loop peristiwa:

void event_loop_iteration() {
  // ..
  while (auto task = getTask()) {
    task.run(); // this *may* be a network fetch
  }
  // ..
}

Bayangkan bahwa fungsi ini dipanggil sekali per requestAnimationFrame . Itu menjalankan tugas yang diberikan padanya, yang mungkin termasuk: rendering, fisika, audio, dan pengambilan jaringan. Jika kita memiliki acara pengambilan jaringan, maka kita akan menjalankan instruksi await fetch pada Promise fetch. Kami dapat melakukannya 0 kali untuk satu panggilan event_loop_iteration , atau 1 kali, atau berkali-kali. Kami hanya tahu apakah kami akhirnya melakukannya selama eksekusi wasm ini - tidak sebelumnya, dan khususnya tidak pada pemanggil JS dari ekspor wasm ini. Jadi penelepon itu harus siap jika instance dijeda atau tidak.

Situasi yang agak analog dapat terjadi dalam JavaScript murni:

function foo(bar) {
  // ..
  let result = bar(42);
  // ..
}

foo mendapatkan fungsi JS bar dan memanggilnya dengan beberapa data. Di JS bar mungkin merupakan fungsi async atau mungkin yang normal. Jika tidak sinkron, ia mengembalikan Janji, dan hanya menyelesaikan eksekusi nanti. Jika normal, itu dijalankan sebelum kembali dan mengembalikan hasil yang sebenarnya. foo dapat berasumsi bahwa ia mengetahui jenis bar (tidak ada jenis yang ditulis dalam JS, bahkan bar mungkin bukan fungsi!), atau ia dapat menangani kedua jenis fungsi menjadi sepenuhnya umum.

Sekarang, biasanya Anda tahu persis apa set fungsi bar mungkin! Misalnya, Anda mungkin telah menulis foo dan kemungkinan bar s dalam koordinasi, atau mendokumentasikan dengan tepat apa yang diharapkan. Tetapi interaksi wasm/JS yang sedang kita bicarakan di sini sebenarnya lebih mirip dengan kasus di mana Anda tidak memiliki hubungan yang erat antara berbagai hal, dan di mana sebenarnya Anda perlu menangani kedua kasus tersebut. Seperti disebutkan sebelumnya, contoh event_loop_iteration membutuhkan itu. Tetapi lebih umum lagi, seringkali wasm adalah aplikasi terkompilasi Anda sedangkan JS adalah kode "runtime" generik, sehingga JS harus menangani semua kasus. JS dapat dengan mudah melakukannya, tentu saja, misalnya menggunakan result instanceof Promise untuk memeriksa hasilnya, atau menggunakan JS await :

async function runEventLoopIteration() {
  // await in JavaScript can handle Promises as well as regular synchronous values
  // in the same way, so the log is guaranteed to be written out consistently after
  // the operation has finished (note: this handles 0 or 1 iterations, but could be
  // generalized)
  await wasm.event_loop_iteration();
  console.log("the event loop iteration is done");
}

(perhatikan bahwa jika kita tidak membutuhkan console.log maka kita tidak memerlukan JS await dalam contoh ini, dan hanya akan memiliki panggilan normal ke ekspor wasm)

Untuk meringkas hal di atas, kami mengusulkan agar perilaku instance wasm yang berhenti dimodelkan pada kasus JS dari fungsi yang mungkin atau mungkin tidak asinkron, yang dapat kami nyatakan sebagai:

  • Ketika await dieksekusi, instance wasm segera keluar kembali ke siapa pun yang memanggilnya (biasanya itu adalah JS yang memanggil ekspor wasm, tetapi lihat catatan nanti). Penelepon menerima Janji yang dapat digunakan untuk mengetahui kapan eksekusi wasm berakhir, dan untuk mendapatkan hasil jika ada.

Dukungan rantai alat / perpustakaan

Dalam pengalaman kami dengan Asyncify dan alat terkait, mudah (dan menyenangkan!) untuk menulis sedikit JS untuk menangani instance wasm yang menunggu. Selain opsi yang disebutkan sebelumnya, perpustakaan dapat melakukan salah satu hal berikut:

  1. Bungkus contoh wasm untuk membuat ekspornya selalu mengembalikan Janji. Itu memberikan antarmuka sederhana yang bagus ke luar (namun, itu menambahkan overhead ke panggilan cepat ke wasm yang tidak berhenti). Inilah yang dilakukan perpustakaan pembantu Asyncify mandiri , misalnya.
  2. Tulis beberapa status global ketika sebuah instans berhenti dan periksa dari JS yang dipanggil ke dalam instans. Itulah yang dilakukan integrasi Asyncify Emscripten, misalnya.

Lebih banyak lagi yang dapat dibangun di atas pendekatan semacam itu, atau pendekatan lainnya. Kami lebih suka menyerahkan semua itu ke rantai alat dan pustaka untuk menghindari kerumitan dalam proposal dan VM.

Implementasi dan Kinerja

Beberapa faktor akan membantu menjaga implementasi VM tetap sederhana:

  1. Jeda/lanjutan hanya terjadi pada penantian, dan kami mengetahui lokasinya secara statis di dalam setiap fungsi.
  2. Ketika kami melanjutkan, kami melanjutkan persis dari tempat kami meninggalkan sesuatu, dan kami hanya melakukannya sekali. Secara khusus, kami tidak pernah "membagi" eksekusi: tidak ada yang mengembalikan dua kali, tidak seperti setjmp C atau coroutine dalam sistem yang memungkinkan kloning/forking.
  3. Dapat diterima jika kecepatan await lebih lambat dari panggilan normal ke JS, karena kita akan menunggu Promise, yang setidaknya menyiratkan Promise dialokasikan dan kita menunggu di loop acara ( yang memiliki overhead minimum plus berpotensi menunggu hal-hal lain yang sedang berjalan ). Artinya, kasus penggunaan di sini tidak menuntut pelaksana VM menemukan cara untuk membuat await sangat cepat. Kami hanya ingin await menjadi efisien dibandingkan dengan persyaratan di sini, dan khususnya mengharapkannya jauh lebih cepat daripada overhead besar Asyncify.

Mengingat hal di atas, implementasi alami adalah menyalin tumpukan saat kami berhenti. Sementara itu memiliki beberapa overhead, mengingat ekspektasi kinerja di sini seharusnya sangat masuk akal. Dan jika kita menyalin tumpukan hanya saat kita menjeda maka kita dapat menghindari melakukan pekerjaan ekstra untuk mempersiapkan jeda. Artinya, tidak boleh ada biaya tambahan umum (yang sangat berbeda dari Asyncify!)

Perhatikan bahwa meskipun menyalin tumpukan adalah pendekatan alami di sini, ini bukan operasi yang sepenuhnya sepele, karena penyalinan mungkin bukan memcpy sederhana, tergantung pada internal VM. Misalnya, jika tumpukan berisi pointer ke dirinya sendiri, maka itu perlu disesuaikan, atau agar tumpukan dapat dipindahkan. Atau, dimungkinkan untuk menyalin tumpukan kembali ke posisi semula sebelum melanjutkannya, karena seperti yang disebutkan sebelumnya tidak pernah "bercabang".

Perhatikan juga bahwa tidak ada dalam proposal ini yang memerlukan penyalinan tumpukan. Mungkin beberapa implementasi dapat melakukan hal lain, berkat faktor penyederhanaan yang disebutkan dalam 3 poin sebelumnya di bagian ini. Perilaku yang dapat diamati di sini cukup sederhana, dan penanganan tumpukan eksplisit bukan bagian darinya.

Kami sangat tertarik untuk mendengar masukan dari pelaksana VM di bagian ini!

Klarifikasi

Proposal ini hanya menjeda eksekusi WebAssembly kembali ke pemanggil instance wasm. Itu tidak mengizinkan menjeda bingkai tumpukan host (JS atau browser). await beroperasi pada instance wasm, hanya memengaruhi bingkai tumpukan di dalamnya.

Tidak apa-apa untuk memanggil ke instance WebAssembly saat jeda telah terjadi, dan beberapa acara jeda/lanjutkan dapat dilakukan sekaligus. (Perhatikan bahwa jika VM mengambil pendekatan menyalin tumpukan maka ini tidak berarti tumpukan baru harus dialokasikan setiap kali kita memasuki modul, karena kita hanya perlu menyalinnya jika kita benar-benar berhenti.)

Koneksi ke proposal lain

Pengecualian

Penolakan janji melontarkan eksepsi berarti proposal ini bergantung pada proposal eksepsi wasm.

coroutine

Proposal coroutine Andreas Rossberg juga berkaitan dengan jeda dan melanjutkan eksekusi. Namun, sementara ada beberapa tumpang tindih konseptual, kami tidak berpikir proposal bersaing. Keduanya berguna karena berfokus pada kasus penggunaan yang berbeda. Secara khusus, proposal coroutine memungkinkan coroutine untuk dialihkan di antara wasm dalam , sementara proposal menunggu memungkinkan seluruh instance menunggu lingkungan luar . Dan cara di mana kedua hal itu dilakukan mengarah pada karakteristik yang berbeda.

Secara khusus, proposal coroutine menangani pembuatan tumpukan secara eksplisit (instruksi diberikan untuk membuat coroutine, untuk menjeda satu, dll.). Proposal menunggu hanya berbicara tentang menjeda dan melanjutkan dan oleh karena itu penanganan tumpukan implisit . Penanganan tumpukan eksplisit sesuai saat Anda tahu Anda membuat coroutine tertentu, sementara implisit sesuai saat Anda hanya tahu Anda perlu menunggu sesuatu selama eksekusi (lihat contoh sebelumnya dengan event_loop_iteration ).

Karakteristik kinerja kedua model tersebut mungkin sangat berbeda. Jika misalnya kita membuat coroutine setiap kali kita menjalankan kode yang mungkin berhenti (sekali lagi, sering kita tidak tahu sebelumnya) yang mungkin mengalokasikan memori yang tidak perlu. Perilaku yang diamati dari await lebih sederhana daripada yang dapat dilakukan coroutine umum sehingga mungkin lebih mudah untuk diterapkan.

Perbedaan signifikan lainnya adalah bahwa await adalah instruksi tunggal yang menyediakan semua kebutuhan modul wasm untuk memperbaiki ketidakcocokan sinkronisasi/asinkron yang dimiliki wasm dengan Web (lihat contoh .wat pertama dari awal). Ini juga sangat mudah digunakan di sisi JS yang hanya dapat memberikan dan/atau menerima Janji (sementara sedikit kode perpustakaan mungkin berguna untuk ditambahkan, seperti yang disebutkan sebelumnya, itu bisa sangat minim).

Secara teori, kedua proposal dapat dirancang untuk saling melengkapi. Mungkin await bisa menjadi salah satu instruksi dalam proposal coroutine? Pilihan lain adalah mengizinkan await untuk beroperasi pada coroutine (pada dasarnya memberikan contoh wasm cara mudah untuk menunggu hasil coroutine).

WASI#276

Kebetulan WASI #276 diposting oleh @tqchen tepat saat kami selesai menulis ini. Kami sangat senang melihat hal itu karena kami meyakini bahwa dukungan coroutine dan async adalah fungsi yang terpisah.

Kami percaya bahwa instruksi await dapat membantu mengimplementasikan sesuatu yang sangat mirip dengan apa yang diusulkan di sana (opsi C3), dengan perbedaan bahwa tidak perlu ada syscalls async khusus, tetapi beberapa syscalls dapat mengembalikan waitref yang kemudian bisa menjadi await -ed.

Untuk JavaScript, kami mendefinisikan menunggu sebagai menjeda instance wasm, yang masuk akal karena kami dapat memiliki banyak instance serta JavaScript di halaman. Namun, di beberapa lingkungan server mungkin hanya ada host dan satu instance wasm, dan dalam hal ini, menunggu bisa jauh lebih sederhana, mungkin benar-benar menunggu di deskriptor file atau di GPU. Atau menunggu dapat menjeda seluruh VM wasm tetapi tetap menjalankan loop acara. Kami sendiri tidak memiliki ide khusus di sini, tetapi berdasarkan diskusi dalam masalah itu mungkin ada kemungkinan menarik di sini, kami ingin tahu apa yang dipikirkan orang!

Kasus sudut: contoh wasm => contoh wasm => menunggu

Di lingkungan JS, ketika instance wasm dijeda, ia segera kembali ke siapa pun yang memanggilnya. Kami menjelaskan apa yang terjadi jika penelepon berasal dari JS, dan hal yang sama terjadi jika penelepon adalah browser (misalnya, jika kami melakukan setTimeout pada ekspor wasm yang berhenti; tetapi tidak ada hal menarik yang terjadi di sana, seperti Janji yang dikembalikan diabaikan saja). Tetapi ada kasus lain, panggilan yang berasal dari wasm, yaitu, di mana instance wasm A secara langsung memanggil ekspor dari instance B , dan B berhenti. Jeda membuat kita segera keluar dari B dan mengembalikan Promise .

Ketika penelepon adalah JavaScript, sebagai bahasa dinamis, ini bukan masalah, dan sebenarnya masuk akal untuk mengharapkan penelepon untuk memeriksa jenis seperti yang dibahas sebelumnya. Ketika penelepon adalah WebAssembly, yang diketik secara statis, ini canggung. Jika kita tidak melakukan sesuatu dalam proposal untuk ini maka nilainya akan diberikan, dalam contoh kita dari Promise ke instance apa pun yang diharapkan A (jika i32 , itu akan dilemparkan ke sebuah 0 ). Sebagai gantinya, kami menyarankan agar terjadi kesalahan:

  • Jika instance wasm memanggil (secara langsung atau menggunakan call_indirect ) fungsi dari instance wasm lain, dan saat menjalankan instance lain await dieksekusi, maka pengecualian RuntimeError adalah dilempar dari lokasi await .

Yang penting, ini dapat dilakukan tanpa overhead kecuali menjeda, yaitu, mempertahankan panggilan normal wasm instance -> wasm instance dengan kecepatan penuh, dengan memeriksa tumpukan hanya saat melakukan jeda.

Perhatikan bahwa pengguna yang menginginkan sesuatu seperti instance wasm untuk memanggil yang lain dan memiliki jeda terakhir dapat melakukannya, tetapi mereka perlu menambahkan beberapa JS di antara keduanya.

Opsi lain di sini adalah jeda untuk menyebar ke wasm panggilan juga, yaitu, semua wasm akan berhenti sepenuhnya ke JS, berpotensi mencakup beberapa instance wasm. Ini memiliki beberapa keuntungan, seperti batas modul wasm berhenti penting, tetapi juga kerugiannya, seperti propagasi menjadi kurang intuitif (penulis instance panggilan mungkin tidak mengharapkan perilaku seperti itu) dan menambahkan JS di tengah dapat mengubah perilaku (juga berpotensi secara tidak terduga). Mengharuskan pengguna memiliki JS di antaranya, seperti yang disebutkan sebelumnya, tampaknya kurang berisiko.

Pilihan lain mungkin untuk beberapa ekspor wasm ditandai async sementara yang lain tidak, dan kemudian kita dapat mengetahui secara statis apa itu, dan tidak mengizinkan panggilan yang tidak tepat; tetapi lihat contoh event_loop_iteration dari sebelumnya yang merupakan kasus umum yang tidak dapat diselesaikan dengan menandai ekspor, dan ada juga panggilan tidak langsung, jadi kami tidak dapat menghindari masalah seperti itu.

Pendekatan alternatif dipertimbangkan

Mungkin kita tidak memerlukan instruksi await baru sama sekali, jika wasm berhenti setiap kali impor JS mengembalikan Janji? Masalahnya adalah saat ini ketika JS mengembalikan Janji yang bukan kesalahan. Perubahan yang tidak kompatibel ke belakang seperti itu berarti wasm tidak dapat lagi menerima Janji tanpa berhenti, tetapi itu mungkin berguna juga.

Opsi lain yang kami pertimbangkan adalah menandai impor dengan cara mengatakan "impor ini harus berhenti jika mengembalikan Janji". Kami memikirkan berbagai opsi untuk menandainya, baik di JS atau di sisi lain, tetapi tidak menemukan sesuatu yang terasa benar. Misalnya, jika kita menandai impor di sisi JS maka modul wasm tidak akan tahu apakah panggilan ke impor dijeda atau tidak hingga langkah tautan, saat impor tiba. Artinya, panggilan untuk mengimpor dan menjeda akan "dicampur bersama". Sepertinya hal yang paling mudah adalah hanya memiliki instruksi baru untuk ini, await , yang eksplisit tentang menunggu. Secara teori kemampuan seperti itu mungkin berguna di luar Web juga (lihat catatan sebelumnya), jadi memiliki instruksi untuk semua orang dapat membuat segalanya lebih konsisten secara keseluruhan.

Diskusi terkait sebelumnya

https://github.com/WebAssembly/design/issues/1171
https://github.com/WebAssembly/design/issues/1252
https://github.com/WebAssembly/design/issues/1294
https://github.com/WebAssembly/design/issues/1321

Terima kasih telah membaca, umpan balik diterima!

Komentar yang paling membantu

Saya berharap untuk melakukan lebih banyak diskusi secara publik di sini, tetapi untuk menghemat waktu, saya menghubungi beberapa pelaksana VM secara langsung, karena sejauh ini hanya sedikit yang terlibat di sini. Mengingat umpan balik mereka bersama dengan diskusi di sini, sayangnya saya pikir kita harus menghentikan proposal ini.

Await memiliki perilaku yang dapat diamati jauh lebih sederhana daripada coroutine umum atau peralihan tumpukan, tetapi orang-orang VM yang saya ajak bicara setuju dengan @rossberg bahwa VM bekerja pada akhirnya mungkin akan serupa untuk keduanya. Dan setidaknya beberapa orang VM percaya bahwa kami akan mendapatkan coroutine atau peralihan tumpukan, dan bahwa kami dapat mendukung kasus penggunaan menunggu menggunakan itu. Itu berarti membuat coroutine/tumpukan baru pada setiap panggilan ke wasm (tidak seperti dengan proposal ini), tetapi setidaknya beberapa orang VM berpikir itu bisa dibuat cukup cepat.

Selain kurangnya minat dari orang-orang VM, kami memiliki beberapa keberatan yang kuat terhadap proposal ini dari @fgmccabe dan @RossTate , seperti yang dibahas di atas. Kami tidak setuju pada beberapa hal tetapi saya menghargai sudut pandang itu, dan waktu yang digunakan untuk menjelaskannya.

Kesimpulannya, secara keseluruhan rasanya akan membuang-buang waktu semua orang untuk mencoba bergerak maju di sini. Tapi terima kasih untuk semua orang yang berpartisipasi dalam diskusi! Dan semoga setidaknya ini memotivasi untuk memprioritaskan coroutine/stack switching.

Perhatikan bahwa bagian JS dari proposal ini mungkin relevan di masa mendatang, karena JS gula pada dasarnya untuk integrasi Promise yang nyaman. Kita harus menunggu peralihan tumpukan atau coroutine dan melihat apakah ini bisa bekerja di atas itu. Tapi saya pikir tidak ada gunanya membiarkan masalah ini tetap terbuka, jadi tutup.

Semua 96 komentar

Tulisan yang luar biasa! Saya menyukai gagasan penangguhan yang dikendalikan oleh tuan rumah. Proposal @rossberg juga membahas sistem efek fungsional, dan saya akui bukan ahlinya, tetapi pada pandangan pertama sepertinya itu dapat memenuhi kebutuhan aliran kontrol non-lokal yang sama.

Mengenai: "Mengingat hal di atas, implementasi alami adalah menyalin tumpukan saat kami menjeda." Bagaimana ini bekerja untuk tumpukan eksekusi? Saya membayangkan bahwa sebagian besar mesin JIT berbagi tumpukan eksekusi C asli antara JS dan wasm jadi saya tidak yakin apa artinya menyimpan dan memulihkan dalam konteks ini. Apakah proposal ini berarti bahwa tumpukan eksekusi wasm perlu divirtualisasikan? IIUC menghindari penggunaan C stack seperti ini cukup rumit ketika python mencoba melakukan sesuatu yang serupa: https://github.com/stackless-dev/stackless/wiki.

Saya berbagi kekhawatiran yang sama dengan @sbc100. Menyalin tumpukan secara inheren adalah operasi yang cukup sulit, terutama jika VM Anda belum memiliki implementasi GC.

@sbc100

Apakah proposal ini berarti bahwa tumpukan eksekusi wasm perlu divirtualisasikan?

Saya harus menyerahkan ini kepada pelaksana VM karena saya bukan ahlinya. Dan saya tidak mengerti koneksi ke python stackless, tapi mungkin saya tidak tahu apa itu cukup baik untuk memahami koneksi, maaf!

Namun secara umum: berbagai pendekatan coroutine bekerja dengan memanipulasi penunjuk tumpukan pada level rendah . Pendekatan-pendekatan itu mungkin bisa menjadi pilihan di sini. Kami ingin menunjukkan bahwa bahkan jika tumpukan harus disalin sebagai bagian dari pendekatan semacam itu, hal itu memiliki overhead yang dapat diterima dalam konteks ini.

(Kami tidak yakin apakah pendekatan tersebut dapat bekerja di VM wasm atau tidak - berharap untuk mendengar dari pelaksana jika ya atau tidak, dan apakah ada opsi yang lebih baik!)

@lachlansneff

Bisakah Anda menjelaskan lebih detail apa yang Anda maksud dengan GC membuat segalanya lebih mudah? Saya tidak mengikuti.

@kripken GC sering (tetapi tidak selalu) memiliki kemampuan untuk berjalan di tumpukan, yang diperlukan jika Anda perlu menulis ulang pointer pada tumpukan untuk menunjuk ke tumpukan baru. Mungkin seseorang yang tahu lebih banyak tentang JSC dapat mengkonfirmasi atau menyangkal hal ini.

@lachlansneff

Terima kasih, sekarang saya mengerti apa yang Anda katakan.

Kami tidak menyarankan bahwa berjalan di tumpukan sedemikian rupa (mengidentifikasi setiap lokal sepanjang jalan, dll) diperlukan untuk melakukan ini. (Untuk pendekatan lain yang mungkin, lihat tautan di komentar terakhir saya tentang metode implementasi coroutine tingkat rendah.)

Saya minta maaf atas terminologi "salin tumpukan" dalam proposal - saya melihat itu tidak cukup jelas, berdasarkan umpan balik Anda dan @sbc100 . Sekali lagi, kami tidak ingin menyarankan pendekatan implementasi VM tertentu. Kami hanya ingin mengatakan bahwa jika menyalin tumpukan diperlukan dalam beberapa pendekatan, itu tidak akan menjadi masalah untuk kecepatan.

Daripada menyarankan pendekatan implementasi khusus, kami berharap untuk mendengar dari orang-orang VM bagaimana menurut mereka hal ini dapat dilakukan!

Saya sangat senang melihat proposal ini. Lucet telah memiliki operator yield dan resume untuk sementara waktu sekarang, dan kami menggunakannya secara tepat untuk berinteraksi dengan kode asinkron yang berjalan di lingkungan host Rust.

Ini cukup mudah untuk ditambahkan ke Lucet, karena desain kami sudah berkomitmen untuk mempertahankan tumpukan terpisah untuk eksekusi Wasm, tapi saya bisa membayangkan itu bisa menghadirkan beberapa kesulitan implementasi untuk VM yang tidak.

Proposal ini terdengar hebat! Kami telah mencoba untuk mendapatkan cara yang baik untuk mengelola kode async pada wasmer-js untuk sementara waktu (karena kami tidak memiliki akses ke internal VM dalam konteks browser).

Daripada menyarankan pendekatan implementasi khusus, kami berharap untuk mendengar dari orang-orang VM bagaimana menurut mereka hal ini dapat dilakukan!

Saya pikir mungkin menggunakan strategi panggilan balik untuk fungsi async mungkin cara termudah untuk membuat semuanya berjalan dan juga dengan cara agnostik bahasa.

Tampaknya .await dapat dipanggil dalam JsPromise di dalam fungsi Rust menggunakan wasm-bindgen-futures ? Bagaimana ini bisa bekerja tanpa instruksi await yang diusulkan di sini? Saya minta maaf atas ketidaktahuan saya, saya mencari solusi untuk memanggil fetch inside wasm dan saya sedang belajar tentang Asyncify, tetapi tampaknya solusi Rust lebih sederhana. Apa yang saya lewatkan di sini? Bisakah seseorang menjelaskannya untuk saya?

Saya sangat senang dengan proposal ini. Keuntungan utama dari proposal ini adalah kesederhanaannya, karena kita dapat membangun API yang disinkronkan dengan POV wasm, dan itu membuatnya lebih mudah untuk mem-port aplikasi tanpa harus secara eksplisit memikirkan panggilan balik dan async/menunggu. Ini akan memungkinkan kami untuk menghadirkan pembelajaran mesin berbasis WASM dan WebGPU ke wasm vms asli menggunakan satu API asli dan berjalan di web dan asli.

Satu hal yang menurut saya layak didiskusikan adalah tanda tangan dari fungsi-fungsi yang berpotensi dipanggil menunggu. Bayangkan kita memiliki fungsi berikut:

int test() {
   await();
   return 1;
}

Tanda tangan dari fungsi yang sesuai adalah () => i32 . Di bawah proposal baru, panggilan ke dalam pengujian dapat mengembalikan i32, atau Promise<i32> . Perhatikan bahwa lebih sulit untuk meminta pengguna mendeklarasikan tanda tangan baru secara statis (karena biaya porting kode, dan dapat berupa panggilan tidak langsung di dalam fungsi yang kami tidak tahu bahwa panggilan menunggu).

Haruskah kita memiliki mode panggilan terpisah ke dalam fungsi yang diekspor (misalnya panggilan async) untuk menunjukkan menunggu diizinkan selama runtime?

Dari segi terminologi, operasi yang diusulkan seperti operasi hasil dalam sistem operasi. Karena itu menghasilkan kontrol ke OS (dalam hal ini wasm VM) untuk menunggu syscall ke finsih.

Jika saya memahami proposal ini dengan benar, saya pikir ini kira-kira setara dengan menghapus batasan bahwa await di JS hanya dapat digunakan dalam fungsi async . Artinya, di sisi wasm waitref bisa menjadi externref dan alih-alih instruksi await Anda bisa memiliki fungsi yang diimpor $await : [externref] -> [] , dan di sisi JS anda dapat menyediakan foo(promise) => await promise sebagai fungsi untuk mengimpor. Di arah lain, jika Anda adalah kode JS yang ingin await pada Janji di luar fungsi async , Anda dapat memberikan janji itu ke modul wasm yang hanya memanggil await pada masukan. Apakah itu pemahaman yang benar?

@RossTate Tidak cukup, AIUI. Kode wasm dapat await janji (sebut saja promise1 ), tetapi hanya eksekusi wasm yang akan menghasilkan, bukan JS. Kode wasm akan mengembalikan janji yang berbeda (sebut saja promise2 ) ke pemanggil JS. Ketika promise1 diselesaikan, maka eksekusi wasm berlanjut. Akhirnya, ketika kode wasm keluar secara normal, maka promise2 akan diselesaikan dengan hasil fungsi wasm.

@tqchen

Haruskah kita memiliki mode panggilan terpisah ke dalam fungsi yang diekspor (misalnya panggilan async) untuk menunjukkan menunggu diizinkan selama runtime?

Menarik - di mana Anda melihat manfaatnya? Seperti yang Anda katakan, benar-benar tidak ada cara untuk memastikan apakah ekspor akan berakhir dengan await atau tidak, dalam situasi port umum, jadi yang terbaik hanya dapat digunakan kadang-kadang. Apakah ini akan membantu VM secara internal?

Memiliki deklarasi eksplisit dapat memastikan bahwa pengguna menyatakan maksud mereka dengan jelas, dan VM dapat memberikan pesan kesalahan yang tepat jika maksud pengguna tidak melakukan panggilan yang menjalankan asinkron.

Dari POV pengguna juga membuat penulisan kode lebih konsisten. Misalnya, pengguna dapat menulis kode berikut, bahkan jika pengujian tidak memanggil menunggu, dan antarmuka sistem mengembalikan Promise.resolve(test()) secara otomatis.

await inst.exports_async.test();

Dari POV pengguna juga membuat penulisan kode lebih konsisten. Misalnya, pengguna dapat menulis kode berikut, bahkan jika pengujian tidak memanggil menunggu, dan antarmuka sistem mengembalikan Promise.resolve(test()) secara otomatis.

@tqchen Perhatikan bahwa pengguna sudah dapat melakukan ini seperti yang ditunjukkan pada contoh di tes proposal. Artinya, JavaScript sudah mendukung dan menangani nilai sinkron dan asinkron dalam operator await dengan cara yang sama.

Jika sarannya adalah untuk menerapkan satu jenis statis, maka kami yakin ini dapat dilakukan pada tingkat sistem lint atau jenis atau tingkat pembungkus JavaScript tanpa menimbulkan kerumitan di sisi WebAssembly inti atau membatasi pelaksana pembungkus tersebut.

Ah, terima kasih atas koreksinya, @binji.

Dalam hal ini, apakah yang berikut ini kira-kira setara? Tambahkan fungsi WebAssembly.instantiateAsync(moduleBytes, imports, "name1", "name2") ke JS API. Misalkan moduleBytes memiliki sejumlah impor ditambah impor tambahan import "name1" "name2" (func (param externref)) . Kemudian fungsi ini membuat instance impor dengan nilai yang diberikan oleh imports dan membuat instance impor tambahan dengan apa yang secara konseptual await . Ketika fungsi yang diekspor dibuat dari modul ini, fungsi tersebut dijaga sehingga ketika await ini dipanggil, ia berjalan ke atas tumpukan untuk menemukan penjaga pertama dan kemudian menyalin isi tumpukan ke dalam Promise baru yang kemudian segera dikembalikan.

Apakah itu akan berhasil? Menurut saya, proposal ini dapat dilakukan semata-mata dengan memodifikasi JS API tanpa perlu memodifikasi WebAssembly itu sendiri. Tentu saja, itu masih menambahkan banyak fungsi yang berguna.

@kripken Bagaimana fungsi start ditangani? Apakah itu akan secara statis melarang await , atau entah bagaimana akan berinteraksi dengan instantiasi Wasm?

@malbarbo wasm-bindgen-futures memungkinkan Anda untuk menjalankan kode async di Rust. Itu berarti Anda harus menulis program Anda dengan cara async: Anda harus menandai fungsi Anda sebagai async , dan Anda perlu menggunakan .await . Tetapi proposal ini memungkinkan Anda menjalankan kode asinkron tanpa menggunakan async atau .await , alih-alih terlihat seperti pemanggilan fungsi sinkron biasa.

Dengan kata lain, saat ini Anda tidak dapat menggunakan API OS sinkron (seperti std::fs ) karena web hanya memiliki API asinkron. Tetapi dengan proposal ini Anda dapat menggunakan API OS sinkron: mereka akan menggunakan Promises secara internal, tetapi mereka akan terlihat sinkron dengan Rust.

Bahkan jika proposal ini diterapkan, wasm-bindgen-futures akan tetap ada dan akan tetap berguna, karena menangani kasus penggunaan yang berbeda (menjalankan fungsi async ). Dan fungsi async berguna karena dapat dengan mudah diparalelkan.

@RossTate Tampaknya saran Anda sangat mirip dengan yang tercakup dalam "Pendekatan alternatif dipertimbangkan":

Opsi lain yang kami pertimbangkan adalah menandai impor dengan cara mengatakan "impor ini harus berhenti jika mengembalikan Janji". Kami memikirkan berbagai opsi untuk menandainya, baik di JS atau di sisi lain, tetapi tidak menemukan sesuatu yang terasa benar. Misalnya, jika kita menandai impor di sisi JS maka modul wasm tidak akan tahu apakah panggilan ke impor dijeda atau tidak hingga langkah tautan, saat impor tiba. Artinya, panggilan untuk mengimpor dan menjeda akan "dicampur bersama". Sepertinya hal yang paling mudah adalah memiliki instruksi baru untuk ini, menunggu, yang secara eksplisit tentang menunggu. Secara teori kemampuan seperti itu mungkin berguna di luar Web juga (lihat catatan sebelumnya), jadi memiliki instruksi untuk semua orang dapat membuat segalanya lebih konsisten secara keseluruhan.

Bagaimana fungsi start ditangani? Apakah itu akan secara statis melarang menunggu, atau entah bagaimana akan berinteraksi dengan instantiasi Wasm?

@Pauan Kami tidak membahas ini secara khusus, tetapi saya pikir tidak ada yang menghentikan kami untuk mengizinkan await di start juga. Dalam hal ini Promise yang dikembalikan dari instantiate{Streaming} akan tetap diselesaikan/ditolak secara alami ketika fungsi start telah selesai dijalankan sepenuhnya, dengan satu-satunya perbedaan adalah bahwa ia akan menunggu janji await ed.

Yang mengatakan, batasan yang sama seperti hari ini berlaku dan untuk saat ini tidak akan terlalu berguna untuk kasus yang memerlukan akses misalnya memori yang diekspor.

@RReverser Bagaimana cara kerjanya untuk new WebAssembly.Instance sinkron (yang digunakan pada pekerja)?

Poin menarik @Pauan tentang memulai!

Ya, untuk instantiasi sinkron tampaknya berisiko - jika await diizinkan, aneh jika seseorang memanggil ekspor saat dijeda. Melarang await mungkin ada yang paling sederhana dan paling aman. (Mungkin juga dalam permulaan asinkron untuk konsistensi, sepertinya tidak ada kasus penggunaan penting yang akan mencegahnya? Perlu lebih banyak pemikiran.)

(yang digunakan pada pekerja)?

Hmm poin bagus; Saya tidak berpikir itu harus digunakan di Pekerja, tetapi karena API ini sudah ada, mungkin itu bisa mengembalikan Janji? Saya telah melihat ini sebagai pola yang muncul semi-populer untuk mengembalikan kemudian dari konstruktor berbagai perpustakaan, meskipun tidak yakin apakah itu ide yang baik untuk melakukan ini dalam API standar.

Saya setuju untuk tidak mengizinkannya di start (seperti dalam trapping) paling aman untuk saat ini, dan kami selalu dapat mengubahnya di masa mendatang dengan cara yang kompatibel ke belakang jika sesuatu berubah.

Mungkin saya melewatkan sesuatu, tetapi tidak ada diskusi tentang apa yang terjadi ketika eksekusi WASM dijeda dengan instruksi await dan janji dikembalikan ke JS, kemudian JS memanggil kembali ke WASM tanpa menunggu janji.

Apakah itu kasus penggunaan yang valid? Jika ya, maka itu dapat memungkinkan aplikasi "loop utama" untuk menerima peristiwa input tanpa menyerah ke browser secara manual. Sebaliknya mereka bisa menyerah dengan menunggu janji yang segera diselesaikan.

Bagaimana dengan pembatalan? Itu tidak diimplementasikan dalam janji JS dan ini menyebabkan beberapa masalah.

@Kangz

Mungkin saya melewatkan sesuatu, tetapi tidak ada diskusi tentang apa yang terjadi ketika eksekusi WASM dijeda dengan instruksi menunggu dan janji dikembalikan ke JS, kemudian JS memanggil kembali ke WASM tanpa menunggu janji.

Apakah itu kasus penggunaan yang valid? Jika ya, maka itu dapat memungkinkan aplikasi "loop utama" untuk menerima peristiwa input tanpa menyerah ke browser secara manual. Sebaliknya mereka bisa menyerah dengan menunggu janji yang segera diselesaikan.

Teks saat ini mungkin tidak cukup jelas tentang itu. Untuk paragraf pertama ya boleh, lihat bagian "Klarifikasi": It is ok to call into the WebAssembly instance while a pause has occurred, and multiple pause/resume events can be in flight at once.

Untuk paragraf kedua, tidak - Anda tidak bisa mendapatkan acara lebih awal, dan Anda tidak bisa membuat JS menyelesaikan Janji lebih awal dari yang seharusnya. Biarkan saya mencoba meringkas hal-hal dengan cara lain:

  • Ketika wasm berhenti pada Janji A, ia keluar kembali ke apa pun namanya, dan mengembalikan Janji B baru.
  • Wasm dilanjutkan ketika Janji A diselesaikan. Itu terjadi pada waktu normal , yang berarti semuanya normal di loop acara JS.
  • Setelah wasm dilanjutkan dan juga selesai berjalan, barulah Janji B diselesaikan.

Jadi khususnya Janji B harus diselesaikan setelah Janji A. Anda tidak bisa mendapatkan hasil Janji A lebih awal dari yang bisa diperoleh JS.

Dengan kata lain: perilaku proposal ini dapat diisi dengan poli oleh Asyncify + beberapa JS yang menggunakan Janji di sekitarnya.

@RReverser , saya rasa itu tidak sama, tetapi pertama-tama saya pikir kita perlu mengklarifikasi sesuatu (jika belum diklarifikasi, dalam hal ini saya minta maaf karena melewatkannya).

Mungkin ada beberapa panggilan dari JS ke instance wasm yang sama di tumpukan yang sama secara bersamaan. Jika await dieksekusi oleh instance, panggilan mana yang dijeda dan mengembalikan janji?

Untuk paragraf kedua, tidak - Anda tidak bisa mendapatkan acara lebih awal, dan Anda tidak bisa membuat JS menyelesaikan Janji lebih awal dari yang seharusnya.

Maaf saya rasa pertanyaan saya kurang jelas. Saat ini aplikasi "main-loop" di C++ menggunakan emscripten_set_main_loop sehingga antara setiap menjalankan fungsi frame, kontrol dikembalikan ke browser, dan input atau peristiwa lain dapat diproses.

Dengan proposal ini, sepertinya yang berikut ini berfungsi untuk menerjemahkan aplikasi "loop utama". (walaupun saya tidak tahu loop acara JS dengan baik)

int main() {
  while (true) {
    frame();
    processEvents();
  }
}

// polyfillable with ASYNCIFY!
void processEvents() {
  __builtin_await(EM_ASM(
    new Promise((resolve, reject) => {
      setTimeout(0, () => resolve());
    })
  ))
}

@Kangz Itu seharusnya berfungsi, ya (kecuali Anda memiliki masalah kecil dengan urutan argumen dalam kode setTimeout Anda plus itu bisa disederhanakan):

int main() {
  while (true) {
    frame();
    processEvents();
  }
}

// polyfillable with ASYNCIFY!
void processEvents() {
  __builtin_await(EM_ASM_WAITREF(
    return new Promise(resolve => setTimeout(resolve));
  ));
}

Mungkin ada beberapa panggilan dari JS ke instance wasm yang sama di tumpukan yang sama secara bersamaan. Jika menunggu dieksekusi oleh instance, panggilan mana yang dijeda dan mengembalikan janji?

Yang paling dalam. Itu tugas pembungkus JS untuk mengoordinasikan sisanya jika ingin melakukannya.

@Kangz Maaf, saya salah paham sebelumnya. Ya, seperti yang dikatakan @RReverser , itu akan berfungsi, dan ini adalah contoh yang baik dari kasus penggunaan yang dimaksudkan di sini!

Seperti yang Anda katakan itu polyfillable dengan Asyncify, dan sebenarnya itu setara dengan kode yang sama dengan Asyncify hari ini dengan mengganti __builtin_await dengan panggilan ke emscripten_sleep(0) (yang melakukan setTimeout(0) ) .

Terima kasih, @RReverser , atas klarifikasinya. Saya pikir akan membantu untuk mengulangi deskripsi untuk mengatakan bahwa panggilan (terbaru) ke dalam instance dijeda, daripada instance itu sendiri.

Dalam hal ini, ini terdengar hampir setara dengan menambahkan dua fungsi primitif berikut ke JS: promise-on-await(f) dan await-for-promise(p) . Yang pertama memanggil f() tetapi, jika selama eksekusi f() panggilan dilakukan ke await-for-promise(p) , alih-alih mengembalikan Janji baru yang melanjutkan eksekusi setelah p diselesaikan dan itu sendiri diselesaikan setelah eksekusi itu selesai (atau memanggil await-for-promise lagi). Jika panggilan ke await-for-promise dilakukan dalam konteks beberapa promise-on-await , maka yang terbaru mengembalikan Promise. Jika panggilan ke await-for-promise dilakukan di luar promise-on-await , maka sesuatu yang buruk akan terjadi (seperti jika kode $#$11 start instance dieksekusi await ).

Apakah itu masuk akal?

@RossTate Itu cukup dekat, ya, dan menangkap ide umum. (Tapi seperti yang Anda katakan, hanya hampir setara, karena tidak dapat digunakan untuk polyfill ini, dan tidak ada penanganan batas wasm/JS tertentu.)

Terima kasih atas saran untuk menyusun ulang teks itu. Saya menyimpan daftar catatan seperti itu dari diskusi di sini. (Saya tidak yakin apakah itu layak untuk diterapkan pada posting pertama, karena tampaknya tidak terlalu membingungkan untuk tidak mengubahnya dari waktu ke waktu?)

@RossTate Menarik... Saya suka ini! Itu membuat sifat panggilan asinkron menjadi eksplisit ( promise-on-await diperlukan untuk panggilan yang berpotensi asinkron), dan tidak memerlukan perubahan apa pun pada Wasm. Juga masuk akal (beberapa) jika Anda menghapus Wasm dari tengah -- jika promise-on-await memanggil await-for-promise secara langsung, maka ia mengembalikan Promise .

@kripken dapatkah Anda menjelaskan lebih detail tentang mengapa ini berbeda? Saya tidak begitu mengerti mengapa batas Wasm/JS penting di sini.

@binji Saya hanya bermaksud bahwa fungsi seperti itu di JS tidak akan membiarkan wasm melakukan hal serupa. Menyebut mereka sebagai impor dari wasm tidak akan berhasil. Kita masih membutuhkan cara untuk membuat wasm keluar ke perbatasan dll dengan cara yang dapat dilanjutkan, bukan?

@kripken benar, saya kira pada saat itu impor await-for-promise harus berfungsi seperti intrinsik Wasm.

Pemikiran saya adalah, alih-alih menambahkan instruksi await ke wasm, modul semacam itu malah akan mengimpor await-for-promise dan menyebutnya. Demikian pula, alih-alih mengubah fungsi yang diekspor, kode JS akan memanggilnya di dalam promise-on-await . Ini berarti bahwa JS primitif akan menangani semua pekerjaan tumpukan, termasuk tumpukan WebAssembly. Itu juga akan lebih fleksibel, misalnya jika Anda mau, Anda dapat memberikan modul panggilan balik JS yang kemudian dapat memanggil kembali ke modul dan memiliki jeda panggilan luar alih-alih klausa dalam --- semuanya tergantung pada apakah kode JS memilih untuk membungkus panggilan dalam promise-on-await atau tidak. Saya tidak berpikir Anda perlu mengubah apa pun menjadi wasm itu sendiri.

Saya akan tertarik untuk mendengar pendapat @syg tentang potensi JS primitif ini.

Oh ok, maaf - saya menganggap komentar Anda @RossTate sebagai "untuk memastikan saya mengerti, biarkan saya ulangi seperti ini, dan beri tahu saya jika itu memiliki bentuk yang benar", dan bukan saran yang konkret.

Memikirkannya, ide Anda ingin menjeda tidak hanya bingkai JS tetapi juga wasm, tetapi ada juga bingkai Host/browser. (Proposal saat ini menghindarinya dengan hanya bekerja pada batas di mana ia dipanggil.) Berikut contohnya:

myList.forEach((item) => {
  .. call something which ends up pausing ..
});

Jika forEach diimplementasikan dalam kode browser maka itu berarti menjeda frame browser. Juga penting adalah bahwa berhenti di tengah-tengah loop seperti itu, dan melanjutkan nanti, akan menjadi kekuatan baru yang dapat dilakukan JS, dan ide Anda akan memungkinkan itu untuk loop normal juga:

for (let i of something) {
  .. call something which ends up pausing ..
}

Dan semua ini mungkin memiliki interaksi spesifikasi yang aneh dengan fungsi JS async . Ini semua tampak seperti diskusi besar dengan browser dan orang-orang JS.

Tetapi juga, ini hanya menghindari penambahan await dan waitref ke dalam spesifikasi inti wasm, tetapi itu adalah tambahan kecil - karena mereka tidak melakukan apa pun dalam spesifikasi inti. Proposal saat ini sudah memiliki 99% kompleksitas di sisi JS. Dan IIUC proposal Anda menukar tambahan kecil untuk spesifikasi wasm dengan tambahan yang jauh lebih besar di sisi JS - sehingga membuat platform Web secara keseluruhan lebih kompleks, dan tidak perlu karena ini semua untuk wasm. Plus, sebenarnya ada manfaat untuk mendefinisikan await dalam spesifikasi inti wasm, yang mungkin berguna di luar Web.

Mungkin saya melewatkan sesuatu dalam saran Anda, maaf jika demikian. Secara keseluruhan, saya ingin tahu apa motivasi Anda mencoba menghindari penambahan spesifikasi inti wasm?

Saya tidak berpikir primitif itu masuk akal untuk js, dan saya pikir lebih banyak implementasi wasm daripada yang di browser bisa mendapatkan keuntungan dari ini. Saya masih penasaran mengapa pengecualian yang dapat dilanjutkan (kira-kira efek) tidak akan memenuhi kasus penggunaan ini.

Komentar saya adalah kombinasi keduanya. Pada tingkat tinggi, saya mencoba mencari tahu apakah ada cara untuk menyusun ulang proposal sebagai murni pengayaan JS API (dan juga bagaimana host lain akan berinteraksi dengan modul wasm). Latihan ini membantu menilai apakah wasm benar-benar perlu diubah dan membantu menentukan apakah benar-benar proposal tersebut secara diam-diam menambahkan primitif baru ke JS yang mungkin disetujui atau tidak disetujui oleh orang-orang JS. Yaitu, jika tidak mungkin dilakukan hanya dengan await : func (param externref) (result externref) yang diimpor, maka kemungkinan besar ini menambahkan fungsionalitas baru ke JS.

Untuk kesederhanaan perubahan wasm, masih banyak hal yang perlu dipertimbangkan seperti apa yang harus dilakukan tentang pemanggilan modul ke modul, apa yang harus dilakukan ketika fungsi yang diekspor mengembalikan nilai GC yang berisi pointer ke fungsi yang dapat mengeksekusi await setelah panggilan selesai, dan seterusnya.

Kembali ke latihan, seperti yang Anda tunjukkan, ada alasan bagus untuk hanya menangkap tumpukan wasm. Ini membawa saya kembali ke saran saya sebelumnya, meskipun sedikit direvisi dengan beberapa perspektif baru. Tambahkan fungsi WebAssembly.instantiateAsync(moduleBytes, imports, "name1", "name2") ke JS API. Misalkan moduleBytes memiliki sejumlah impor ditambah impor tambahan import "name1" "name2" (func (param externref) (result externref)) . Kemudian instantiateAsync membuat instance impor lain dari moduleBytes hanya dengan nilai yang diberikan oleh imports dan membuat instance impor tambahan dengan apa yang secara konseptual await-for-promise . Ketika fungsi yang diekspor dibuat dari instance ini, fungsi tersebut dilindungi (secara konseptual oleh promise-on-await ) sehingga ketika await-for-promise ini dipanggil, ia berjalan ke atas tumpukan untuk menemukan penjaga pertama dan kemudian menyalin konten dari tumpukan menjadi Janji baru yang kemudian segera dikembalikan. Sekarang kami memiliki primitif yang sama seperti yang saya sebutkan di atas, tetapi mereka bukan lagi kelas satu, dan pola terbatas ini memastikan bahwa hanya tumpukan wasm yang akan ditangkap. Pada saat yang sama, WebAssembly tidak perlu diubah untuk mendukung pola tersebut.

Pikiran?

@devsnek

Saya masih penasaran mengapa pengecualian yang dapat dilanjutkan (kira-kira efek) tidak akan memenuhi kasus penggunaan ini.

Mereka adalah pilihan di ruang ini, tentu saja.

Pemahaman saya dari presentasi terakhir @rossberg adalah bahwa dia awalnya ingin menempuh rute itu, tetapi kemudian berubah arah untuk melakukan pendekatan coroutine. Lihat slide dengan judul "Masalah". Setelah slide itu, coroutine dijelaskan, yang merupakan opsi lain di ruang ini. Jadi mungkin pertanyaan Anda lebih kepada @rossberg yang mungkin bisa menjelaskan?

Proposal ini difokuskan pada pemecahan masalah sinkronisasi/asinkron yang tidak memerlukan daya sebanyak pengecualian atau coroutine yang dapat dilanjutkan. Itu fokus pada interaksi internal di dalam modul wasm, sementara kami fokus pada interaksi antara modul wasm dan luar (karena di situlah masalah sinkronisasi/asinkron terjadi). Itu sebabnya kami hanya membutuhkan satu instruksi baru dalam spesifikasi wasm inti, dan hampir semua logika dalam proposal ini ada dalam spesifikasi wasm JS. Dan itu berarti Anda bisa menunggu Janji seperti ini:

call $get_promise
await
;; use it!

Kesederhanaan dalam wasm berguna untuk dirinya sendiri tetapi juga berarti sangat jelas bagi VM apa yang terjadi yang mungkin juga bermanfaat.

@RossTate

Artinya, jika tidak mungkin dilakukan hanya dengan wait yang diimpor : func (param externref) (result externref), maka kemungkinan besar ini menambahkan fungsionalitas baru ke JS.

Saya tidak mengikuti kesimpulan itu, maaf. Tapi sepertinya berputar-putar bagi saya. Jika menurut Anda proposal ini menambahkan fungsionalitas baru ke JS, mengapa tidak menunjukkannya secara langsung? (Saya sangat percaya itu tidak, tetapi saya ingin tahu apakah Anda menemukan kami melakukan kesalahan!)

Adapun kesederhanaan perubahan wasm, masih banyak hal yang perlu dipertimbangkan seperti apa yang harus dilakukan tentang panggilan modul ke modul

Apakah spesifikasi inti wasm mengatakan sesuatu tentang panggilan modul ke modul? Saya tidak ingat itu melakukannya, dan membaca sekilas bagian yang relevan sekarang saya tidak melihatnya. Tapi mungkin aku melewatkan sesuatu?

Keyakinan saya adalah bahwa penambahan spesifikasi wasm inti pada dasarnya akan mencantumkan await , katakan itu dimaksudkan untuk "menunggu sesuatu", dan hanya itu. Itu sebabnya saya menulis That's it for the core wasm spec! dalam proposal. Jika saya salah, tolong tunjukkan saya di spesifikasi inti wasm di mana kami perlu menambahkan lebih banyak.

Mari kita berspekulasi dan mengatakan bahwa suatu hari spesifikasi wasm inti akan memiliki instruksi baru untuk membuat modul wasm dan memanggil metode di dalamnya. Dalam hal ini, saya membayangkan kita akan mengatakan await hanya menjebak karena intinya adalah menunggu sesuatu di luar, di host.

Ini membawa saya kembali ke saran saya sebelumnya, meskipun sedikit direvisi dengan beberapa perspektif baru [ide baru]

Apakah ide tersebut tidak secara fungsional sama dengan paragraf kedua di Alternative approaches considered dalam proposal? Hal seperti itu bisa saja dilakukan, tapi kami jelaskan mengapa menurut kami itu kurang baik.

@kripken mengerti. untuk lebih jelasnya saya pikir await memecahkan kasus penggunaan yang disajikan dengan cara yang sangat praktis dan elegan. Saya juga agak berharap kita mungkin dapat menggunakan momentum ini untuk menyelesaikan kasus penggunaan lain juga dengan sedikit memperluas desain.

Saya pikir saran @RossTate memang terdengar sangat mirip dengan apa yang disebutkan dalam "Pendekatan alternatif dipertimbangkan". Jadi saya pikir kita harus membahas lebih detail mengapa pendekatan itu ditolak. Saya pikir kita semua bisa setuju bahwa solusi yang tidak melibatkan perubahan spesifikasi wasm akan lebih disukai, jika kita dapat membuat sisi JS bisa diterapkan. Saya mencoba memahami kerugian yang Anda berikan di bagian itu, dan mengapa mereka membuat solusi khusus JS sangat tidak dapat diterima.

Saya pikir kita semua bisa setuju bahwa solusi yang tidak melibatkan perubahan spesifikasi akan lebih disukai

Tidak! Lihat kasus penggunaan non-Web yang dibahas di sini. Tanpa await dalam spesifikasi wasm, kita akan berakhir dengan setiap platform melakukan sesuatu ad-hoc: lingkungan JS melakukan beberapa hal impor, tempat lain membuat API baru bertanda "sinkron", dll. Ekosistem wasm akan menjadi kurang konsisten, akan lebih sulit untuk memindahkan wasm dari Web ke tempat lain, dll.

Tapi ya, kita harus membuat bagian spesifikasi inti wasm sesederhana mungkin. Saya pikir ini melakukan itu? 99% logika ada di sisi JS (tetapi @RossTate tampaknya tidak setuju, dan kami masih mencoba mencari tahu - saya mengajukan pertanyaan konkret dalam tanggapan terakhir saya yang saya harap akan memajukan banyak hal).

Keyakinan saya adalah bahwa penambahan spesifikasi wasm inti pada dasarnya adalah daftar await , katakan itu dimaksudkan untuk "menunggu sesuatu", dan hanya itu.

Kecuali semantik ini dapat diformalkan lebih tepat, ini memperkenalkan ambiguitas atau perilaku yang ditentukan implementasi ke dalam spesifikasi. Kami sejauh ini menghindari itu (dengan biaya yang signifikan dalam kasus SIMD), jadi ini jelas sesuatu yang ingin saya lihat. Saya tidak berpikir proposal itu sendiri harus diubah untuk menjadikannya lebih formal, tetapi "tunggu sesuatu" harus ditulis ulang dalam terminologi yang tepat yang sudah digunakan oleh spesifikasi.

Apakah spesifikasi inti wasm mengatakan sesuatu tentang panggilan modul ke modul?

Impor instans dapat dipakai dengan ekspor instans lain. Dari apa yang saya pahami tentang JS API (dan prinsip komposisi wasm), panggilan ke impor semacam itu secara konseptual adalah panggilan langsung ke fungsi apa pun yang diekspor instance lain. Hal yang sama berlaku untuk panggilan (tidak langsung) pada nilai fungsional seperti funcref yang diteruskan di antara dua instance.

Mari kita berspekulasi dan mengatakan bahwa suatu hari spesifikasi wasm inti akan memiliki instruksi baru untuk membuat modul wasm dan memanggil metode di dalamnya. Dalam hal ini, saya membayangkan kita akan mengatakan menunggu hanya jebakan karena intinya adalah menunggu sesuatu di luar, di tuan rumah.

Berdasarkan prinsip komposisi modul yang dibahas pada pertemuan tatap muka, seharusnya tidak menjebak. Seharusnya seolah-olah hanya ada satu instance modul (tersusun) dan dieksekusi await . Artinya, await akan mengemas tumpukan hingga bingkai tumpukan JS terbaru.

Perhatikan bahwa ini menyiratkan bahwa jika f adalah nilai fungsi unary yang diekspor dari beberapa instance wasm, maka objek parameter-instansi {"some" : {"import" : f}} akan secara semantik berbeda dari {"some" : {"import" : (x) => f(x)}} karena panggilan ke yang pertama akan tetap berada di dalam tumpukan wasm sedangkan panggilan ke yang terakhir akan memasuki tumpukan JS, meskipun hanya sedikit. Sejauh ini objek parameter instantiasi ini akan dianggap setara. Saya dapat membahas mengapa itu berguna dari sudut pandang migrasi kode/interop bahasa, tetapi itu akan menjadi penyimpangan saat ini.

Apakah gagasan itu secara fungsional tidak sama dengan paragraf kedua dalam Pendekatan alternatif yang dipertimbangkan dalam proposal? Hal seperti itu bisa saja dilakukan, tapi kami jelaskan mengapa menurut kami itu kurang baik.

Maaf, saya membaca alternatif itu sebagai makna yang berbeda, tetapi itu tidak masalah sekarang kecuali untuk menjelaskan kebingungan saya. Sepertinya maksud Anda sama dengan saran saya, dalam hal ini ada baiknya membahas pro dan kontra.

Fakta bahwa proposal ini sangat ringan di sisi wasm adalah karena instruksi await tampaknya secara semantik identik dengan panggilan ke fungsi yang diimpor. Tentu saja, konvensi itu penting, seperti yang Anda tunjukkan! Tapi await bukan satu-satunya fungsi yang dimiliki; hal yang sama berlaku untuk sebagian besar fungsi yang diimpor. Dalam kasus await , menurut saya kekhawatiran tentang konvensi dapat diatasi dengan memiliki modul dengan fungsi ini memiliki klausa import "control" "await" (func (param externref) (result externref)) , dan memiliki lingkungan yang mendukung fungsi ini selalu membuat instance impor itu dengan panggilan balik yang sesuai.

Itu sepertinya memberikan solusi yang menghemat banyak pekerjaan dengan tidak mengubah wasm sambil tetap memberikan portabilitas lintas platform yang Anda cari. Tapi saya masih bekerja untuk memahami nuansa proposal, dan saya sudah melewatkan banyak sejauh ini!

Fakta bahwa proposal ini sangat ringan di sisi wasm adalah karena instruksi menunggu tampaknya secara semantik identik dengan panggilan ke fungsi yang diimpor.

FWIW di sinilah proposal ini awalnya dimulai, tetapi menggunakan intrinsik seperti itu tampaknya lebih buram bagi VM dan umumnya tidak disarankan (saya pikir @binji menyarankan untuk menjauh darinya dalam diskusi asli).

Misalnya, mengikuti argumen Anda, sesuatu seperti memory.grow atau atomic.wait juga dapat dilakukan sebagai import "control" "memory_grow" atau import "control" "atomic_wait" secara bersamaan, tetapi tidak seperti biasanya 't memberikan tingkat peluang analisis interop dan statis yang sama (baik di VM dan sisi perkakas) sebagai instruksi nyata.

Anda dapat berargumen bahwa memory.grow sebagai instruksi masih berguna untuk kasus di mana memori tidak diekspor, tetapi atomic.wait pasti dapat diimplementasikan di luar inti. Faktanya, ini sangat mirip dengan await , kecuali untuk level di mana jeda/resume terjadi dan fakta bahwa await sebagai fungsi akan membutuhkan lebih banyak keajaiban daripada atomic.wait karena harus dapat berinteraksi dengan tumpukan VM dan tidak hanya memblokir utas saat ini hingga nilai berubah.

@tlively

"tunggu sesuatu" harus ditulis ulang dalam terminologi yang tepat yang sudah digunakan oleh spesifikasi.

Pasti ya. Saya dapat menyarankan beberapa teks yang lebih spesifik sekarang jika itu akan membantu:

When an await instruction is executed on a waitref, the host environment is requested to do some work. Typically there would be a natural meaning to what that work is based on what a waitref is on a specific host (in particular, waiting for some form of host event), but from the wasm module's point of view, the semantics of an await are similar to a call to an imported host function, that is: we don't know exactly what the host will do, but at least expect to give it certain types and receive certain results; after the instruction executes, global state (the store) may change; and an exception may be thrown.

The behavior of an await from the host's perspective may be very different, however, from a call to an imported host function, and might involve something like pausing and resuming the wasm module. It is for this reason that this instruction is defined. For the instruction to be usable on a particlar host, the host would need to define the proper behavior.

Btw, perbandingan lain yang datang kepada saya saat menulis ini adalah petunjuk penyelarasan pada beban dan penyimpanan. Wasm mendukung pemuatan dan penyimpanan yang tidak selaras, sehingga petunjuknya tidak dapat mengarah ke perilaku berbeda yang dapat diamati oleh modul wasm (bahkan jika petunjuknya salah), tetapi untuk Host, petunjuk tersebut menyarankan implementasi yang sangat berbeda pada platform tertentu (yang mungkin lebih efisien). Jadi itu adalah contoh instruksi yang berbeda tanpa semantik berbeda yang dapat diamati secara internal, seperti yang dikatakan oleh spesifikasi: The alignment in load and store instructions does not affect the semantics .

@RossTate

Berdasarkan prinsip komposisi modul yang dibahas pada pertemuan tatap muka, seharusnya tidak menjebak. Seharusnya seolah-olah hanya ada satu instance modul (tersusun) dan dieksekusi menunggu. Artinya, wait akan mengemas tumpukan ke bingkai tumpukan JS terbaru.

Kedengarannya bagus, dan bagus untuk diketahui, terima kasih, saya melewatkan bagian itu.

Saya pikir ini menjelaskan kepada saya bagian dari kesalahpahaman kami. Modul => panggilan modul tidak ada di atm spesifikasi wasm, yang merupakan poin saya sebelumnya. Tapi sepertinya Anda berpikir ke depan untuk spesifikasi masa depan di mana mereka mungkin berada. Bagaimanapun, itu tidak terlihat seperti masalah di sini, karena komposisi menentukan dengan tepat bagaimana penantian harus berperilaku dalam situasi itu (yang bukan seperti yang saya sarankan sebelumnya! tetapi lebih masuk akal).

Apakah spesifikasi inti wasm mengatakan sesuatu tentang panggilan modul ke modul? Saya tidak ingat itu melakukannya, dan membaca sekilas bagian yang relevan sekarang saya tidak melihatnya. Tapi mungkin aku melewatkan sesuatu?

Ya, spesifikasi wasm inti membedakan antara fungsi yang telah diimpor dari modul wasm lain dan fungsi host (§ 4.2.6). Semantik pemanggilan fungsi (§ 4.4.7) tidak bergantung pada modul yang mendefinisikan fungsi, dan khususnya pemanggilan fungsi lintas-modul saat ini ditentukan untuk berperilaku identik dengan pemanggilan fungsi modul yang sama.

Jika await s di bawah panggilan lintas-modul didefinisikan untuk menjebak, ini kemudian akan memerlukan menentukan traversal ke atas tumpukan panggilan untuk memeriksa apakah panggilan lintas-modul ada sebelum bingkai dummy terakhir yang dibuat oleh permintaan dari host (§ 4.5.5). Ini akan menjadi komplikasi yang tidak menguntungkan dalam spesifikasi. Tetapi saya setuju dengan Ross bahwa memiliki jebakan panggilan lintas-modul akan menjadi pelanggaran komposisi, jadi saya lebih suka semantik di mana seluruh tumpukan dibekukan kembali ke permintaan terakhir dari Host. Cara paling sederhana untuk menentukannya adalah dengan membuat await mirip dengan pemanggilan fungsi host (§ 4.4.7.3), seperti yang Anda katakan, @kripken. Tetapi pemanggilan fungsi host sepenuhnya nondeterministik, jadi nama yang lebih baik untuk instruksi dari sudut pandang spesifikasi inti mungkin adalah undefined . Dan pada titik ini saya benar-benar mulai lebih memilih impor intrinsik yang akan selalu disediakan oleh platform Web (dan WASI untuk portabilitas) karena spesifikasi inti, dengan sendirinya, tidak mendapat manfaat dari memiliki instruksi IMO undefined .

Secara semantik, panggilan ke lingkungan host yang mengembalikan waitref plus await hanyalah panggilan pemblokiran, bukan?

Nilai apa yang diberikan hal ini pada penyematan non-web yang tidak memiliki lingkungan asinkron seperti yang dimiliki browser dan secara native dapat mendukung panggilan pemblokiran?

@RReverser , saya mengerti poin yang Anda buat tentang intrinsik. Ada panggilan penilaian yang terlibat dalam memutuskan kapan suatu operasi harus didefinisikan melalui fungsi yang tidak ditafsirkan versus instruksi. Saya pikir salah satu faktor dalam penilaian ini adalah untuk mempertimbangkan bagaimana berinteraksi dengan instruksi lain. memory.grow mempengaruhi perilaku instruksi memori lainnya. Saya belum sempat membaca dengan teliti proposal Utas, tetapi saya membayangkan atomic.wait memengaruhi atau dipengaruhi oleh perilaku instruksi sinkronisasi lainnya. Spesifikasi kemudian harus diperbarui untuk memformalkan interaksi ini.

Tetapi dengan await dengan sendirinya, tidak ada interaksi dengan instruksi lain. Satu-satunya interaksi adalah dengan Host, itulah sebabnya intuisi saya adalah bahwa proposal ini harus dilakukan melalui fungsi Host yang diimpor.

Saya pikir perbedaan besar antara atomic.wait dan await yang diusulkan ini adalah bahwa modul tidak dapat dimasukkan kembali dengan atomic.wait . Agen ditangguhkan secara keseluruhan.

@kripken :

Pemahaman saya dari presentasi terakhir @rossberg adalah bahwa dia awalnya ingin menempuh rute itu, tetapi kemudian berubah arah untuk melakukan pendekatan coroutine. Lihat slide dengan judul "Masalah". Setelah slide itu, coroutine dijelaskan, yang merupakan opsi lain di ruang ini. Jadi mungkin pertanyaan Anda lebih kepada @rossberg yang mungkin bisa menjelaskan?

Ya, jadi faktorisasi coroutine-ish dapat dianggap sebagai generalisasi dari desain pengecualian yang dapat dilanjutkan sebelumnya. Itu masih memiliki gagasan yang sama tentang peristiwa/pengecualian yang dapat dilanjutkan, tetapi instruksi try didekomposisi menjadi primitif yang lebih kecil -- yang membuat semantik lebih sederhana dan model biaya lebih eksplisit. Ini juga agak lebih ekspresif.

Tujuannya tetap adalah bahwa ini dapat mengekspresikan semua abstraksi kontrol yang relevan, dan async adalah salah satu kasus penggunaan yang memotivasi. Untuk interop dengan JS async, JS API mungkin dapat menyediakan acara await yang telah ditentukan sebelumnya (membawa janji JS sebagai externref) yang dapat diimpor oleh modul Wasm dan throw untuk ditangguhkan. Tentu saja, ada banyak detail yang harus disempurnakan, tetapi pada prinsipnya itu mungkin.

Adapun proposal saat ini, saya masih mencoba untuk membungkus kepala saya di sekitarnya. :)

Secara khusus, tampaknya mengizinkan await di fungsi Wasm lama apa pun, apakah saya membacanya dengan benar? Jika demikian, itu sangat berbeda dari JS, yang memungkinkan await hanya dalam fungsi async. Dan itu adalah kendala yang sangat sentral, karena memungkinkan mesin untuk mengkompilasi await dengan transformasi _local_ dari satu fungsi (async)!

Tanpa batasan itu, mesin akan perlu melakukan transformasi program _global_ (seperti yang seharusnya dilakukan Asyncify), di mana setiap panggilan berpotensi menjadi jauh lebih mahal (Anda umumnya tidak dapat mengetahui apakah beberapa panggilan mungkin mencapai penantian). Atau, secara setara, mesin harus dapat membuat banyak tumpukan dan beralih di antara mereka!

Sekarang, inilah fitur yang coba diperkenalkan oleh ide penangan coroutine / efek ke Wasm. Tapi jelas, ini adalah tambahan yang sangat non-sepele untuk platform dan model eksekusinya, komplikasi yang JS sangat berhati-hati untuk menghindari abstraksi kontrolnya (seperti async dan generator).

@rossberg

Secara khusus, tampaknya memungkinkan menunggu di fungsi Wasm lama apa pun, apakah saya membacanya dengan benar? Jika demikian, itu sangat berbeda dari JS, yang memungkinkan menunggu hanya di fungsi async.

Ya, model di sini sangat berbeda. JS menunggu adalah berdasarkan fungsi, sementara proposal ini menunggu seluruh instance wasm (karena tujuannya adalah untuk menyelesaikan ketidakcocokan sinkronisasi/asinkron antara JS dan wasm, yaitu antara JS dan wasm). Juga JS menunggu adalah untuk kode tulisan tangan, sementara ini untuk mengaktifkan port dari kode yang dikompilasi.

Dan itu kendala yang sangat sentral, karena memungkinkan mesin untuk mengkompilasi menunggu dengan transformasi lokal dari fungsi tunggal (async)! Tanpa batasan itu, mesin akan perlu melakukan transformasi program global (seperti yang seharusnya dilakukan Asyncify), di mana setiap panggilan berpotensi menjadi jauh lebih mahal (Anda umumnya tidak dapat mengetahui apakah beberapa panggilan mungkin mencapai penantian). Atau, secara setara, mesin harus dapat membuat banyak tumpukan dan beralih di antara mereka!

Jelas transformasi program global tidak dimaksudkan di sini! Maaf jika itu tidak jelas.

Seperti disebutkan dalam proposal, beralih antar tumpukan adalah salah satu opsi implementasi yang memungkinkan, tetapi perhatikan bahwa ini tidak sama dengan beralih tumpukan gaya coroutine:

  • Hanya seluruh instance wasm yang dapat dijeda. Ini bukan untuk pengalihan tumpukan di dalam modul. (Khususnya, itu sebabnya proposal ini tidak dapat memiliki tambahan pada spesifikasi wasm inti dan sepenuhnya berada di sisi JS wasm; sejauh ini beberapa orang lebih suka itu, dan saya pikir cara mana pun bisa berhasil.)
  • Coroutine mendeklarasikan tumpukan secara eksplisit, menunggu tidak.
  • tumpukan menunggu hanya dapat dilanjutkan sekali, tidak ada forking/pengembalian lebih dari sekali (tidak yakin apakah Anda akan memilikinya dalam proposal Anda atau tidak?).
  • Model kinerja sangat berbeda di sini. menunggu akan menunggu Janji di JS, yang sudah memiliki overhead & latensi minimal. Jadi tidak apa-apa jika implementasi memiliki beberapa overhead ketika kami benar-benar berhenti, dan kami kurang peduli daripada coroutine mungkin.

Mengingat faktor-faktor tersebut, dan bahwa perilaku yang dapat diamati dari proposal ini adalah bahwa seluruh instance wasm dijeda, mungkin ada berbagai cara untuk mengimplementasikannya. Misalnya, di luar Web dalam VM yang menjalankan instance wasm tunggal, itu benar-benar dapat menjalankan loop acaranya hingga waktunya untuk melanjutkan wasm. Di Web, satu pendekatan implementasi mungkin: ketika suatu penantian terjadi, salin seluruh tumpukan wasm, dari posisi saat ini ke tempat kita memanggil ke dalam wasm; simpan itu di samping; untuk melanjutkan, salin kembali dan lanjutkan dari sana. Mungkin juga ada pendekatan atau variasi lain dalam hal ini (beberapa mungkin tanpa penyalinan, tetapi sekali lagi, menghindari overhead penyalinan sebenarnya tidak penting di sini!).

Maaf untuk posting yang panjang, dan beberapa pengulangan dari teks proposal itu sendiri, tetapi saya harap ini membantu memperjelas beberapa poin yang Anda maksud?

Saya pikir ada banyak hal untuk dibahas di sini dalam hal implementasi. Sejauh ini komentar @acfoltzer tentang Lucet sangat menggembirakan!

Hanya untuk memperjelas beberapa frasa dalam komentar terbaru @kripken , bukan seluruh instance wasm yang berhenti. Itu hanya panggilan terbaru dari bingkai host ke wasm pada tumpukan yang dijeda, dan kemudian bingkai host dikembalikan sebagai janji yang sesuai (atau analog yang sesuai untuk host). Lihat di sini untuk klarifikasi sebelumnya yang relevan.

Hm, saya tidak melihat bagaimana itu membuat perbedaan. Saat Anda menunggu di suatu tempat jauh di dalam Wasm, Anda harus menangkap semua tumpukan panggilan dari setidaknya entri host ke titik itu. Dan Anda dapat mempertahankan penangguhan tersebut (yaitu, segmen tumpukan itu) selama yang Anda inginkan, sambil melakukan panggilan lain dari atas atau membuat lebih banyak penangguhan. Dan Anda dapat melanjutkan dari tempat lain (saya pikir?). Bukankah itu membutuhkan semua mesin implementasi dari kelanjutan yang dibatasi? Hanya bahwa prompt diatur pada entri Wasm alih-alih oleh konstruksi terpisah.

@rossberg

Itu mungkin benar pada beberapa VM, ya. Jika menunggu dan coroutine akhirnya membutuhkan pekerjaan VM yang sama persis, maka setidaknya tidak diperlukan pekerjaan tambahan. Dalam hal ini, manfaat dari proposal menunggu adalah integrasi JS yang nyaman.

Saya pikir Anda bisa mendapatkan integrasi JS yang nyaman tanpa transformasi seluruh program jika Anda tidak mengizinkan modul untuk dimasukkan kembali.

Saya pikir Anda bisa mendapatkan integrasi JS yang nyaman tanpa transformasi seluruh program jika Anda tidak mengizinkan modul untuk dimasukkan kembali.

Ini terdengar cara yang lebih mudah untuk menyelesaikannya, tetapi itu akan membutuhkan pemblokiran modul apa pun yang dikunjungi di tumpukan panggilan (atau sebagai langkah pertama, semua modul WebAssembly).

Ini terdengar cara yang lebih mudah untuk menyelesaikannya, tetapi itu akan membutuhkan pemblokiran modul apa pun yang dikunjungi di tumpukan panggilan (atau sebagai langkah pertama, semua modul WebAssembly).

Benar, seperti atomic.wait .

@taralx

Saya pikir Anda bisa mendapatkan integrasi JS yang nyaman tanpa transformasi seluruh program jika Anda tidak mengizinkan modul untuk dimasukkan kembali.

Di satu sisi, entri ulang dapat berguna, misalnya, mesin game dapat mengunduh file dan tidak ingin UI sepenuhnya dijeda saat melakukannya (Asyncify mengizinkannya hari ini). Tetapi di sisi lain, mungkin entri ulang dapat dilarang tetapi aplikasi dapat membuat beberapa instance dari modul yang sama untuk itu (semua mengimpor memori yang sama, global yang dapat diubah, dll.?), jadi entri ulang akan menjadi panggilan ke contoh lain. Saya pikir kita dapat membuatnya berfungsi di rantai alat (akan ada batas efektif pada jumlah entri ulang yang aktif sekaligus - sama dengan jumlah instance - yang tampaknya baik-baik saja).

Jadi, jika penyederhanaan Anda akan membantu VM, itu pasti layak dipertimbangkan!

(Perhatikan bahwa seperti yang telah dibahas sebelumnya, saya tidak berpikir kita memerlukan transformasi program secara keseluruhan di sini dengan salah satu opsi yang sedang dibahas. Anda hanya perlu bahwa jika Anda berada dalam situasi yang buruk, Asyncify berada, di mana hanya itu yang dapat Anda lakukan di level toolchain. Untuk menunggu, dalam kasus terburuk seperti yang dibahas dengan @rossberg Anda dapat melakukan apa yang akan dilakukan proposal coroutine secara internal. Tetapi ide Anda berpotensi sangat menarik jika membuat segalanya lebih sederhana dari itu!)

Di satu sisi, entri ulang dapat berguna, misalnya, mesin game dapat mengunduh file dan tidak ingin UI sepenuhnya dijeda saat melakukannya (Asyncify mengizinkannya hari ini).

Saya tidak yakin ini adalah fitur suara sekalipun. Sepertinya saya ini akan memperkenalkan _unexpected concurrency_ dalam aplikasi. Aplikasi asli yang memuat aset saat rendering akan menggunakan 2 utas secara internal, dan setiap utas akan dipetakan ke WebWorker + SharedArrayBuffer. Jika suatu aplikasi menggunakan utas maka itu juga bisa menggunakan primitif Web sinkron dari WebWorkers (sebagaimana diizinkan, setidaknya dalam beberapa kasus). Jika tidak, selalu mungkin untuk memetakan operasi asinkron di utas utama ke operasi pemblokiran di pekerja menggunakan Atomics.wait (misalnya).

Saya ingin tahu apakah seluruh kasus penggunaan belum diselesaikan dengan multithreading secara umum. Dengan menggunakan primitif pemblokiran di pekerja, seluruh tumpukan (JS/Wasm/asli browser) dipertahankan yang tampaknya jauh lebih sederhana dan kuat.

Dengan menggunakan primitif pemblokiran di pekerja, seluruh tumpukan (JS/Wasm/asli browser) dipertahankan yang tampaknya jauh lebih sederhana dan kuat.

Itu sebenarnya implementasi alternatif lain dari pembungkus Asyncify JS mandiri yang telah saya coba, tetapi, sementara itu memecahkan masalah ukuran kode, overhead kinerja bahkan jauh lebih tinggi daripada Asyncify saat ini yang menggunakan transformasi Wasm.

@alexp-sssup

Tampaknya bagi saya ini akan memperkenalkan konkurensi yang tidak terduga dalam aplikasi.

Jelas, ya - itu perlu dilakukan dengan sangat hati-hati, dan dapat merusak barang-barang. Kami memiliki pengalaman yang beragam dengan ini menggunakan Asyncify, baik dan buruk (misalnya kasus penggunaan yang valid: file diunduh di JS, dan JS memanggil wasm ke malloc beberapa ruang untuk menyalinnya, sebelum melanjutkan). Tetapi bagaimanapun juga, masuk kembali bukanlah bagian penting dari proposal ini.

Untuk menambah apa yang @RReverser katakan, masalah lain dengan utas adalah bahwa dukungan untuk mereka tidak dan tidak akan universal. Tapi menunggu bisa di mana-mana.

Dalam bahasa lain di mana async/await telah diperkenalkan, re-entry sangat penting. Intinya adalah bahwa peristiwa lain dapat terjadi saat seseorang (a) menunggu. Tampaknya bagi saya bahwa masuk kembali cukup penting.

Lebih jauh lagi, bukankah benar bahwa setiap kali sebuah modul membuat panggilan ke fungsi eksternal, ia harus mengasumsikan bahwa modul itu dapat dimasukkan kembali melalui salah satu ekspornya (dalam contoh di atas, bahkan tanpa menunggu apa pun, panggilan apa pun ke dan eksternal fungsi gratis (tidak ada permainan kata-kata) untuk memanggil malloc).

aplikasi dapat membuat beberapa instance dari modul yang sama untuk itu (semua mengimpor memori yang sama, global yang dapat diubah, dll.?), jadi entri ulang akan menjadi panggilan ke instance lain

Hanya untuk memori bersama modul. Memori lainnya harus di-instantiate ulang, yang penting untuk menghindari satu operasi menginjak-injak operasi lain dalam perubahan penerbangan.

Saya perhatikan bahwa versi non-reentrant ini dapat diisi ulang pada setiap penyematan dengan dukungan utas, jika seseorang ingin bermain dengannya dan melihat betapa bergunanya itu.

Saya perhatikan bahwa versi non-reentrant ini dapat diisi ulang pada setiap penyematan dengan dukungan utas, jika seseorang ingin bermain dengannya dan melihat betapa bergunanya itu.

Seperti disebutkan di atas, itu adalah sesuatu yang sudah kami mainkan, tetapi dibuang karena membawa kinerja yang lebih buruk daripada solusi saat ini, tidak didukung secara universal, dan selain itu membuatnya sangat sulit untuk membagikan WebAssembly.Global atau WebAssembly.Table dengan utas utama tanpa peretasan tambahan, menjadikannya pilihan yang buruk untuk polyfill transparan.

Solusi saat ini yang menulis ulang modul Wasm tidak mengalami masalah ini, tetapi memiliki biaya ukuran file yang signifikan.

Dengan demikian, tidak satu pun dari ini yang bagus untuk aplikasi dunia nyata yang besar, yang memotivasi kami untuk melihat ke dalam dukungan asli untuk integrasi asinkron seperti yang dijelaskan di sini.

kinerja yang lebih buruk

Apakah Anda memiliki semacam patokan?

Ya, saya dapat membagikannya ketika saya kembali bekerja pada hari Selasa (atau, lebih mungkin, Rabu), atau cukup mudah untuk menyiapkan satu yang hanya memanggil untuk mengosongkan fungsi JS async sendiri.

Terima kasih. Saya bisa membuat microbenchmark, tetapi itu tidak akan terlalu instruktif.

Oh ya, milik saya juga merupakan microbenchmark karena kami hanya tertarik pada perbandingan overhead.

Masalah dengan microbenchmark adalah kita tidak tahu berapa banyak latensi yang dapat diterima oleh aplikasi nyata. Jika dibutuhkan tambahan 1 ms, apakah itu benar-benar masalah jika aplikasi hanya melakukan operasi menunggu pada kecepatan 1/s, misalnya?

Saya pikir fokus pada kecepatan pendekatan berbasis atom mungkin menjadi gangguan. Seperti yang disebutkan sebelumnya, atom tidak dan tidak akan berfungsi di mana-mana (karena COOP/COEP) dan juga hanya seorang pekerja yang dapat menggunakan pendekatan atom karena utas utama tidak dapat memblokir. Itu ide yang bagus, tetapi untuk solusi universal kita membutuhkan sesuatu seperti Await.

Saya tidak menyarankannya sebagai solusi jangka panjang. Saya menyarankan polyfill yang menggunakannya dapat digunakan untuk melihat apakah solusi non-reentrant akan bekerja untuk orang-orang.

@taralx Oh, oke, sekarang saya mengerti, terima kasih.

@taralx :

Saya pikir Anda bisa mendapatkan integrasi JS yang nyaman tanpa transformasi seluruh program jika Anda tidak mengizinkan modul untuk dimasukkan kembali.

Itu akan buruk. Ini berarti bahwa menggabungkan beberapa modul dapat merusak perilakunya. Itu akan menjadi antitesis terhadap modularitas.

Sebagai prinsip desain umum, perilaku operasional tidak boleh bergantung pada batasan modul (selain pelingkupan sederhana). Modul hanyalah mekanisme pengelompokan dan pelingkupan di Wasm, dan Anda ingin mempertahankan kemampuan untuk mengelompokkan kembali hal-hal (tautkan/gabungkan/pisahkan modul) tanpa itu mengubah perilaku program.

@rossberg : ini dapat digeneralisasikan sebagai memblokir akses ke modul Wasm apa pun, seperti yang diusulkan sebelumnya. Tapi kemudian itu mungkin terlalu membatasi.

Itu akan buruk. Ini berarti bahwa menggabungkan beberapa modul dapat merusak perilakunya. Itu akan menjadi antitesis terhadap modularitas.

Itulah maksud saya dengan argumen polyfilling - atomic.wait tidak merusak modularitas, jadi ini juga tidak boleh.

@taralx , atomic.wait mereferensikan lokasi tertentu dalam memori tertentu. Memori dan lokasi mana yang akan digunakan oleh pemblokiran await , dan bagaimana cara mengontrol modul mana yang berbagi memori itu?

@rossberg dapatkah Anda menguraikan skenario yang menurut Anda ini rusak? Saya menduga kami memiliki ide yang berbeda tentang bagaimana versi non-reentrant akan bekerja.

@taralx , pertimbangkan untuk memuat dua modul A dan B, masing-masing menyediakan beberapa fungsi ekspor, katakanlah A.f dan B.g . Keduanya mungkin melakukan await saat dipanggil. Dua potong kode klien masing-masing melewati salah satu fungsi ini, dan mereka memanggilnya secara independen. Mereka tidak mengganggu atau menghalangi satu sama lain. Kemudian seseorang menggabungkan atau memfaktorkan ulang A dan B menjadi C, tanpa mengubah apa pun tentang kodenya. Tiba-tiba kedua bagian kode klien dapat mulai memblokir satu sama lain secara tidak terduga. Aksi seram di kejauhan melalui status bersama yang tersembunyi.

Itu masuk akal. Tetapi mengizinkan masuk kembali berisiko konkurensi dalam modul yang tidak mengharapkannya, jadi itu adalah tindakan yang menakutkan di kejauhan.

Tapi modul sudah bisa masuk kembali, bukan? Setiap kali modul melakukan panggilan impor, kode eksternal dapat memasukkan kembali modul yang dapat mengubah status global sebelum kembali. Saya tidak dapat melihat bagaimana masuk kembali selama penantian yang diusulkan lebih menakutkan atau bersamaan daripada memanggil fungsi yang diimpor. Mungkin aku kehilangan sesuatu?

(diedit)

Hm, ya. Oke, jadi fungsi yang diimpor bisa masuk kembali ke modul. Saya jelas perlu berpikir lebih keras tentang ini.

Ketika kode sedang berjalan, dan memanggil suatu fungsi, ada dua kemungkinan: Ia tahu bahwa fungsi itu tidak akan memanggil hal-hal acak, atau fungsi mungkin memanggil hal-hal acak. Dalam kasus terakhir, masuk kembali selalu memungkinkan. Aturan yang sama berlaku untuk await .

(mengedit komentar saya di atas)

Terima kasih semuanya untuk diskusi sejauh ini!

Untuk meringkas, sepertinya ada minat umum di sini, tetapi ada pertanyaan terbuka besar seperti apakah ini harus 100% di sisi JS atau hanya 99% - sepertinya yang pertama akan menghilangkan kekhawatiran utama yang dimiliki beberapa orang, dan itu akan baik-baik saja untuk kasus Web, jadi itu mungkin baik-baik saja. Pertanyaan terbuka besar lainnya adalah seberapa layak hal ini dilakukan di VM yang kami perlukan lebih banyak info.

Saya akan menyarankan item agenda untuk pertemuan CG berikutnya dalam 2 minggu untuk membahas proposal ini dan mempertimbangkannya untuk tahap 1, yang berarti membuka repo dan membahas pertanyaan terbuka dalam masalah terpisah secara lebih rinci di sana. (Saya percaya itu proses yang benar, tapi tolong perbaiki saya jika saya salah.)

Hanya untuk FYI
Kami akan menyusun proposal peralihan tumpukan penuh dalam hal serupa
jangka waktu. Saya merasa itu mungkin membuat varian kasus khusus Anda diperdebatkan -
Bagaimana menurut anda?
Fransiskus

Pada Kamis, 28 Mei 2020 pukul 15:51 Alon [email protected] menulis:

Terima kasih semuanya untuk diskusi sejauh ini!

Untuk meringkas, sepertinya ada minat umum di sini, tapi ada
pertanyaan terbuka besar seperti apakah ini harus 100% di sisi JS atau hanya
99% - sepertinya yang pertama akan menghilangkan kekhawatiran utama beberapa orang
miliki, dan itu akan baik-baik saja untuk kasus Web, jadi itu mungkin baik-baik saja.
Pertanyaan terbuka besar lainnya adalah seberapa layak ini dilakukan di VM yang
kami membutuhkan lebih banyak info tentang.

Saya akan menyarankan item agenda untuk pertemuan CG berikutnya dalam 2 minggu untuk dibahas
proposal ini dan pertimbangkan untuk tahap 1, yang berarti membuka repo
dan mendiskusikan pertanyaan terbuka dalam masalah terpisah secara lebih rinci di sana.
(Saya percaya itu proses yang benar, tapi tolong perbaiki saya jika saya salah.)


Anda menerima ini karena Anda berlangganan utas ini.
Balas email ini secara langsung, lihat di GitHub
https://github.com/WebAssembly/design/issues/1345#issuecomment-635649331 ,
atau berhenti berlangganan
https://github.com/notifications/unsubscribe-auth/AAQAXUCLZ4CJVQYEUBK23BLRT3TFLANCNFSM4NEJW2PQ
.

>

Francis McCabe
SW

@fgmccabe

Kita harus mendiskusikan itu dengan pasti.

Namun secara umum, kecuali proposal Anda berfokus pada sisi JS, saya kira itu tidak akan membuat yang satu ini diperdebatkan (yaitu 99% -100% di sisi JS).

Sekarang diskusi tentang detail implementasi telah selesai, saya ingin mengangkat kembali kekhawatiran tingkat yang lebih tinggi yang saya ungkapkan sebelumnya tetapi dibatalkan demi diskusi satu per satu.

Sebuah program terdiri dari banyak komponen. Dari perspektif rekayasa perangkat lunak, penting untuk memisahkan komponen menjadi beberapa bagian atau menggabungkan komponen bersama-sama tidak secara signifikan mengubah perilaku program. Ini adalah alasan di balik prinsip komposisi modul yang dibahas pada pertemuan CG tatap muka terakhir, dan ini tersirat dalam desain banyak bahasa.

Dalam kasus program web, sekarang dengan WebAssembly komponen yang berbeda ini bahkan dapat ditulis dalam bahasa yang berbeda: JS atau wasm. Faktanya, banyak komponen dapat ditulis dengan baik dalam kedua bahasa tersebut; Saya akan menyebutnya sebagai komponen "ambivalen". Saat ini, sebagian besar komponen ambivalen ditulis dalam JS, tetapi saya membayangkan kita semua berharap bahwa semakin banyak komponen yang akan ditulis ulang menjadi wasm. Untuk memfasilitasi "migrasi kode" ini, kita harus mencoba memastikan bahwa menulis ulang komponen dengan cara ini tidak mengubah cara interaksinya dengan lingkungan. Sebagai contoh mainan, apakah komponen program "terapkan" tertentu (f, x) => f(x) ditulis dalam JS atau dalam wasm seharusnya tidak mempengaruhi perilaku program secara keseluruhan. Ini adalah prinsip migrasi kode.

Sayangnya, semua varian dari proposal ini tampaknya melanggar program komposisi modul atau prinsip migrasi kode. Yang pertama dilanggar ketika await menangkap tumpukan ke tempat modul wasm saat ini paling baru dimasukkan, karena batas ini berubah saat modul dipisah atau digabungkan bersama. Yang terakhir dilanggar ketika await menangkap tumpukan ke tempat wasm terakhir dimasukkan, karena batas ini berubah saat kode dimigrasikan dari JS ke wasm (sehingga memigrasikan sesuatu yang sederhana seperti (f, x) => f(x) dari JS ke wasm dapat secara signifikan mengubah perilaku program secara keseluruhan).

Saya tidak berpikir pelanggaran ini disebabkan oleh pilihan desain yang buruk dari proposal ini. Sebaliknya, masalahnya tampaknya adalah bahwa proposal ini mencoba untuk menghindari secara tidak langsung membuat JS menjadi lebih kuat, dan tujuan itu memaksanya untuk memaksakan batasan buatan yang melanggar prinsip-prinsip ini. Saya benar-benar memahami tujuan itu, tetapi saya menduga masalah ini akan semakin sering muncul: menambahkan fungsionalitas ke WebAssembly dengan cara yang menghormati prinsip-prinsip ini seringkali memerlukan penambahan fungsionalitas secara tidak langsung ke JS karena JS menjadi bahasa penyematan. Preferensi saya adalah mengatasi masalah itu secara langsung (yang saya benar-benar tidak tahu bagaimana menyelesaikannya). Jika bukan itu, maka preferensi sekunder saya adalah membuat perubahan ini hanya di JS API, karena JS-lah yang merupakan faktor pembatas di sini, daripada menambahkan instruksi ke WebAssembly yang tidak memiliki interpretasi untuk wasm.

Saya tidak berpikir pelanggaran ini disebabkan oleh pilihan desain yang buruk dari proposal ini. Sebaliknya, masalahnya tampaknya proposal ini mencoba untuk menghindari secara tidak langsung membuat JS menjadi lebih kuat

Itu penting, tetapi itu bukan alasan utama untuk desain di sini.

Alasan utama untuk desain ini adalah bahwa meskipun saya sepenuhnya setuju bahwa prinsip komposisi masuk akal untuk wasm , masalah mendasar yang kami miliki di Web adalah bahwa sebenarnya JS dan wasm tidak setara dalam praktiknya. Kami memiliki JS tulisan tangan yang asinkron dan porting wasm yang sinkron. Dengan kata lain, batas di antara mereka sebenarnya adalah masalah yang sedang kita coba atasi. Secara keseluruhan saya tidak yakin saya setuju prinsip komposisi harus diterapkan pada wasm dan JS (tapi mungkin harus, bisa menjadi perdebatan yang menarik).

Saya berharap untuk melakukan lebih banyak diskusi secara publik di sini, tetapi untuk menghemat waktu, saya menghubungi beberapa pelaksana VM secara langsung, karena sejauh ini hanya sedikit yang terlibat di sini. Mengingat umpan balik mereka bersama dengan diskusi di sini, sayangnya saya pikir kita harus menghentikan proposal ini.

Await memiliki perilaku yang dapat diamati jauh lebih sederhana daripada coroutine umum atau peralihan tumpukan, tetapi orang-orang VM yang saya ajak bicara setuju dengan @rossberg bahwa VM bekerja pada akhirnya mungkin akan serupa untuk keduanya. Dan setidaknya beberapa orang VM percaya bahwa kami akan mendapatkan coroutine atau peralihan tumpukan, dan bahwa kami dapat mendukung kasus penggunaan menunggu menggunakan itu. Itu berarti membuat coroutine/tumpukan baru pada setiap panggilan ke wasm (tidak seperti dengan proposal ini), tetapi setidaknya beberapa orang VM berpikir itu bisa dibuat cukup cepat.

Selain kurangnya minat dari orang-orang VM, kami memiliki beberapa keberatan yang kuat terhadap proposal ini dari @fgmccabe dan @RossTate , seperti yang dibahas di atas. Kami tidak setuju pada beberapa hal tetapi saya menghargai sudut pandang itu, dan waktu yang digunakan untuk menjelaskannya.

Kesimpulannya, secara keseluruhan rasanya akan membuang-buang waktu semua orang untuk mencoba bergerak maju di sini. Tapi terima kasih untuk semua orang yang berpartisipasi dalam diskusi! Dan semoga setidaknya ini memotivasi untuk memprioritaskan coroutine/stack switching.

Perhatikan bahwa bagian JS dari proposal ini mungkin relevan di masa mendatang, karena JS gula pada dasarnya untuk integrasi Promise yang nyaman. Kita harus menunggu peralihan tumpukan atau coroutine dan melihat apakah ini bisa bekerja di atas itu. Tapi saya pikir tidak ada gunanya membiarkan masalah ini tetap terbuka, jadi tutup.

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

frehberg picture frehberg  ·  6Komentar

aaabbbcccddd00001111 picture aaabbbcccddd00001111  ·  3Komentar

cretz picture cretz  ·  5Komentar

konsoletyper picture konsoletyper  ·  6Komentar

dpw picture dpw  ·  3Komentar