node-gtk icon indicating copy to clipboard operation
node-gtk copied to clipboard

Improve startup-time

Open harisvsulaiman opened this issue 6 years ago • 5 comments

Using

    console.time(`time for ${ns}`)
    for (let i = 0; i < nInfos; i++) {

        const info = GI.Repository_get_info.call(repo, ns, i);
        const item = makeInfo(info);

        if (item)
            module[getInfoName(info)] = item
    }

    try {
        const override = require(`./overrides/${[ns, version].join('-')}.js`)
        override.apply(module)
    } catch(e) {
        try {
            const override = require(`./overrides/${ns}.js`)
            override.apply(module)
        } catch(e) { /* No override */ }
    }
    console.timeEnd(`time for ${ns}`)

I got the following result ( might not be accurate )

time for cairo: 19.651ms
time for GLib: 207.643ms
time for GObject: 58.584ms
time for Pango: 81.324ms
time for xlib: 1.424ms
time for Gio: 260.415ms
time for GModule: 1.616ms
time for GdkPixbuf: 25.397ms
time for Gdk: 210.193ms
time for Atk: 55.839ms
time for Gtk: 660.253ms

Can we improve this result using lazy initialization using proxies? for example

 let proxy = new Proxy(module, {
    get: (obj, prop) => {
      if (!obj[prop]) {
        const info = GI.Repository_find_by_name(repo, ns, prop);
        const item = makeInfo(info);
        if (item) obj[prop] = item;

        try {
          const override = require(`./overrides/${[ns, version].join("-")}.js`);
          override.apply(module);
        } catch (e) {
          try {
            const override = require(`./overrides/${ns}.js`);
            override.apply(module);
          } catch (e) {
            /* No override */
          }
        }
      }
      return obj[prop];
    }
  });

  module = moduleCache[ns] = proxy;

@romgrk @WebReflection

harisvsulaiman avatar Mar 06 '19 14:03 harisvsulaiman

I also dislike the startup time, but I would tend to think that it won't be as simple as that.

The reason is that sometimes a GObject class (or boxed class) will be used from the C++ side without being called from the Javascript side.

For this example, it would work:

const module = gi.require('SomeModule')
const glork = new module.Glork() 

For this one, it wouldn't, because the proxy property is never accessed:

const module = gi.require('SomeModule')
const object = module.getSomeGlork() // => function returning an instance of module.Glork
// C++ would expect to be able to call Nan::NewInstance(glork_class, []);
// also C++ doesn't use the moduleCache object to retrieve the correct class,
// it uses the GType with gtype_get_qdata, a faster hashmap.
//
// Note: some exceptional boxeds don't have a GType, those ones are accessed
//       through the moduleCache object from C++

Another thing that needs to be pointed is that the initialization process becomes a bit more tricky with #93, because some classes are created from the C++ side, not the JS side. (this is because Cairo isn't an introspectable library even if it's the backbone of Gtk ; therefore it needs to be manually binded).

So basically we have C++ defining classes directly on the module object, those ones will be a bit harder to lasy-load (though not impossible). E.g.:

void Surface::Initialize(Nan::ADDON_REGISTER_FUNCTION_ARGS_TYPE target) {
  Nan::Set (target, Nan::New ("Surface").ToLocalChecked(), Surface::GetConstructor());
  Nan::Set (target, Nan::New ("ImageSurface").ToLocalChecked(), ImageSurface::GetConstructor());
  Nan::Set (target, Nan::New ("RecordingSurface").ToLocalChecked(), RecordingSurface::GetConstructor());
}

I have been thinking about this one for a while, but it needs more thought to be implemented properly. A different route that I know exists is V8 snapshots, basically a snapshot of some context. It's taken once and then it's directly loaded in memory in subsequent runs. Though I'm not sure if it's possible in this case, it might be too tricky for snapshots.

romgrk avatar Mar 06 '19 19:03 romgrk

FWIW same issues when I first tried with Proxies (years ago) on the JS side only, from time to time some expected class was failing 'cause ... unexpectedly not loaded yet.

Up to 1 sec for UI bootstrap doesn't feel too bad though

WebReflection avatar Mar 06 '19 19:03 WebReflection

@romgrk i used https://github.com/zeit/ncc to shave off 200ms from the startuptime.

harisvsulaiman avatar Mar 09 '19 06:03 harisvsulaiman

@harisvsulaiman whoaa, cool! That's an interesting result. I'll check if I have some time to set it up this week (and as always, I accept PRs).

romgrk avatar Mar 12 '19 18:03 romgrk

@harisvsulaiman cool, thanks for mentioning the https://github.com/zeit/ncc

dhonx avatar Sep 02 '19 07:09 dhonx