When developing strategies on FMZ using the JavaScript language, since the strategy architecture is polled. If there is a concurrent design scenario, the exchange.Go
function is used to make concurrent calls to some interfaces, so as to meet the requirements of some concurrent scenarios. But if you want to create a single thread to perform a series of operations, it is impossible. For example, like the Python language, use the threading
library to do some concurrent design.
Based on this requirement, the FMZ platform has upgraded the bottom layer of the system. True multithreading support has also been added to the JavaScript language. Detailed features include:
Next, I will take you to understand each function one by one.
The __Thread
function can create a thread and execute a function concurrently. For example, you need to create a concurrent function func1
, what does the func1
function do? We can let it accumulate from 0 to 9. In order to see the gradual accumulation process, we use the for loop in the func1 function to pause each time (the Sleep function is used to sleep for a certain number of milliseconds) for a certain period of time.
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 practical applications, we can make http requests concurrently like this:
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))
})
}
In the above example, we used the __threadJoin
function in the main function finally to wait for the concurrent threads to finish executing. The variable ret
receives the return value of the __threadJoin
function, and we print the return value, we can observe the specific results of the concurrent thread execution.
// 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
}
We still use the example just now, after creating a thread, you can forcibly terminate the execution of the thread after waiting for 1 second.
Inter-thread communication mainly uses the __threadPostMessage
function and the __threadPeekMessage
function. Let’s look at the following simple example:
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)
}
}
The __threadPostMessage
function is used to send a message to a thread. The first parameter is the ID of the specific thread to send to, and the second parameter is the message to be sent, which can be a string, a value, an array, or a JSON object and so on. Messages can be sent to the main thread in concurrent thread functions, and the ID of the main thread is defined as 0.
The __threadPeekMessage
function is used to monitor the message sent by a certain thread. The first parameter is to monitor the specific ID of the thread. The second parameter can set the timeout time (in milliseconds), or it can be set to -1, which means blocking, and it will not return until there is a message. We can listen to the message sent by the main thread to the current thread in the concurrent thread function, and the ID of the main thread is defined as 0.
Of course, except for concurrent threads communicating with the main thread. Concurrent threads can also communicate with each other directly.
In the above example, var id = __threadId()
is used, and the __threadId()
function can get the ID of the current thread.
In addition to communication between threads, shared variables can also be used for interaction.
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
}
The above is a simple demonstration of all functions. Let’s look at a slightly more complicated test example.
This test strategy address: https://www.fmz.com/strategy/401463
At a glance, you may not know what this test strategy does. It doesn’t matter, let’s explain it. First, let’s learn “what WASM is”.
WebAssembly
is WASM
, WebAssembly
is a new encoding format and can run in the browser, WASM
can be used with JavaScript
coexists, and WASM is more like a low-level assembly language.
Then the test strategy is to compare the execution efficiency of wasm and javascript, but when comparing, the two execution methods can be executed successively, and the time-consuming of each is counted. It is also possible to allow the two execution methods to execute concurrently, and the statistics are time-consuming. Now that the underlying concurrency implementation of the JavaScript language strategy has been supported, the test strategy uses a concurrent method to compare naturally and compare the execution speed of the same algorithm.
// Recursive algorithm of Fibonacci Numbers in C Language
int fib(int f) {
if (f < 2) return f;
return fib(f - 1) + fib(f - 2);
}
// 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)
}
It can be seen that the logic of the two fib function algorithms is exactly the same. The following is the source code of the test strategy:
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
})
}
Simply put, WASM is a program code with higher execution efficiency. In the example, we convert the c language code of “Fibonacci number recursive algorithm” into WASM. The process is like this:
We can use the website to convert: 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);
}
The following commands can be used:
python -c "print('data:hex,'+bytes.hex(open('program.wasm','rb').read()))"
The encoded hex string is let data = 'data:hex,0061736d0100000001868...
in the code.
Then parse it into a wasm model through the function wasm.parseModule()
integrated by FMZ.
Create a wasm model instance through the function wasm.buildInstance()
integrated by FMZ.
Then call the fib
function in this wasm model instance, namely: ret = instance.callFunction('fib', input)
.
This test strategy can only be used for real bot testing. JavaScript multi-threading functions do not support backtesting so far.
wasm
and JavaScript
execution comparison, the final execution results:
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
It seems that wasm
takes less time and is better.