Ejecución simultánea de estrategias con soporte de multithreading de JavaScript

El autor:FMZ~Lydia, Creado: 2023-03-07 15:12:04, Actualizado: 2023-09-18 20:04:21

Concurrent Strategy Execution with JavaScript Multithreading Support

Deja que el programa de estrategia se ejecute simultáneamente, y añadir soporte multi-thread en la parte inferior del sistema a la estrategia JavaScript

Cuando se desarrollan estrategias en FMZ utilizando el lenguaje JavaScript, ya que la arquitectura de la estrategia se encuesta.exchange.Gola función se utiliza para hacer llamadas concurrentes a algunas interfaces, de modo que se cumplan los requisitos de algunos escenarios concurrentes. pero si desea crear un único hilo para realizar una serie de operaciones, es imposible. por ejemplo, como el lenguaje Python, utilizar elthreadingbiblioteca para hacer algún diseño simultáneo.

Basándose en este requisito, la plataforma FMZ ha actualizado la capa inferior del sistema.

  • Crear hilos para ejecutar funciones personalizadas simultáneamente.
  • Comunicación entre hilos.
  • Variables almacenados entre hilos compartidos.
  • Espera a que el hilo termine de ejecutar para recuperar los recursos y devolver el resultado de la ejecución.
  • Terminar con la fuerza el hilo y recuperar los recursos.
  • Obtener el ID de hilo actual en la función de ejecución de hilo concurrente.

A continuación, voy a llevar a entender cada función una por una.

Crear hilos para ejecutar funciones personalizadas simultáneamente

El__ThreadPor ejemplo, necesitas crear una función concurrentefunc1, ¿qué hace elfunc1Para ver el proceso de acumulación gradual, usamos el bucle for en la función func1 para pausar cada vez (la función Sleep se usa para dormir durante un cierto número de milisegundos) durante un cierto período de tiempo.

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

En aplicaciones prácticas, podemos hacer peticiones http simultáneamente como esto:

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

Espera hasta el final de la ejecución del hilo para recuperar los recursos y devolver el resultado de la ejecución

En el ejemplo anterior, utilizamos el__threadJoinfunción en la función principal finalmente para esperar a que los hilos concurrentes terminen de ejecutar.retrecibe el valor de retorno de la__threadJoinfunción, y imprimimos el valor de retorno, podemos observar los resultados específicos de la ejecución de hilo concurrente.

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

Termina el hilo por la fuerza y reclamar los recursos

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
}

Todavía usamos el ejemplo de ahora, después de crear un hilo, se puede terminar por la fuerza la ejecución del hilo después de esperar 1 segundo.

Comunicación entre hilos

La comunicación entre hilos utiliza principalmente el__threadPostMessageLa función y el__threadPeekMessageVeamos el siguiente ejemplo simple:

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

El__threadPostMessageEl primer parámetro es el ID del hilo específico al que enviar, y el segundo parámetro es el mensaje a enviar, que puede ser una cadena, un valor, una matriz u objeto JSON, etc. Los mensajes se pueden enviar al hilo principal en funciones de hilo concurrentes, y el ID del hilo principal se define como 0.

El__threadPeekMessageEl segundo parámetro puede establecer el tiempo de tiempo de espera (en milisegundos), o se puede establecer en -1, lo que significa bloqueo, y no regresará hasta que no haya un mensaje. Podemos escuchar el mensaje enviado por el hilo principal al hilo actual en la función de hilo concurrente, y el ID del hilo principal se define como 0.

Por supuesto, excepto los hilos concurrentes que se comunican con el hilo principal. Los hilos concurrentes también pueden comunicarse entre sí directamente.

Obtener el ID de hilo actual en la función de ejecución de hilo concurrente

En el ejemplo anterior,var id = __threadId()se utiliza, y el__threadId()la función puede obtener el ID del hilo actual.

Variables almacenados entre hilos compartidos

Además de la comunicación entre hilos, las variables compartidas también se pueden utilizar para la interacción.

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
}

Lo anterior es una demostración simple de todas las funciones.

Comparación de rendimiento entre JavaScript nativo de múltiples hilos y WASM

Esta dirección de la estrategia de ensayo:https://www.fmz.com/strategy/401463

A simple vista, es posible que no sepa qué hace esta estrategia de prueba. No importa, explicémosla. Primero, aprendamos qué es WASM.


Then the test strategy is to compare the execution efficiency of wasm and javascript, but when comparing, the two execution methods can be executed successively, and the time-consuming of each is counted. It is also possible to allow the two execution methods to execute concurrently, and the statistics are time-consuming. Now that the underlying concurrency implementation of the JavaScript language strategy has been supported, the test strategy uses a concurrent method to compare naturally and compare the execution speed of the same algorithm.

- Algorithm of C language version, fib function

// Algoritmo recursivo de números de Fibonacci en lenguaje C Int fib ((int f) { si (f < 2) devuelve f; El valor de las emisiones de gases de efecto invernadero es el valor de las emisiones de gases de efecto invernadero. ¿ Por qué?

- JavaScript language version of the algorithm, fib function

// Un algoritmo recursivo para los mismos números de Fibonacci, escrito en JavaScript Función fib (f) { si (f < 2) devuelve f Devolver fib ((f - 1) + fib ((f - 2) ¿ Por qué?

It can be seen that the logic of the two fib function algorithms is exactly the same. The following is the source code of the test strategy:

Función principal (() { // Para que sea más fácil ver el código, escribo el comentario en el siguiente código directamente: dejar ciclo = 100 // La prueba ejecuta el bucle 100 veces dejar entrada = 30 // Los parámetros que se pasarán a la función fib algoritmo que los hilos = __Thread(function(cycle, input) { // Se crea un hilo simultáneamente para realizar cálculos utilizando la versión JavaScript de la función fib función fib ((f) { // El algoritmo específico utilizado para la prueba, la función fib si (f < 2) devuelve f Devolver fib ((f - 1) + fib ((f - 2) ¿ Por qué? dejar ret = 0 para (dejemos que i = 0; i < ciclo; i++) { // bucle durante 100 veces ret = fib(input); // Llama a la función fib del lenguaje JavaScript Log ((progreso de JavaScript: , i) ¿ Por qué? Retorno javascript fib: + ret }, ciclo, entrada),

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

}

Simply put, WASM is a program code with higher execution efficiency. In the example, we convert the c language code of "Fibonacci number recursive algorithm" into WASM. The process is like this:

1. Compile a piece of C language function code into wasm code.

We can use the website to convert: https://wasdk.github.io/WasmFiddle/

// Algoritmo recursivo de números de Fibonacci en lenguaje C Int fib ((int f) { si (f < 2) devuelve f; El valor de las emisiones de gases de efecto invernadero es el valor de las emisiones de gases de efecto invernadero. ¿ Por qué?

2. Further encode the wasm code into a hex string.

The following commands can be used:

python -c print(data:hex,+bytes.hex(abierto(program.wasm,rb).leído)))

The encoded hex string is ```let data = 'data:hex,0061736d0100000001868...``` in the code.

3. Then parse it into a wasm model through the function ```wasm.parseModule()``` integrated by FMZ.

4. Create a wasm model instance through the function ```wasm.buildInstance()``` integrated by FMZ.

5. Then call the ```fib``` function in this wasm model instance, namely: ```ret = instance.callFunction('fib', input)```.

## Create a real bot to run the test
This test strategy can only be used for real bot testing. JavaScript multi-threading functions do not support backtesting so far.

```wasm``` and ```JavaScript``` execution comparison, the final execution results:

2023-03-06 11:00:33 información # 2 fue en fib: 832040 vencido: 13283.773019 2023-03-06 11:00:33 información #1 javascript fib: 832040 vencido: 21266.326974 It seems thatWasm` toma menos tiempo y es mejor.


Contenido relacionado

Más contenido