Les ressources ont été chargées... Je charge...

Exécution simultanée des stratégies avec support de multithreading JavaScript

Auteur:FMZ~Lydia, Créé à: 2023-03-07 15:12:04, Mis à jour à: 2023-09-18 20:04:21

img

Laissez le programme de stratégie être exécuté simultanément, et ajouter la prise en charge multi-thread au bas du système à la stratégie JavaScript

Lors du développement de stratégies sur FMZ en utilisant le langage JavaScript, puisque l'architecture de stratégie est interrogée.exchange.GoCette fonction est utilisée pour faire des appels simultanés à certaines interfaces, afin de répondre aux exigences de certains scénarios simultanés. Mais si vous voulez créer un seul thread pour effectuer une série d'opérations, c'est impossible.threadingbibliothèque pour faire une conception simultanée.

En fonction de cette exigence, la plateforme FMZ a mis à niveau la couche inférieure du système.

  • Créez des threads pour exécuter simultanément des fonctions personnalisées.
  • Communication entre les fils.
  • Variables stockées entre des threads partagés.
  • Attendez que le thread termine l'exécution pour récupérer les ressources et retourner le résultat de l'exécution.
  • Mettre fin à la violence et récupérer les ressources.
  • Obtenez l'identifiant de thread actuel dans la fonction d'exécution de thread concurrent.

Ensuite, je vais vous aider à comprendre chaque fonction une par une.

Créer des threads pour exécuter des fonctions personnalisées simultanément

Le__Threadfonction peut créer un thread et exécuter une fonction simultanément.func1Qu'est-ce que lefunc1Pour voir le processus d'accumulation progressive, nous utilisons la boucle for dans la fonction func1 pour faire une pause à chaque fois (la fonction Sleep est utilisée pour dormir pendant un certain nombre de millisecondes) pendant une certaine période de temps.

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

Dans les applications pratiques, nous pouvons faire des requêtes http simultanément comme ceci:

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

Attendez la fin de l'exécution du thread pour récupérer les ressources et retourner le résultat de l'exécution

Dans l'exemple ci-dessus, nous avons utilisé le__threadJoinfonction dans la fonction principale enfin d'attendre que les threads concurrents pour terminer l'exécution.retreçoit la valeur de retour de la__threadJoinfonction, et nous imprimons la valeur de retour, nous pouvons observer les résultats spécifiques de l'exécution simultanée du thread.

// 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}

Terminez le fil de force et récupérez les ressources

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
}

Nous utilisons toujours l'exemple juste maintenant, après avoir créé un thread, vous pouvez mettre fin de force à l'exécution du thread après avoir attendu 1 seconde.

Communication entre les fils

La communication inter-thread utilise principalement le__threadPostMessagefonction et le__threadPeekMessageNous allons examiner l'exemple simple suivant:

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

Le__threadPostMessageLe premier paramètre est l'ID du fil spécifique à envoyer, et le deuxième paramètre est le message à envoyer, qui peut être une chaîne, une valeur, un tableau ou un objet JSON, etc. Les messages peuvent être envoyés au fil principal dans des fonctions de fil concurrentes, et l'ID du fil principal est défini comme 0.

Le__threadPeekMessagele premier paramètre est de surveiller l'ID spécifique du thread. Le deuxième paramètre peut définir le temps d'arrêt (en millisecondes), ou il peut être défini sur -1, ce qui signifie le blocage, et il ne reviendra pas jusqu'à ce qu'il y ait un message. Nous pouvons écouter le message envoyé par le thread principal au thread actuel dans la fonction de thread concurrent, et l'ID du thread principal est défini comme 0.

Bien sûr, à l'exception des threads concurrents qui communiquent avec le thread principal.

Obtenir l' ID du fil en cours dans la fonction d'exécution du fil en même temps

Dans l'exemple précédent,var id = __threadId()est utilisé, et le__threadId()fonction peut obtenir l'ID du fil actuel.

Variables stockées entre des threads partagés

En plus de la communication entre les threads, les variables partagées peuvent également être utilisées pour l'interaction.

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
}

Ce qui précède est une démonstration simple de toutes les fonctions.

Comparaison des performances entre JavaScript natif multi-threaded et WASM

Cette stratégie de test s'adresse à:https://www.fmz.com/strategy/401463

Au premier coup d'œil, vous ne savez peutêtre pas ce que cette stratégie de test fait.

WebAssemblyestWASM, WebAssemblyest un nouveau format de codage et peut être exécuté dans le navigateur,WASMpeut être utilisé avecJavaScriptcoexiste, et WASM est plus comme un langage d'assemblage de bas niveau.

Ensuite, la stratégie de test consiste à comparer l'efficacité d'exécution de wasm et javascript, mais lors de la comparaison, les deux méthodes d'exécution peuvent être exécutées successivement, et le temps de consommation de chacun est compté. Il est également possible de permettre aux deux méthodes d'exécution d'exécuter simultanément, et les statistiques sont chronophages. Maintenant que la mise en œuvre sous-jacente de la stratégie de langage JavaScript a été prise en charge, la stratégie de test utilise une méthode concurrente pour comparer naturellement et comparer la vitesse d'exécution du même algorithme.

  • Algorithme de la version du langage C, fonction 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);
}
  • Version de l'algorithme en langage JavaScript, fonction 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)
}

On peut voir que la logique des deux algorithmes de la fonction fib est exactement la même.

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

En termes simples, WASM est un code de programme avec une plus grande efficacité d'exécution. Dans l'exemple, nous convertissons le code de langage c de algorithme récursif des nombres de Fibonacci en WASM. Le processus est le suivant:

  1. Compiler un morceau de code de fonction du langage C en code wasm.

On peut utiliser le site pour convertir: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. Codifiez le code de la guêpe dans une chaîne hexagonale.

Les commandes suivantes peuvent être utilisées:

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

La chaîne hexagonale codée estlet data = 'data:hex,0061736d0100000001868...dans le code.

  1. Ensuite, analysez-le dans un modèle de guêpe à travers la fonctionwasm.parseModule()intégrées par FMZ.

  2. Créez une instance de modèle wasm à l'aide de la fonctionwasm.buildInstance()intégrées par FMZ.

  3. Alors appelle lefibfonction dans cette instance de modèle wasm, à savoir:ret = instance.callFunction('fib', input).

Créez un vrai bot pour exécuter le test

Cette stratégie de test ne peut être utilisée que pour les tests de bots réels.

wasmetJavaScriptla comparaison de l'exécution, les résultats finaux de l'exécution:

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

Il semble quewasmprend moins de temps et c'est mieux.


Relationnée

Plus de