Eksekusi strategi bersamaan dengan dukungan multithreading JavaScript

Penulis:Lydia, Dibuat: 2023-03-07 15:12:04, Diperbarui: 2023-09-18 20:04:21

img

Biarkan program strategi dijalankan secara bersamaan, dan tambahkan dukungan multi-thread di bagian bawah sistem ke strategi JavaScript

Ketika mengembangkan strategi di FMZ menggunakan bahasa JavaScript, karena arsitektur strategi disurvei.exchange.Gofungsi digunakan untuk membuat panggilan serentak ke beberapa antarmuka, sehingga memenuhi persyaratan beberapa skenario serentak. tapi jika Anda ingin membuat thread tunggal untuk melakukan serangkaian operasi, itu tidak mungkin. misalnya seperti bahasa Python, menggunakanthreadingperpustakaan untuk melakukan beberapa desain paralel.

Berdasarkan persyaratan ini, platform FMZ telah memperbarui lapisan bawah sistem. dukungan multithreading sejati juga telah ditambahkan ke bahasa JavaScript.

  • Buat thread untuk mengeksekusi fungsi kustom secara bersamaan.
  • Komunikasi antar benang.
  • Variabel yang disimpan di antara thread bersama.
  • Tunggu thread selesai menjalankan untuk merebut kembali sumber daya dan mengembalikan hasil eksekusi.
  • Dengan paksa mengakhiri benang dan merebut kembali sumber daya.
  • Dapatkan ID thread saat ini dalam fungsi eksekusi thread bersamaan.

Selanjutnya, saya akan membawa Anda untuk memahami setiap fungsi satu per satu.

Buat thread untuk menjalankan fungsi kustom secara bersamaan

Peraturan__Threadfungsi dapat membuat thread dan mengeksekusi fungsi secara bersamaan.func1, apa yang dilakukanfunc1Untuk melihat proses akumulasi secara bertahap, kita menggunakan loop for dalam fungsi func1 untuk berhenti setiap kali (fungsi Sleep digunakan untuk tidur selama sejumlah milidetik) untuk periode waktu tertentu.

function func1(sleepMilliseconds) {
    var sum = 0 
    for (var i = 0 ; i < 10 ; i++) {
        sum += i 
        Sleep(sleepMilliseconds)
        Log("sum:", sum)
    }
    
    return sum
}

function main() {
    // Use the __Thread function to create a thread concurrently, and the parameter 200 is the parameter of the func1 function,
    // If the func1 function has multiple parameters, here we pass the corresponding parameters.
    var thread1Id = __Thread(func1, 200)
    
    // Here we need to wait for the execution result of the thread whose thread Id is thread1Id, otherwise all threads will be released directly after the main function is executed.
    var ret = __threadJoin(thread1Id)
    Log("ret:", ret)
}

Dalam aplikasi praktis, kita dapat membuat permintaan http secara bersamaan seperti ini:

function main() {
    let threads = [
        "https://www.baidu.com",
        "https://www.163.com"
    ].map(function(url) {
        return __Thread(function(url) {
            Log("GET", url)
            return HttpQuery(url)
        }, url)
    })
    threads.forEach(function(tid) {
        Log(__threadJoin(tid))
    })
}

Tunggu hingga akhir thread eksekusi untuk merebut kembali sumber daya dan mengembalikan hasil eksekusi

Dalam contoh di atas, kami menggunakan__threadJoinfungsi dalam fungsi utama akhirnya untuk menunggu thread bersamaan untuk menyelesaikan pelaksanaan.retmenerima nilai kembali dari__threadJoinfungsi, dan kita mencetak nilai kembali, kita dapat mengamati hasil spesifik dari pelaksanaan thread bersamaan.

// id: thread ID, terminated: whether it was forced to stop, elapsed: time-consuming (nanoseconds), ret: the return value of the thread execution function
ret: {"id":1,"terminated":false,"elapsed":2004884301,"ret":45}

Hentikan thread dengan paksa dan dapatkan kembali sumber daya

function func1(sleepMilliseconds) {
    var sum = 0 
    for (var i = 0 ; i < 10 ; i++) {
        sum += i 
        Sleep(sleepMilliseconds)
        Log("sum:", sum)
    }
    
    return sum
}

function main() {
    var thread1Id = __Thread(func1, 200)
    Sleep(1000)
    retThreadTerminate = __threadTerminate(thread1Id)
    Log(retThreadTerminate)   // true
}

Kami masih menggunakan contoh saat ini, setelah membuat thread, Anda dapat memaksa mengakhiri eksekusi thread setelah menunggu selama 1 detik.

Komunikasi antar thread

Komunikasi antar thread terutama menggunakan__threadPostMessageFungsi dan__threadPeekMessageMari kita lihat contoh sederhana berikut:

function func1() {
    var id = __threadId()
    while (true) {
        var postMsg = "Message from thread function func1" with "from id:" + id + 
        __threadPostMessage(0, postMsg)              // Send a message to the main thread
        var peekMsg = __threadPeekMessage(0)         // Receive messages from the main thread
        Log(peekMsg)
        Sleep(5000)
    }
}

function main() {
    var threadId = __Thread(func1)
    
    while (true) {
        var postMsg = "Messages from the main function of the main thread"
        __threadPostMessage(threadId, postMsg)
        var peekMsg = __threadPeekMessage(threadId)
        Log(peekMsg, "#FF0000")                     // #FF0000 , Set the log to red for distinction
        Sleep(5000)
    }
}

Peraturan__threadPostMessagefungsi digunakan untuk mengirim pesan ke thread. Parameter pertama adalah ID thread tertentu untuk dikirim ke, dan parameter kedua adalah pesan yang akan dikirim, yang dapat menjadi string, nilai, array, atau objek JSON dan sebagainya. Pesan dapat dikirim ke thread utama dalam fungsi thread paralel, dan ID thread utama didefinisikan sebagai 0.

Peraturan__threadPeekMessagefunction digunakan untuk memantau pesan yang dikirim oleh thread tertentu. Parameter pertama adalah untuk memantau ID spesifik thread. Parameter kedua dapat mengatur waktu timeout (dalam milidetik), atau dapat diatur menjadi -1, yang berarti memblokir, dan tidak akan kembali sampai ada pesan. Kita dapat mendengarkan pesan yang dikirim oleh thread utama ke thread saat ini dalam fungsi thread paralel, dan ID thread utama didefinisikan sebagai 0.

Tentu saja, kecuali thread paralel yang berkomunikasi dengan thread utama. thread paralel juga dapat berkomunikasi satu sama lain secara langsung.

Dapatkan ID thread saat ini dalam fungsi eksekusi thread bersamaan

Dalam contoh di atas,var id = __threadId()digunakan, dan__threadId()fungsi dapat mendapatkan ID dari thread saat ini.

Variabel yang disimpan di antara thread bersama

Selain komunikasi antara thread, variabel bersama juga dapat digunakan untuk interaksi.

function testFunc() {
    __threadSetData(0, "testFunc", 100)   // Stored in the current thread environment, key-value pair testFunc : 100
    Log("testFunc execution completed")
}

function main() {
    // threadId is 1, the created thread with threadId 1 will be executed first, as long as the thread resources are not reclaimed, the variables stored locally in the thread will be valid
    var testThread = __Thread(testFunc)
    
    Sleep(1000)

    // export in main, get testFunc: 100
    Log("in main, get testFunc:", __threadGetData(testThread, "testFunc"))   // Take out the value whose key name is testFunc
}

Di atas adalah demonstrasi sederhana dari semua fungsi. mari kita lihat contoh tes yang sedikit lebih rumit.

Perbandingan kinerja antara JavaScript multi-threaded asli dan WASM

Alamat strategi uji ini:https://www.fmz.com/strategy/401463

Pada pandangan pertama, Anda mungkin tidak tahu apa yang dilakukan strategi uji ini. Tidak masalah, mari kita jelaskan. Pertama, mari kita pelajari apa itu WASM.

WebAssemblyadalahWASM, WebAssemblyadalah format pengkodean baru dan dapat dijalankan di browser,WASMdapat digunakan denganJavaScriptada bersama, dan WASM lebih seperti bahasa assembly tingkat rendah.

Kemudian strategi pengujian adalah untuk membandingkan efisiensi eksekusi wasm dan javascript, tetapi ketika membandingkan, kedua metode eksekusi dapat dijalankan secara berurutan, dan memakan waktu masing-masing dihitung.

  • Algoritma versi bahasa C, fungsi fib
// Recursive algorithm of Fibonacci Numbers in C Language
int fib(int f) {
    if (f < 2) return f;
    return fib(f - 1) + fib(f - 2);
}
  • Versi bahasa JavaScript dari algoritma, fungsi fib
// A recursive algorithm for the same Fibonacci numbers, written in JavaScript
function fib(f) {
    if (f < 2) return f
    return fib(f - 1) + fib(f - 2)
}

Hal ini dapat dilihat bahwa logika dari dua algoritma fungsi fib adalah persis sama. Berikut adalah kode sumber dari strategi tes:

function main() {
    // In order to make it easier to see the code, I write the comment on the following code directly:
    let cycle = 100    // The test executes the loop 100 times
    let input = 30     // The parameters that will be passed to the algorithm fib function
    let threads = [
        __Thread(function(cycle, input) {           // A thread is created concurrently to perform calculations using the JavaScript version of the fib function
            function fib(f) {                       // The specific algorithm used for testing, the fib function
                if (f < 2) return f
                return fib(f - 1) + fib(f - 2)
            }
            let ret = 0
            for (let i = 0; i < cycle; i++) {       // loop for 100 times 
                ret = fib(input);                   // Call the fib function of the JavaScript language 
                Log("javascript progress: ", i)
            }
            return 'javascript fib: ' + ret
        }, cycle, input),
        
        __Thread(function(cycle, input) {           // Run a thread concurrently to perform calculations using the wasm version of the fib function
            let data = 'data:hex,0061736d010000000186808080000160017f017f0382808080000100048480808000017000000583808080000100010681808080000007908080800002066d656d6f727902000366696200000aa480808000019e80808000000240200041024e0d0020000f0b2000417f6a10002000417e6a10006a0b'
            let m = wasm.parseModule(data)          // The data variable is the hex string of the wasm-encoded C language fib function, and the wasm model m is created using wasm.parseModule

            let instance = wasm.buildInstance(m, {  // Model instantiation, allocate a certain stack space
                stack_size: 65 * 1024 * 1024,
            })

            let ret = 0
            for (let i = 0; i < cycle; i++) {                // loop for 100 times 
                ret = instance.callFunction('fib', input)    // Calling the fib function code in the wasm instance is equivalent to calling the int fib(int f) function 
                Log("wasm progress: ", i)
            }

            return 'wasm fib: ' + ret
        }, cycle, input)
    ]
    
    // The elements in the threads array are the IDs returned by the __Thread function
    threads.forEach(function(tid) {
        let info = __threadJoin(tid)                         // Use the __threadJoin function to wait for two concurrent threads to execute and get the execution result
        Log('#'+tid, info.ret, 'elapsed:', info.elapsed / 1e6, "#ff0000")   // output execution result
    })
}

Secara sederhana, WASM adalah kode program dengan efisiensi eksekusi yang lebih tinggi. Dalam contoh ini, kita mengubah kode bahasa c dari algoritma rekursif bilangan Fibonacci menjadi WASM. Prosesnya seperti ini:

  1. Mengkompilasi sepotong kode fungsi bahasa C ke dalam kode wasm.

Kita bisa menggunakan situs web untuk mengkonversi:https://wasdk.github.io/WasmFiddle/

// Recursive Algorithm of Fibonacci numbers in C Language
int fib(int f) {
    if (f < 2) return f;
    return fib(f - 1) + fib(f - 2);
}
  1. Lebih lanjut mengkodekan kode wasm ke dalam string hex.

Perintah berikut dapat digunakan:

python -c "print('data:hex,'+bytes.hex(open('program.wasm','rb').read()))"

String hex yang dikodekan adalahlet data = 'data:hex,0061736d0100000001868...dalam kode.

  1. Kemudian menganalisisnya ke dalam model wasm melalui fungsiwasm.parseModule()diintegrasikan oleh FMZ.

  2. Membuat contoh model wasm melalui fungsiwasm.buildInstance()diintegrasikan oleh FMZ.

  3. Lalu hubungifibfungsi dalam contoh model wasm ini, yaitu:ret = instance.callFunction('fib', input).

Buat bot nyata untuk menjalankan tes

Strategi pengujian ini hanya dapat digunakan untuk pengujian bot nyata. Fungsi multi-threading JavaScript tidak mendukung backtesting sejauh ini.

wasmdanJavaScriptPerbandingan pelaksanaan, hasil pelaksanaan akhir:

2023-03-06 11:00:33		infomation	#2 wasm fib: 832040 elapsed: 13283.773019
2023-03-06 11:00:33		infomation	#1 javascript fib: 832040 elapsed: 21266.326974

Sepertinyawasmmembutuhkan waktu lebih sedikit dan lebih baik.


Berkaitan

Lebih banyak