Gleichzeitige Strategieausführung mit JavaScript-Multithreading-Unterstützung

Schriftsteller:Lydia., Erstellt: 2023-03-07 15:12:04, Aktualisiert: 2023-09-18 20:04:21

img

Lassen Sie das Strategieprogramm gleichzeitig ausgeführt werden, und fügen Sie Multi-Thread-Unterstützung am Ende des Systems zu der JavaScript-Strategie

Bei der Entwicklung von Strategien auf FMZ mit der JavaScript-Sprache, da die Strategie-Architektur befragt wird.exchange.GoFunktion wird verwendet, um gleichzeitige Aufrufe an einige Schnittstellen zu tätigen, um die Anforderungen einiger gleichzeitiger Szenarien zu erfüllen.threadingBibliothek, um gleichzeitig etwas zu entwerfen.

Aufgrund dieser Anforderung hat die FMZ-Plattform die untere Schicht des Systems aktualisiert.

  • Erstellen Sie Threads, um benutzerdefinierte Funktionen gleichzeitig auszuführen.
  • Kommunikation zwischen Threads.
  • Variablen, die zwischen geteilten Threads gespeichert werden.
  • Warten Sie, bis der Thread die Ausführung beendet hat, um Ressourcen zurückzufordern und das Ausführungsresultat zurückzugeben.
  • Wir müssen den Faden zwangsweise beenden und Ressourcen zurückfordern.
  • Erhalten Sie die aktuelle Thread-ID in der gleichzeitigen Thread-Ausführungsfunktion.

Als Nächstes werde ich Sie dazu bringen, jede Funktion einzeln zu verstehen.

Erstellen von Threads zur gleichzeitigen Ausführung von benutzerdefinierten Funktionen

Die__ThreadFunktion kann einen Thread erstellen und eine Funktion gleichzeitig ausführen.func1, was macht diefunc1Wir können es von 0 bis 9 akkumulieren lassen. Um den schrittweisen Akkumulationsprozess zu sehen, verwenden wir die For-Schleife in der Funktion func1 um jedes Mal (die Sleep-Funktion wird für eine bestimmte Anzahl von Millisekunden zum Schlafen verwendet) für einen bestimmten Zeitraum zu pausieren.

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

In praktischen Anwendungen können wir http-Anfragen gleichzeitig so machen:

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

Warten Sie auf das Ende der Thread-Ausführung, um Ressourcen zurückzufordern und das Ausführungsergebnis zurückzugeben

In dem obigen Beispiel haben wir die__threadJoinFunktion in der Hauptfunktion endlich warten, bis die gleichzeitigen Threads zu beenden ausführen.reterhält den Rückgabewert der__threadJoinFunktion, und wir drucken den Rückgabewert, können wir die spezifischen Ergebnisse der gleichzeitigen Thread-Ausführung beobachten.

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

Endet den Thread mit Gewalt und holt die Ressourcen zurück.

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
}

Wir verwenden immer noch das Beispiel gerade jetzt, nachdem Sie einen Thread erstellt haben, können Sie die Ausführung des Threads nach einem Wartezeitraum von 1 Sekunde zwangsweise beenden.

Kommunikation zwischen Threads

Die Inter-Thread-Kommunikation nutzt hauptsächlich die__threadPostMessageFunktion und__threadPeekMessageWir wollen uns das folgende einfache Beispiel ansehen:

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

Die__threadPostMessageDer erste Parameter ist die ID des spezifischen Threades, an den gesendet werden soll, und der zweite Parameter ist die zu versendende Nachricht, die eine Zeichenfolge, ein Wert, ein Array oder ein JSON-Objekt sein kann. Nachrichten können in gleichzeitigen Threadfunktionen an den Hauptthread gesendet werden, und die ID des Hauptthreads ist als 0 definiert.

Die__threadPeekMessageDer erste Parameter ist die Überwachung der spezifischen ID des Threads. Der zweite Parameter kann die Timeout-Zeit (in Millisekunden) festlegen, oder er kann auf -1 gesetzt werden, was bedeutet, dass er blockiert wird, und er wird nicht zurückkehren, bis es eine Nachricht gibt. Wir können die Nachricht hören, die vom Hauptthread an den aktuellen Thread in der gleichzeitigen Thread-Funktion gesendet wird, und die ID des Hauptthreads ist als 0 definiert.

Natürlich, mit Ausnahme von gleichzeitigen Threads, die mit dem Hauptthread kommunizieren. Gleichzeitige Threads können auch direkt miteinander kommunizieren.

Erhalten Sie die aktuelle Thread-ID in der gleichzeitigen Thread-Ausführung

In dem obigen Beispielvar id = __threadId()Die__threadId()Funktion kann die ID des aktuellen Threads erhalten.

Variablen zwischen geteilten Threads gespeichert

Zusätzlich zur Kommunikation zwischen Threads können geteilte Variablen auch für die Interaktion verwendet werden.

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
}

Das ist eine einfache Demonstration aller Funktionen. Schauen wir uns ein etwas komplizierteres Testbeispiel an.

Leistungsvergleich zwischen nativen mehrthreaded JavaScript und WASM

Diese Teststrategie richtet sich an:https://www.fmz.com/strategy/401463

Auf den ersten Blick wissen Sie vielleicht nicht, was diese Teststrategie bewirkt.Es spielt keine Rolle, lassen Sie uns es erklären.Zuerst lernen wir, was WASM ist.

WebAssemblyistWASM, WebAssemblyist ein neues Codierungsformat und kann im Browser ausgeführt werden,WASMkann mitJavaScriptund WASM ist eher wie eine Assembler-Sprache auf niedriger Ebene.

Dann ist die Teststrategie, die Ausführungsfähigkeit von wasm und javascript zu vergleichen, aber beim Vergleich können die beiden Ausführungsmethoden sukzessive ausgeführt werden, und der zeitaufwendige Wert jedes wird gezählt. Es ist auch möglich, die beiden Ausführungsmethoden gleichzeitig auszuführen, und die Statistiken sind zeitaufwändig. Jetzt, da die zugrunde liegende gleichzeitige Implementierung der JavaScript-Sprachstrategie unterstützt wurde, verwendet die Teststrategie eine gleichzeitige Methode, um die Ausführungsgeschwindigkeit desselben Algorithmus natürlich zu vergleichen und zu vergleichen.

  • Algorithmus der C-Sprache Version, fib Funktion
// Recursive algorithm of Fibonacci Numbers in C Language
int fib(int f) {
    if (f < 2) return f;
    return fib(f - 1) + fib(f - 2);
}
  • JavaScript-Version des Algorithmus, Fib-Funktion
// 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)
}

Es kann gesehen werden, dass die Logik der beiden Fib-Funktionsalgorithmen genau gleich ist.

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

Einfach ausgedrückt, WASM ist ein Programmcode mit höherer Ausführungsfähigkeit. Im Beispiel konvertieren wir den C-Sprachcode des Fibonacci-Zahlenrekursivalgorithmus in WASM. Der Prozess ist wie folgt:

  1. Kompilieren Sie ein Stück C-Funktionscode in Wasm-Code.

Wir können die Website verwenden, um zu konvertieren: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. Weiter kodieren Sie den Wasm-Code in eine Hex-String.

Die folgenden Befehle können verwendet werden:

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

Die verschlüsselte Hex-String istlet data = 'data:hex,0061736d0100000001868...in den Code.

  1. Dann analysieren Sie es in ein Wasm-Modell durch die Funktionwasm.parseModule()integriert durch FMZ.

  2. Erstellen Sie eine wasm-Modell-Instanz über die Funktionwasm.buildInstance()integriert durch FMZ.

  3. Dann rufen Sie diefibFunktion in dieser wasm-Modell-Instanz, nämlich:ret = instance.callFunction('fib', input).

Erstellen Sie einen echten Bot, um den Test auszuführen

Diese Teststrategie kann nur für echte Bot-Tests verwendet werden. JavaScript Multi-Threading-Funktionen unterstützen bisher kein Backtesting.

wasmundJavaScriptVergleich der Ausführung, endgültige Ausführungsergebnisse:

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

Es scheint, dasswasmdauert weniger Zeit und ist besser.


Verwandt

Mehr