Execução simultânea de estratégias com suporte a multithreading JavaScript

Autora:Lydia., Criado: 2023-03-07 15:12:04, Atualizado: 2023-09-18 20:04:21

img

Deixe o programa de estratégia ser executado simultaneamente, e adicionar suporte multi-thread na parte inferior do sistema para a estratégia JavaScript

Ao desenvolver estratégias no FMZ usando a linguagem JavaScript, uma vez que a arquitetura da estratégia é pesquisada.exchange.GoA função é usada para fazer chamadas simultâneas para algumas interfaces, de modo a atender aos requisitos de alguns cenários simultâneos. Mas se você quiser criar um único thread para realizar uma série de operações, é impossível.threadingbiblioteca para fazer algum design simultâneo.

Com base neste requisito, a plataforma FMZ atualizou a camada inferior do sistema.

  • Crie threads para executar funções personalizadas simultaneamente.
  • Comunicação entre fios.
  • Variaveis armazenadas entre threads compartilhados.
  • Espere que o thread termine de executar para recuperar recursos e retornar o resultado da execução.
  • Acabar com a força o fio e recuperar os recursos.
  • Obtenha a ID de thread atual na função de execução de thread simultânea.

Em seguida, vou levá-lo a entender cada função uma por uma.

Criar tópicos para executar funções personalizadas simultaneamente

O__ThreadPor exemplo, você precisa criar uma função simultâneafunc1, o que faz ofunc1Para ver o processo de acumulação gradual, usamos o loop for na função func1 para pausar cada vez (a função Sleep é usada para dormir por um certo número de milissegundos) por um determinado período de tempo.

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

Em aplicações práticas, podemos fazer solicitações http simultaneamente assim:

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

Espere o fim da execução do thread para recuperar recursos e retornar o resultado da execução

No exemplo acima, usamos o__threadJoinfunção na função principal finalmente para esperar que os tópicos concorrentes para terminar de executar.retrecebe o valor de retorno do__threadJoinfunção, e imprimimos o valor de retorno, podemos observar os resultados específicos da execução simultânea do 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}

Acaba o fio à força e recupera os 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
}

Ainda usamos o exemplo agora, depois de criar um thread, você pode forçar a encerrar a execução do thread depois de esperar por 1 segundo.

Comunicação entre fios

A comunicação inter-thread utiliza principalmente o__threadPostMessageFunção e__threadPeekMessageVejamos o seguinte exemplo simples:

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

O__threadPostMessageO primeiro parâmetro é o ID do thread específico para enviar, e o segundo parâmetro é a mensagem a ser enviada, que pode ser uma cadeia, um valor, uma matriz ou um objeto JSON e assim por diante. As mensagens podem ser enviadas para o thread principal em funções de thread simultâneas, e o ID do thread principal é definido como 0.

O__threadPeekMessageA função é usada para monitorar a mensagem enviada por um determinado thread. O primeiro parâmetro é monitorar o ID específico do thread. O segundo parâmetro pode definir o tempo de tempo (em milissegundos), ou pode ser definido como -1, o que significa bloqueio, e não retornará até que haja uma mensagem. Podemos ouvir a mensagem enviada pelo thread principal para o thread atual na função de thread concorrente, e o ID do thread principal é definido como 0.

Claro, exceto para threads simultâneos que se comunicam com o thread principal.

Obter o ID de thread atual na função de execução de thread simultânea

No exemplo acima,var id = __threadId()A utilização de__threadId()função pode obter a ID do tópico atual.

Variaveis armazenadas entre tópicos compartilhados

Além da comunicação entre threads, variáveis compartilhadas também podem ser usadas para interação.

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
}

O exemplo acima é uma demonstração simples de todas as funções.

Comparação de desempenho entre JavaScript nativo multi-threaded e WASM

Este endereço da estratégia de ensaio:https://www.fmz.com/strategy/401463

Em primeiro lugar, vamos aprender o que é o WASM.

WebAssemblyéWASM, WebAssemblyé um novo formato de codificação e pode ser executado no navegador,WASMpode ser utilizado comJavaScriptcoexiste, e WASM é mais como uma linguagem de assembleamento de baixo nível.

Em seguida, a estratégia de teste é comparar a eficiência de execução de wasm e javascript, mas ao comparar, os dois métodos de execução podem ser executados sucessivamente, e o tempo de cada um é contado. Também é possível permitir que os dois métodos de execução sejam executados simultaneamente, e as estatísticas são demoradas.

  • Algoritmo da versão da linguagem C, função 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);
}
  • Versão em JavaScript do algoritmo, função 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)
}

Pode-se ver que a lógica dos dois algoritmos de função fib é exatamente a mesma.

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

Simplificando, o WASM é um código de programa com maior eficiência de execução. No exemplo, convertemos o código da linguagem c do algoritmo recursivo de números de Fibonacci em WASM. O processo é assim:

  1. Compilar um pedaço de código de função da linguagem C em código wasm.

Podemos usar o site para converter: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. Encode o código da vespa numa cadeia hexadecimal.

Os seguintes comandos podem ser utilizados:

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

A cadeia hexadecimal élet data = 'data:hex,0061736d0100000001868...no código.

  1. Então analisá-lo em um modelo de vespa através da funçãowasm.parseModule()integrado pela FMZ.

  2. Criar uma instância do modelo wasm através da funçãowasm.buildInstance()integrado pela FMZ.

  3. Então chama ofibfunção nesta instância do modelo wasm, nomeadamente:ret = instance.callFunction('fib', input).

Crie um bot real para executar o teste

Esta estratégia de teste só pode ser usada para testes de bots reais.

wasmeJavaScriptcomparação de execução, os resultados finais da execução:

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 quewasmDemora menos tempo e é melhor.


Relacionados

Mais.