Pelaksanaan strategi serentak dengan sokongan multithreading JavaScript

Penulis:Lydia, Dicipta: 2023-03-07 15:12:04, Dikemas kini: 2023-09-18 20:04:21

img

Biarkan program strategi dijalankan serentak, dan menambah sokongan pelbagai benang di bahagian bawah sistem untuk strategi JavaScript

Apabila membangunkan strategi di FMZ menggunakan bahasa JavaScript, kerana seni bina strategi diundi.exchange.Gofungsi digunakan untuk membuat panggilan serentak ke beberapa antara muka, supaya memenuhi keperluan beberapa senario serentak. tetapi jika anda ingin membuat satu benang untuk melakukan satu siri operasi, ia adalah mustahil. contohnya seperti bahasa Python, gunakanthreadingperpustakaan untuk melakukan beberapa reka bentuk serentak.

Berdasarkan keperluan ini, platform FMZ telah menaik taraf lapisan bawah sistem. Sokongan multithreading sejati juga telah ditambah ke dalam bahasa JavaScript. Ciri terperinci termasuk:

  • Buat benang untuk melaksanakan fungsi tersuai secara serentak.
  • Komunikasi antara benang.
  • Variabel disimpan di antara utas yang dikongsi.
  • Tunggu thread selesai menjalankan untuk mendapatkan semula sumber dan mengembalikan hasil pelaksanaan.
  • Mengakhiri tali dengan paksa dan mendapatkan semula sumber.
  • Dapatkan ID thread semasa dalam fungsi pelaksanaan thread serentak.

Seterusnya, saya akan membawa anda untuk memahami setiap fungsi satu demi satu.

Membuat utas untuk menjalankan fungsi tersuai serentak

Peraturan__Threadfungsi boleh mencipta benang dan melaksanakan fungsi serentak.func1, apa yangfunc1Untuk melihat proses pengumpulan secara beransur-ansur, kita menggunakan gelung for dalam fungsi func1 untuk berehat setiap kali (fungsi Sleep digunakan untuk tidur untuk beberapa milidetik) untuk tempoh masa 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 praktikal, kita boleh membuat permintaan http serentak 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 sehingga akhir pelaksanaan thread untuk mendapatkan semula sumber dan mengembalikan hasil pelaksanaan

Dalam contoh di atas, kita menggunakan__threadJoinfungsi dalam fungsi utama akhirnya untuk menunggu untuk utas serentak untuk menyelesaikan pelaksanaan.retmenerima nilai pulangan__threadJoinfungsi, dan kita cetak nilai pulangan, kita boleh memerhatikan hasil tertentu dari pelaksanaan benang serentak.

// 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 benang dengan paksa dan dapatkan semula sumber

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 sekarang, selepas membuat utas, anda boleh memaksa menamatkan pelaksanaan utas selepas menunggu selama 1 saat.

Komunikasi antara benang

Komunikasi antara benang terutamanya menggunakan__threadPostMessagefungsi dan__threadPeekMessageMari kita lihat contoh mudah 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 menghantar mesej ke thread. Parameter pertama adalah ID thread tertentu untuk dihantar, dan parameter kedua adalah mesej yang akan dihantar, yang boleh menjadi rentetan, nilai, array, atau objek JSON dan sebagainya. Mesej boleh dihantar ke thread utama dalam fungsi thread serentak, dan ID thread utama ditakrifkan sebagai 0.

Peraturan__threadPeekMessagefungsi digunakan untuk memantau mesej yang dihantar oleh utas tertentu. Parameter pertama adalah untuk memantau ID khusus utas. Parameter kedua boleh menetapkan masa tamat (dalam mili saat), atau ia boleh ditetapkan kepada -1, yang bermaksud menyekat, dan ia tidak akan kembali sehingga ada mesej. Kita boleh mendengar mesej yang dihantar oleh utas utama ke utas semasa dalam fungsi utas serentak, dan ID utas utama ditakrifkan sebagai 0.

Sudah tentu, kecuali untuk benang serentak yang berkomunikasi dengan benang utama. benang serentak juga boleh berkomunikasi antara satu sama lain secara langsung.

Dapatkan ID thread semasa dalam fungsi pelaksanaan thread bersamaan

Dalam contoh di atas,var id = __threadId()digunakan, dan__threadId()fungsi boleh mendapatkan ID benang semasa.

Variabel yang disimpan di antara utas yang dikongsi

Sebagai tambahan kepada komunikasi antara benang, pembolehubah bersama juga boleh 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
}

Yang di atas adalah demonstrasi mudah semua fungsi. mari kita lihat contoh ujian yang sedikit lebih rumit.

Perbandingan prestasi antara JavaScript multi-thread asli dan WASM

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

Pada pandangan pertama, anda mungkin tidak tahu apa yang dilakukan strategi ujian ini. Tidak penting, mari kita jelaskannya. Pertama, mari belajar apa itu WASM.

WebAssemblyadalahWASM, WebAssemblyadalah format pengekodan baru dan boleh dijalankan dalam penyemak imbas,WASMboleh digunakan denganJavaScriptwujud bersama, dan WASM lebih seperti bahasa pemasangan peringkat rendah.

Kemudian strategi ujian adalah untuk membandingkan kecekapan pelaksanaan wasm dan javascript, tetapi apabila membandingkan, kedua-dua kaedah pelaksanaan boleh dilaksanakan secara berturut-turut, dan memakan masa masing-masing dikira. Ia juga mungkin untuk membenarkan kedua-dua kaedah pelaksanaan dijalankan secara serentak, dan statistik memakan masa. Sekarang bahawa pelaksanaan serentak asas strategi bahasa JavaScript telah disokong, strategi ujian menggunakan kaedah serentak untuk membandingkan secara semula jadi dan membandingkan kelajuan pelaksanaan algoritma yang sama.

  • 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 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)
}

Ia dapat dilihat bahawa logik kedua-dua algoritma fungsi fib adalah sama. Berikut adalah kod sumber strategi ujian:

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 ringkasnya, WASM adalah kod program dengan kecekapan pelaksanaan yang lebih tinggi. Dalam contoh, kita menukar kod bahasa c algoritma rekursif nombor Fibonacci ke WASM. Prosesnya seperti ini:

  1. Mengkompilerkan sekeping kod fungsi bahasa C ke dalam kod wasm.

Kita boleh gunakan laman web untuk menukar: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 mengkod kod wasm ke dalam rentetan hex.

Perintah berikut boleh digunakan:

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

Senar hex dikodkan adalahlet data = 'data:hex,0061736d0100000001868...dalam kod.

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

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

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

Buat bot sebenar untuk menjalankan ujian

Strategi ujian ini hanya boleh digunakan untuk ujian bot sebenar. Fungsi pelbagai benang JavaScript tidak menyokong backtesting setakat 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

Nampaknyawasmmengambil masa lebih sedikit dan lebih baik.


Berkaitan

Lebih lanjut