Very interesting GAS performance results – run locally using htmlservice

A little while ago, I published an item on GAS performance, showing that a complex calculation test would take about 100 times as long on GAS as the same thing in regular javascript running locally on a browser, and 10 times as long as the same thing on VBA.

So the question is – is it really running the same thing? To make sure, I showed how to retrieve and run GAS code directly in the browser, bypassing GAS altogether.

As a final comparison, I’ll also show to use HTML service to cajole the GAS code to be run locally in the browser but, this time directed from GAS.


The test code

function testCompareSpeed() {
    Logger.log(speedTest());
}
function speedTest() {
  useTimer('x','10000 iterations of color comparison').start();
  for (var i = 1 ; i <= 10000 ;i++) {
    compareColors (i, VBCOLORS.vbWhite - i);
  }
  useTimer('x').stop();
  return useTimer().report();
}

GAS direct

Running directly in GAS – 26 seconds.
x : elapsed 26278 : iterations 1 : 10000 iterations of color comparison :

Locally on client, retrieving code from GAS

Running directly in GAS – 0.2 seconds.
x : elapsed 203 : iterations 1 : 10000 iterations of color comparison :

Locally on client, pushed by HTML service.

HTMLservice code will run clientside, as described here. So I reasoned that if I could publish the code, and cause it to be executed locally I should get results similar to the pure javaScript version – but I was wrong – 15 seconds is better than 26, but nowhere near 0.2.

Firstly, here’s the template you need to do this.

<html>
<head>
</head>
<body>
  <div>
  <script>
  // the source from GAS
  <?!= 
    getSources(["vEquivalents","usefulColors","hacks","timerStuff"],"mcpher"); 
   ?> 
   // do the test and report it
   alert (speedTest());
  </script>
  </div>

</body>
</html>

And the code

I use the same libraries as in  Publishing with Google Apps Script to play around with the GAS source, but here’s the specific code for populating the template above with the GAS scripts so that they can be run locally.

function doGet(e) {

    //----------
    var eArgs = {parameter:{template:"runonclient",sandboxmode:"native"}};
    // convert source to correct format
    var result = express(eArgs);
    // publish as appropriate
    var output= HtmlService.createHtmlOutput(result);
    if (eArgs.parameter.sandboxmode=="native") {
      output.setSandboxMode(HtmlService.SandboxMode.NATIVE);
    }
    return output;
}

 /* convert source code to required format
  * @param {object} e arg to doGet(e)
  * @return {object} the source expressed according the format
  */
function express(e) {

    // evaluate the template
    try {
      var template = HtmlService.createTemplateFromFile(e.parameter.template);
      return template.evaluate().getContent(); 
    }
    catch (err) {
      return  { error: 'error ' + err + 'reading template' + e.parameter.template};
    }
}

function getSources(modules,library) {
  var s="";
  for (var i=0; i < modules.length;i++) {
    var e = { parameter: { 
                source: "script", 
                library: library,
                module: modules[i],
                type:"javascript",
                template:""}};
    s+= getMySource(e).results.data;
  }         
  return s;
}
 /** gets the appropriate source for the combination of requested module and requested library
  * @param {String} content the web content 
  * @return {String} the escaped content
  */
function getSource() {
  return getMySource(eArgs);
}

// -- write a getMySource() to retrieve any modules in THIS script file
/* Returns a modules source code
 * @param {parameters} e the argument to doGet(e). should have module parameter specified
 * @return {object} The result.
 */

function getMySource(e) {
  // probably doesnt need changed, except if you need a custom function
  // the purpose of all this is to execute the scriptdb/scriptapp in the correct project according to the library selected
    try {
      return mcpher.getGeneralSource(e,
              e.parameter.source == 'script' ? 
                eval(mcpher.addLibraryPrefix(e,"showMyScriptAppResource"))(e.parameter.module) :
                ( e.parameter.source == 'scriptdb' ? 
                  eval( mcpher.addLibraryPrefix(e,"showMyScriptDb"))() : null
                 ) ,
              function (e) { // this is a custom function if you need one 
                         return null;} 
            );
    }
    catch (err) {
      return { error : err + " when trying to get module ", parameters : e.parameter };
    }
}

You can try it here, or look at it here.

Conclusion

Well I don’t really have a conclusion. Does anyone else have similar experiences? Here’s what I know
  • I am running exactly the same code in all 3 javascript versions, since the code is being served directly from GAS.
  • The VBA version is a simple translation
  • I notice that the cajoled GAS code has been caja-ized (if that’s a word) with oodles of code wrapped around it.
  • If I simplify the Maths, then GAS runs much more quickly – so I figured I’m probably introducing a memory leak that only manifests itself on GAS/Caja – yet the time taken is purely linear – 1000 iterations takes 10 times as long as a 100 – wouldn’t a memory leak be spiky or non-linear. How to debug that when it only appears if wrapped up in caja?

For more stuff like this, see from VBA to Google Apps Script, and for more detail on this post see Serving up javaScript from GAS

About brucemcp 225 Articles
I am a Google Developer Expert and decided to investigate Google Apps Script in my spare time. The more I investigated the more content I created so this site is extremely rich. Now, in 2019, a lot of things have disappeared or don’t work anymore due to Google having retired some stuff. I am however leaving things as is and where I came across some deprecated stuff, I have indicated it. I decided to write a book about it and to also create videos to teach developers who want to learn Google Apps Script. If you find the material contained in this site useful, you can support me by buying my books and or videos.