JavaScript マルチスレッドディングサポートで同時に戦略を実行する

作者: リン・ハーンリディア, 作成日:2023-03-07 15:12:04, 更新日:2023-09-18 20:04:21

img

JavaScript 戦略の底にマルチスレッドサポートを追加します.

FMZ で戦略を開発する際に JavaScript 言語を使用する. 戦略アーキテクチャが調査されているため.exchange.Goこの関数は,いくつかのインタフェースに同時呼び出しを行うために使用され,いくつかの同時シナリオの要件を満たします. しかし,一連の操作を実行するために単一のスレッドを作成したい場合は,それは不可能です.例えば,Python言語のように,threading図書室で並行設計をします

この要件に基づいて,FMZプラットフォームはシステムの底層をアップグレードした.JavaScript言語にも真のマルチスレッドサポートが追加された.詳細な機能には以下のものがある:

  • カスタム関数を同時に実行するスレッドを作成します.
  • 糸間通信
  • 共有スレッド間で保存される変数.
  • スレッドが実行を終えるまで待って リソースをリクエストして実行結果を返します
  • 糸を強制的に切って 資源を取り戻す
  • 同期スレッド実行関数で現在のスレッド ID を取得します.

次に,各関数を"つずつ説明します.

カスタム関数を同時に実行するスレッドを作成する

について__Thread例えば,同じ関数を作成する必要があります. この関数で,func1どこにいるの?func10 から 9 まで蓄積させることができます. 漸進的な蓄積プロセスを確認するために, func1 関数の for ループを使用して,毎回一時停止します (Sleep 関数は一定の数ミリ秒間眠るために使用されます).

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

HTTP リクエストを同時に こんな感じで作成できます

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

リソースをリクリームして実行結果を返すためにスレッド実行の終了を待つ

この例では,__threadJoin実行が完了するまで待つ. 変数ret返回値が表示されます.__threadJoin同時にスレッドを実行する特定の結果を観察できます. 実行するときに,

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

糸を強制的に終了し,資源を取り戻す

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
}

1秒間待った後 強制的にスレッドの実行を終了することができます. この例では,

スレッド間通信

スレッド間の通信は主に__threadPostMessage機能と__threadPeekMessage簡単な例を見てみましょう.

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

について__threadPostMessagefunction はスレッドにメッセージを送信するために使用される.最初のパラメータは送信する特定のスレッドのIDであり,第2パラメータは送信されるメッセージであり,文字列,値,配列,JSONオブジェクトなどである.メッセージは同時スレッド関数でメインスレッドに送信され,メインスレッドのIDは0と定義される.

について__threadPeekMessagefunction は特定のスレッドが送信したメッセージを監視するために使用されます.最初のパラメータはスレッドの特定のIDを監視することです.第2パラメータはタイムアウト時間を (ミリ秒で) 設定したり,ブロックすることを意味する -1 に設定したりできます.メッセージが来るまで戻らないでしょう.メインスレッドが現在のスレッドに送信したメッセージを並行スレッド関数で聞くことができ,メインスレッドのIDは0と定義されます.

もちろん,メインスレッドと通信する並行スレッドを除いて.並行スレッドも互いに直接通信することができます.

同期スレッド実行関数で現在のスレッドIDを取得する

この例では,var id = __threadId()使用されている場合,__threadId()この関数は,現在のスレッドのIDを取得できます.

共有スレッド間で格納される変数

スレッド間の通信に加えて,共有変数はインタラクションにも使用できます.

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
}

上記は,すべての関数の簡単な実証です. 少し複雑なテスト例を見てみましょう.

ネイティブマルチスレッド JavaScript と WASM の性能比較

このテスト戦略のアドレス:https://www.fmz.com/strategy/401463

このテスト戦略が何をしているのか 一目でわからないかもしれません.どうでもいいです.説明しましょう.まず,WASMとは何かを知ろう.

WebAssemblyWASM, WebAssembly新しい暗号化形式で ブラウザで実行できますWASMと使用できます.JavaScriptWASMは低レベルのアセンブリ言語のようなものです

テスト戦略は, wasm と javascript の実行効率を比較することである.しかし,比較するとき,両実行方法が順に実行され,それぞれにかかる時間が計算される.また,両実行方法が同時に実行することを許可することも可能で,統計は時間がかかります.現在,JavaScript 言語戦略の基本的並行実装がサポートされているため,テスト戦略は自然に比較し,同じアルゴリズムの実行速度を比較するために並行方法を使用します.

  • C言語バージョンのアルゴリズム,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);
}
  • アルゴリズムのJavaScript言語バージョン,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)
}

この2つのfib関数アルゴリズムの論理はまったく同じであることがわかります. 以下はテスト戦略のソースコードです.

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

簡単に言うと,WASMは実行効率が高いプログラムコードです.例では,Fibonacci数再帰アルゴリズムのc言語コードをWASMに変換します.プロセスは以下のようなものです:

  1. C言語の関数コードを WASMコードにコンパイルします

ウェブサイトで変換できます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. さらに,ワスムのコードを 6 桁の文字列に暗号化します.

次のコマンドを使用できます:

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

暗号化された六角文字列はlet data = 'data:hex,0061736d0100000001868...コードに書いてある

  1. この関数を使って,その関数をワームモデルに解析しますwasm.parseModule()FMZによって統合されています

  2. 関数で wasm モデルインスタンスを作成するwasm.buildInstance()FMZによって統合されています

  3. じゃあ電話してfibこの wasm モデルインスタンスの関数,すなわち:ret = instance.callFunction('fib', input).

テストを実行するための本物のボットを作成します.

このテスト戦略は,実際のボットテストにのみ使用できます.JavaScriptのマルチスレッド機能は,バックテストをサポートしていません.

wasmそしてJavaScript実行比較,最終的な実行結果:

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

明らかにwasm時間がかかりやすく より良いのです


関連性

もっと