DelSkayn / rquickjs

High level bindings to the quickjs javascript engine
MIT License
434 stars 59 forks source link

AsyncContext not running parallel #243

Closed JameEnder closed 2 months ago

JameEnder commented 6 months ago

Hello, I'm running rquickjs with the futures and parallel features, with #[tokio::main(flavor = "multi_thread", worker_threads = 4)], this is an example of what I'm trying to do:

// Just some urls
let urls = vec![...];

let runtime = AsyncRuntime::new()?;
let context = AsyncContext::full(&runtime).await?;

let mut handles = vec![];

for url in &urls {
  let context = context.clone();

  let handle = tokio::task::spawn(async move {
    let response = reqwest::get(&url).await?;
    let content = response.text().await?;

    async_with!(context => |ctx| {
      ctx.globals().set("PAGE", content)?;
      ctx.clone().eval::<(), _>("$ = cheerio.load(PAGE)")?;

      Ok(url)
    }).await
  })  

  handles.push(handle);
}

let results = futures::future::join_all(handles).await;

But for some reason, the requests are running in parallel, but the ctx.eval is not, and it seems very slow compared to running it in node (maybe a QuickJS bottleneck?).

I probably don't understand something about the rust async runtime, but why isn't the async_with!() part parallel in this case?

DelSkayn commented 6 months ago

Quickjs is single threaded and has no support for running things in parallel. So rquickjs locks a mutex whenever the Quickjs runtime is used to ensure that no two threads can access the same runtime at the same time. You can create multiple runtimes but runtimes can't share JS objects.

Also regarding speed, Quickjs is a pretty small js engine compared to node and its engine V8. Unlike V8 it does not try to JIT compile javascript and is therefore an order of magnitude slower. If you need V8 like performance Quickjs is just not the right engine.

DelSkayn commented 2 months ago

Closing this issue for now for inactivity.