자바스크립트 멀티 스레딩 지원으로 동시 전략 실행

저자:리디아, 창작: 2023-03-07 15:12:04, 업데이트: 2023-09-18 20:04:21

img

전략 프로그램이 동시에 실행되고, 시스템 하단에 다중 스레드 지원을 추가 하 여 자바스크립트 전략

FMZ에 전략을 개발할 때 자바스크립트 언어를 사용 하 여 전략 아키텍처가 조사되기 때문에. 동시 설계 시나리오가 있다면,exchange.Go이 함수는 일부 인터페이스에 동시 호출을 하기 위해 사용 됩니다. 따라서 일부 동시 시나리오의 요구 사항을 충족 합니다. 하지만 일련의 작업을 수행하기 위해 단일 스레드를 만들고 싶다면 불가능합니다. 예를 들어 파이썬 언어처럼,threading라이브러리에서 동시에 디자인할 수 있습니다.

이 요구 사항에 따라 FMZ 플랫폼은 시스템의 하층 층을 업그레이드했습니다. 진정한 멀티 스레딩 지원 또한 자바스크립트 언어에 추가되었습니다. 자세한 기능에는 다음과 같습니다.

  • 사용자 정의 함수를 동시에 실행하는 스레드를 생성합니다.
  • 스레드 간 통신
  • 공유된 스레드 사이에 저장된 변수
  • 스레드가 실행을 마칠 때까지 기다려서 리소스를 검색하고 실행 결과를 반환합니다.
  • 강제적으로 끝내고 자원을 되찾아
  • 동시 스레드 실행 함수에서 현재 스레드 ID를 얻으십시오.

다음으로, 각각의 함수를 하나씩 이해하도록 하겠습니다.

사용자 정의 함수를 동시에 실행하는 스레드를 생성합니다

__Thread함수는 스레드를 생성하고 함수를 동시에 실행할 수 있습니다. 예를 들어, 동시 함수를 생성해야 합니다.func1, 무슨 일이func1함수를 어떻게 할 수 있을까요? 우리는 0에서 9까지 축적하도록 할 수 있습니다. 점진적인 축적 과정을 보기 위해, 우리는 fun1 함수의 for 루프를 사용하여 특정 기간 동안 매번 일시 중지합니다.

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이며 두 번째 매개 변수는 문자열, 값, 배열 또는 JSON 객체 등이 될 수 있는 메시지를 전송합니다. 메시지는 동시 스레드 함수에서 메인 스레드에 전송 될 수 있으며 메인 스레드의 ID는 0으로 정의됩니다.

__threadPeekMessagefunction는 특정 스레드에서 보낸 메시지를 모니터링하는 데 사용됩니다. 첫 번째 매개 변수는 스레드의 특정 ID를 모니터링하는 것입니다. 두 번째 매개 변수는 타임 아웃 시간을 설정할 수 있습니다 (밀리 초에), 또는 차단을 의미하는 -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
}

위의 것은 모든 함수의 간단한 시연입니다. 조금 더 복잡한 테스트 예제를 살펴보겠습니다.

네이티브 멀티 스레드 자바스크립트와 WASM의 성능 비교

이 테스트 전략 주소는:https://www.fmz.com/strategy/401463

한눈에 이 테스트 전략이 무엇을 하는지 모르실지도 모릅니다. 상관없습니다. 먼저 WASM가 무엇인지 알아봅시다.

WebAssemblyWASM, WebAssembly새로운 암호화 형식이고 브라우저에서 실행할 수 있습니다.WASM사용 가능 합니다.JavaScriptWASM은 더 낮은 수준의 어셈블러 언어입니다.

다음 테스트 전략은 wasm와 자바스크립트의 실행 효율을 비교하는 것이지만, 비교할 때 두 실행 방법을 순차적으로 실행할 수 있으며, 각각의 시간 소비가 계산됩니다. 두 실행 방법을 동시에 실행하도록 허용하는 것도 가능하며, 통계는 시간이 많이 걸립니다. 자바스크립트 언어 전략의 기본 동시 구현이 지원되었으므로, 테스트 전략은 동시 방법을 사용하여 자연스럽게 비교하고 동일한 알고리즘의 실행 속도를 비교합니다.

  • 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);
}
  • 알고리즘의 자바스크립트 언어 버전, 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)
}

두 개의 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은 높은 실행 효율을 가진 프로그램 코드입니다. 예제에서, 우리는 피보나치 숫자 재귀 알고리즘의 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 모델로 분석합니다wasm.parseModule()FMZ에 의해 통합됩니다.

  2. 함수를 통해 wasm 모델 인스턴스를 생성wasm.buildInstance()FMZ에 의해 통합됩니다.

  3. 그럼 전화해fib이 wasm 모델 인스턴스에서 함수, 즉:ret = instance.callFunction('fib', input).

테스트를 실행하기 위해 실제 봇을 만들자

이 테스트 전략은 실제 봇 테스트에서만 사용할 수 있습니다. 자바스크립트 멀티 스레딩 기능은 지금까지 백테스팅을 지원하지 않습니다.

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시간이 덜 걸리고 더 낫습니다.


관련

더 많은