Comparing Javascript components and modules

By | July 20, 2013

The code that I develop for my ExQuilla addon uses a combination of C++ and javascript-based code, so I am constantly having to deal with interactions between those languages in a Mozilla Thunderbird binary extension. So I thought it would be a good idea to test the relative speed of two ways of writing javascript code that can also be accessed from C++:

  1. Using a javascript-based XPCOM component for both, or
  2. Using a javascript module for javascript callers, and a javascript component calling into a module for C++ users.

The second option is somewhat unusual, and represents an attempt to combine the speed of a javascript module with the C++ accessibility of a XPCOM component. A little code might make this clearer.

This is the basic code for a do-nothing javascript-based component that implements  a simple interface (nsIMsgPurgeService that has a simple init() and shutdown()  method.):

// speed test
function speedTestComponent()
{
/*
  if (typeof (SpeedTestModule) == "undefined")
    Cu.import("resource://exquilla/speedTestModule.js");
  SpeedTestModule.call(this);
  this.__proto__ = SpeedTestModule.prototype;
/* include the above for hybrid component/module */
}

speedTestComponent.prototype = {
  classID:          Components.ID("{00EDFFE3-8533-4bb1-81D2-DE2A6AC24892}"),
/**/
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIMsgPurgeService]),
  init: function _init() {},
  shutdown: function _shutdown() {},
/* include the above for a pure component, or a pure module*/
}

The code for SpeedTestModule is essentially identical to the pure component case.

So why would I have a component call into a module, instead of just including that code directly in the component? The theory was that modules are more efficient than XPCOM components in javascript, so having this architecture would allow me to use the component version when required to call into this code from C++, but I could use the module version directly from javascript code, thereby bypassing the XPCOM overhead.

So how did that work out in practice? I tried running the dummy init() function 10,000 times, using each of the three methods (as a pure component, as a pure module, and as a component that called into a module.).

The resulting times (smaller is better):

Pure component: 257 milliseconds

Pure module: 185 milliseconds

Hybrid (component calling into a module): 440 milliseconds

The traditional way to write code the needs to be accessed by both javascript and C++ is to write it as a pure component, while if the code was only intended for use with javascript a module would be preferred. Comparing the overhead of the making the call with these two methods, the module reduces the  call overhead by 26%, so that 26% is the cost of making this code accessible from both C++ and javascript (26% = (257 – 189)/257).

How about the hybrid method? In that case, javascript users would see the overhead for the pure module case, so they would see a 26% improvement, but C++ users would see the overhead for the hybrid case, which is a whopping 71% higher for the hybrid compared to the pure component (71% = (440 – 257)/257).

Conclusion: the hybrid format probably does not make sense as a generic pattern. It would only make sense if javascript calls were much more frequent than C++ calls, or if the module form already exists, and the goal is to provide access to the occasional C++ user.