En la carga de los recursos... Cargando...

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

img

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.

WebAssemblyesWASM, WebAssemblyes un nuevo formato de codificación y puede ejecutarse en el navegador,WASMpuede utilizarse conJavaScriptcoexiste, y WASM es más como un lenguaje de ensamblaje de bajo nivel.

Luego, la estrategia de prueba es comparar la eficiencia de ejecución de wasm y javascript, pero al comparar, los dos métodos de ejecución se pueden ejecutar sucesivamente, y se cuenta el tiempo de cada uno. También es posible permitir que los dos métodos de ejecución se ejecuten simultáneamente, y las estadísticas consumen tiempo. Ahora que se ha apoyado la implementación de concurrencia subyacente de la estrategia del lenguaje JavaScript, la estrategia de prueba utiliza un método concurrente para comparar naturalmente y comparar la velocidad de ejecución del mismo algoritmo.

  • Algoritmo de la versión del lenguaje C, función 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ón en lenguaje JavaScript del algoritmo, función 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)
}

Se puede ver que la lógica de los dos algoritmos de la función fib es exactamente la misma.

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 pocas palabras, WASM es un código de programa con una mayor eficiencia de ejecución. En el ejemplo, convertimos el código del lenguaje c de algoritmo recursivo de números de Fibonacci en WASM. El proceso es el siguiente:

  1. Compilar una pieza de código de función del lenguaje C en código wasm.

Podemos usar el sitio web para 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. Codifica el código de la avispa en una cadena hexadecimal.

Se pueden utilizar los siguientes comandos:

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

La cadena hexadecimal codificada eslet data = 'data:hex,0061736d0100000001868...en el código.

  1. Luego analizarlo en un modelo de la avispa a través de la funciónwasm.parseModule()integrado por la FMZ.

  2. Crear una instancia de modelo wasm a través de la funciónwasm.buildInstance()integrado por la FMZ.

  3. Entonces llama alfibfunción en esta instancia del modelo wasm, a saber:ret = instance.callFunction('fib', input).

Crear un bot real para ejecutar la prueba

Esta estrategia de prueba sólo se puede utilizar para pruebas reales de bots. Las funciones de múltiples hilos de JavaScript no admiten backtesting hasta ahora.

wasmyJavaScriptcomparación de ejecución, los resultados finales de ejecución:

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

Parece quewasmtoma menos tiempo y es mejor.


Relacionados

Más.