Code Monkey home page Code Monkey logo

Comments (10)

kleisauke avatar kleisauke commented on June 28, 2024 1

Hopefully, in the future, wasm-vips can leverage Igalia's "Ref C++", which manages the lifetime of objects in a more convenient way.
https://github.com/Igalia/ref-cpp#destructors-and-finalizers

You're right that there's a lack of documentation on this subject in the meantime. I'll have a look at adding this, hence the "documentation"-label.

vips.shutdown() is only useful on non-web environments. It was added to shutdown Emscripten's thread pool, which would otherwise prevent the event loop from exiting.

Note that setAutoDeleteLater() is currently opt-in, so you don't need to pass false to that function outside the playground.

from wasm-vips.

kleisauke avatar kleisauke commented on June 28, 2024 1

v0.0.2 now available with a fix for this. Docs for memory management have yet to be created.

from wasm-vips.

josephrocca avatar josephrocca commented on June 28, 2024

The page seems to use up a lot of memory according to Chrome's task manager. It grows to 1.2GB usage at 131 images, and then crashes. I thought it might have had to do with creating all those PNG blobs and not revoking the URLs after processing, but removing the URL.createObjectURL stuff (so loop body is just fetch and then bicubicResizeAndCenterCrop) doesn't fix it.

image

from wasm-vips.

kleisauke avatar kleisauke commented on June 28, 2024

Hi @josephrocca,

Currently it's required to call delete() on every image instance that is returned from wasm-vips. The Emscripten docs also provides some explanation for this:
https://emscripten.org/docs/porting/connecting_cpp_and_javascript/embind.html#memory-management

It's very inconvenient with operation chains, unfortunately. The emscripten-auto-deletelater.patch patch (which I still need to upstream) makes this less difficult by calling deleteLater() automatically during the construction of handles, you only need to do this within Module.preRun:

module.setAutoDeleteLater(true);
module.setDelayFunction(fn => {
globalThis.cleanup = fn;
});

After that, you can call cleanup() to clean up the used resources. For example, the playground does this before each run in order to clean up the previous run (if any):

if (typeof cleanup === 'function')
cleanup();

This is also used within unit tests, where resources are cleaned up after each test:

afterEach(function () {
cleanup();
});

Given this information, I could resolve this locally by doing this within that playground link:

@@ -10,6 +10,7 @@
         img.title = url;
         document.getElementById('output').appendChild(img);
         console.log(nProcessed++, url);
+        cleanup();
     }
     console.log("FINISHED");
 })();

Note that I haven't documented this yet because I wasn't sure if Embind will be used in later versions, see:
https://github.com/kleisauke/wasm-vips/projects/1#card-44661960

from wasm-vips.

josephrocca avatar josephrocca commented on June 28, 2024

@kleisauke Ahh, I see thanks for that. If I want to just "deal with" the chaining inconvenience so I can do the cleanup with the bicubicResizeAndCenterCrop function, how would I do that? The code below throws "BindingError: Object already scheduled for deletion" at im1.delete().

async function bicubicResizeAndCenterCrop(blob) {
    let im1 = vips.Image.newFromBuffer(await blob.arrayBuffer());

    // Resize so smallest side is 224px:
    let resizeFactor = 1;
    if (im1.width > im1.height) {
        resizeFactor = 224 / im1.height;
    } else {
        resizeFactor = 224 / im1.width;
    }

    let im2 = im1.resize(resizeFactor, { kernel: vips.Kernel.cubic });

    // crop to 224x224:
    let left = (im2.width - 224) / 2;
    let top = (im2.height - 224) / 2;
    let im3 = im2.crop(left, top, 224, 224)

    let outBuffer = new Uint8Array(im3.writeToBuffer('.png'));
    im1.delete();
    im2.delete();
    im3.delete();
    return new Blob([outBuffer], { type: 'image/png' });
}

Also, not on topic, but maybe not big enough for it's own issue: Should the vips.shutdown() method return a promise? (and should I be awaiting the deletes above?)

from wasm-vips.

kleisauke avatar kleisauke commented on June 28, 2024

setAutoDeleteLater(true) and delete() are mutually exclusive. You'll need to pass the ?disableAutoDelete query string in that playground if you want to control memory management using delete().

Should the vips.shutdown() method return a promise? (and should I be awaiting the deletes above?)

I'm not sure if vips.shutdown() can be made asynchronous. Both calls needs probably be done synchronous.

EM_JS(void, shutdown_js, (), {
exitRuntime();
});

https://github.com/emscripten-core/emscripten/blob/eea4056bb71493ae388a1cc22b2bda07cc4932ef/src/preamble.js#L399
https://github.com/emscripten-core/emscripten/blob/eea4056bb71493ae388a1cc22b2bda07cc4932ef/src/embind/embind.js#L1876

from wasm-vips.

josephrocca avatar josephrocca commented on June 28, 2024

Ah gotcha, thanks! I wonder if this could all be made more intuitive with WeakRefs? In any case, in the meantime it would definitely be worth adding a note about deletion stuff to the readme, because it's unusual in JavaScript land to have to worry about memory management, so I'm guessing most people would assume that image objects are garbage collected automatically. Thanks again!

Oh, and RE the shutdown question, I thought maybe the workers wouldn't have been guaranteed to be terminated after vips.shutdown() has been executed. But if it can all be done synchronously, then that's fine. (Or maybe I misunderstand what the shutdown method is supposed to do)

from wasm-vips.

josephrocca avatar josephrocca commented on June 28, 2024

@kleisauke Looks like I still have a leak somewhere. Here's a minimal reproduction - it's basically the same code as before, but I'm just loading one image and resizing it over and over.

Accoding to Chrome's Task Manager, the tab starts out with a memory footprint of about 200MB and steadily grows to ~1GB after processing ~7000 iterations and then crashes with the same OOM error:

image

If there's a deeper problem at play here that could take some time to fix, then is vips.shutdown() the right method to use if I want to e.g. restart vips every 1000 iterations? And if so, is there a way to find out when vips is finished disposing/terminating all the workers/modules/memory? Or does vips.shutdown() somehow do it synchronously?

Edit: If I add the code below within the for loop, then it gets to about 13000 iterations, but then the whole browser tab crashes, instead of just throwing an error in the console.

if(nProcessed % 1000 === 0) {
  vips.shutdown();
  await new Promise(r => setTimeout(r, 5000));
  vips = await Vips();
  vips.EMBIND_AUTOMATIC_DELETELATER = false;
}

from wasm-vips.

kleisauke avatar kleisauke commented on June 28, 2024

I could reproduce this and was able to fix it with commit 89e167f. Here's an updated playground link that now runs steady for me. Thanks for reporting this!

Regarding, vips.shutdown(), I would use it as a last resort on the web. I'm not sure if it's safe to restart libvips in WebAssembly, as vips_init() can't be called after vips_shutdown(). That's probably why the browser tab crashes.

As an aside, commit 02b9215 replaces the EMBIND_AUTOMATIC_DELETELATER property with the setAutoDeleteLater function. I updated my previous comments to reflect this.

from wasm-vips.

josephrocca avatar josephrocca commented on June 28, 2024

Great, looking good so far on my end. Thanks for working to fix that so quickly!

It would definitely be nice to have some sort of vips.destroy()/vips.dispose() method that completely cleans up and removes wasm-vips in a "safe" way. This would be especially important in long-running single-page applications where, e.g. wasm-vips may only be required temporarily for a small part of the app's functionality.

(P.S. Thanks for that sneaky scale factor simplification ^^)

from wasm-vips.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.