Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

long delay before and after script execution #5

Open
andykais opened this issue Mar 11, 2021 · 11 comments
Open

long delay before and after script execution #5

andykais opened this issue Mar 11, 2021 · 11 comments
Labels
bug Something isn't working help wanted Extra attention is needed

Comments

@andykais
Copy link

I have noticed that importing this library will cause a long delay before my script is executed. I assume the delay before the script executes is because the wasm file is dynamically imported automatically

const Canvas = await lib.CanvasKitInit({}) as CanvasKit;

I do not know why there is an extra delay after my script executes though.

To illustrate how long the delays are, here is a simple benchmark. First the script using your library:

import Canvas from 'https://deno.land/x/canvas@v1.1.1/mod.ts'
console.log(Date.now(), 'imports complete')


const canvas = Canvas.MakeCanvas(200, 200);
const ctx = canvas.getContext('2d');
console.log(Date.now(), 'created canvas & context')

ctx.fillStyle = 'red';
ctx.fillRect(10, 10, 200 - 20, 200 - 20);

await Deno.writeFile('image.png', canvas.toBuffer())
console.log(Date.now(), 'deno wrote buffer to png')

and a bash script timing the usage:

#!/bin/bash

millis() {
  echo $(($(date +%s%N)/1000000)) $1
}

deno install -f --allow-write ./canvas.ts
millis 'before script'
canvas
millis 'after script'

Here is the output:

./timer.sh
✅ Successfully installed canvas
/home/andrew/.deno/bin/canvas
1615473303762 before script
1615473305209 imports complete
1615473305213 created canvas & context
1615473305251 deno wrote buffer to png
1615473308244 after script

Thats a 1.447 second delay during import and a 2.993 second delay after the script finishes executing.

If these delays are absolutely necessary, it would be nice if we could at least control the lifecycle. E.g. instead of implicit importing and cleanup, expose an api for loading and cleanup:

import Canvas from 'https://deno.land/x/canvas@v1.1.1/explicit_lifecycle.ts'

// we choose when to begin the slow load
await Canvas.load()
const canvas = Canvas.MakeCanvas(200, 200);
// do some stuff with the canvas...

// we choose when to perform cleanup
await Canvas.unload()
@DjDeveloperr
Copy link
Owner

DjDeveloperr commented Mar 12, 2021

Not really sure about this one; this library is a port of Canvaskit-WASM. Just tested it seems to add some extra cleanup time which might be some WASM related going on behind the scenes.

I can provide a load or init function (as the original library does), would it suffice?
Actually I'll dig up a bit more and see if I can provide method to offload the WASM.

@andykais
Copy link
Author

possibly. Ill say that adding a load function (while a nice interface) is not strictly necessary since I can do a dynamic import if I want to delay the wasm init:

class Context {
  Canvas: CanvasKit
  init() {
    this.Canvas = await import('https://deno.land/x/canvas@v1.1.1/mod.ts')
  }
}

the real kicker is that mandatory cleanup step. I dont know a way to control that lifecycle currently, and when using this package in command line scripts, its a pretty bad user experience to wait an extra 3 seconds before exiting a script

@andykais
Copy link
Author

so I just found something interesting. I modified the benchmark a tad:

import Canvas from 'https://deno.land/x/canvas@v1.1.1/mod.ts'
console.log(Date.now(), 'imports complete')

const canvas = Canvas.MakeCanvas(200, 200)
await new Promise(resolve => setTimeout(resolve, 3000))
const ctx = canvas.getContext('2d')
console.log(Date.now(), 'created canvas & context')

ctx.fillStyle = 'red'
ctx.fillRect(10, 10, 200 - 20, 200 - 20)

await Deno.writeFile('image.png', canvas.toBuffer())
console.log(Date.now(), 'deno wrote buffer to png')

now I arbitrarily wait 3 seconds right after instantiating the canvas and....

 andrew  deno-wasm  ./timer.sh
Check file:///home/andrew/Code/scratchwork/deno-wasm/deno-wasm.ts
✅ Successfully installed deno-wasm
/home/andrew/.deno/bin/deno-wasm
1615929552793 before script
1615929554186 imports complete
1615929557202 created canvas & context
1615929557209 deno wrote buffer to png
1615929557238 after script

there is no cleanup! I guess what was actually happening was some background canvaskit loading was still happening in the background after the script finished executing. Not sure what the intent was there, maybe just load the necessary stuff up-front, and load the other parts slowly. Itd be great if there was some promise somewhere that kept track of when things are still being loaded

@littledivy
Copy link

littledivy commented Mar 22, 2021

Components of WASM cannot lazy load neither can it be split into components. I generally use a WebWorker with deno_canvas while the rest of my application does its job in parellal.
Related links:
rustwasm/team#52
WebAssembly/design#1166

@andykais
Copy link
Author

@littledivy thats good to know, but I dont actually have a problem with loading the whole wasm module. What I have a problem with, is that there doesnt appear to be a way of indicating that the wasm module has fully loaded. It just sort of starts loading and then finishes loading around 3 seconds after the import completes

@DjDeveloperr
Copy link
Owner

DjDeveloperr commented Mar 23, 2021

I'm not sure what exactly is lazy loaded by canvaskit. But maybe I can do a custom build to strip off the other things (related to Skia API) and reduce size of wasm?

@DjDeveloperr
Copy link
Owner

Found the cause of this delay. It is caused by the decoding of wasm base64 which approximately takes 2.4s. We really need wasm imports at this point...

@DjDeveloperr
Copy link
Owner

Also found a fix! But kinda hacky. Reduced the decoding overhead from 2.8s to 0.5s by using internal op_base64_decode instead of std's base64 decode which is pure js.

@DjDeveloperr
Copy link
Owner

DjDeveloperr commented Jun 24, 2021

Alright. Tagged 1.2.2 with the overhead decreased to 1s, is that okay? I think only thing we can do further is to strip off the unused things off the WASM binary which aren't needed for this library.

@DjDeveloperr DjDeveloperr added bug Something isn't working help wanted Extra attention is needed labels Jun 24, 2021
@andykais
Copy link
Author

this is very cool. Likely this is fine now, but I am curious what exactly I was reporting in my benchmarks. According to your comment:

Components of WASM cannot lazy load neither can it be split into components.

and yet there was some definite background-startup processing happening when canvas first loaded

@DjDeveloperr
Copy link
Owner

DjDeveloperr commented Jun 24, 2021

Most of the time as noticed in your benchmarks was taken by base64 decoding, at startup. WASM instantiation takes like 0.6s IIRC. Not sure about "after script execution", but I suppose that might be background cleanup done for wasm at end by v8 or something.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

3 participants