Performance problem with large JavaScript blob

I’m not sure if this is the right place to ask this, but I’m seeing a very large, ~20x difference in JavaScript performance between Chrome and Firefox on a large JavaScript blob.

I have an OCaml playground running in the browser. It’s about ~20mb of JavaScript and contains most of the OCaml compiler. Chrome takes ~3s on first compile for any of the sample programs and Firefox ~57s. Subsequent compiles are ~1s for Chrome and ~20s for Firefox. If you select a sample from the drop-down and click “run” eventually some text in the top-right will show the compilation time.

I also captured a profile using and there were extremely deep call stacks from the compilers type checker [probably because Js_of_ocaml failed to optimize the mutally recursive calls] which seemed to be where time was being spent. Also, I should note that once the programs are compiled they seem to run at comparable speeds in Chrome vs Firefox (at least the frame rate is similar).

I assume I’m hitting some edge case of SpiderMonkey. Are there any workarounds I could do?

Thanks for the report!

I filed a Bugzilla bug:

It looks like the JS code is throwing a lot of exceptions and we’re spending time capturing stack traces. Are these exceptions expected? Exception handling is a relatively slow path in JS engines.

Amazing! Thank you for the quick response and fast resolution!

I believe it is expected that the code is throwing a lot of exceptions. Exceptions in OCaml are very cheap and since it lacks return or break it’s often used for control flow. It’s also used to provide defaults for missing values, for example this is a common pattern in the wild:

let lookup tbl x =
  try Hashtbl.find tbl x
  with Not_found -> true (* or some default *)

which translates to the following JavaScript when using Js_of_ocaml.

lookup =
      {var _ceh_=caml_call2(include$9[6],tbl,x);return _ceh_}
          {_cei_ = caml_wrap_exception(_cei_);
             if(_cei_ === Not_found)return 1;
             throw _cei_}},

For example one of the offenders appears to be this function from the compiler:

which translates to:

function _aaW_(s,path)
   {var val=caml_call2(Map$2[28],path,s[1])}
   {_gVz_ = caml_wrap_exception(_gVz_);
    if(_gVz_ === Not_found)
      {case 0:return path;
       case 1:var n=path[2],p=path[1];return [1,module_path(s,p),n];
       default:return fatal_error(_aaX_)}
    throw _gVz_}
  if(0 === val[0]){var p$0=val[1];return p$0}
  throw [0,Assert_failure,_aaY_]}
function type_path(s,p)
 {var match=constructor_typath(p);
   {case 0:var p$0=match[1];return _aaW_(s,p$0);
    case 1:
     var cstr=match[2],p$1=match[1];return [1,module_path(s,p$1),cstr];
    case 2:return _aaW_(s,p);
     var cstr$0=match[2],ty_path=match[1];
     return [1,_aaW_(s,ty_path),cstr$0]}}

This should be fixed in the latest Firefox Nightly build (you can download it here).

Thanks again for the report and please let us know if you notice other (performance) issues :slight_smile:

1 Like

Awesome, thank you so much! I downloaded the latest Nightly and the compilation times are now on par with Chrome! Which is wonderful, as FF is my daily driver.

Haha, if we are on the subject of performance, I do have another demo where I see ~2x slower frame times in Firefox vs Chrome:

In Nightly I see ~12-14ms per frame in JS and then some extra time for Firefox platform to do some painting related tasks.

In Chrome it’s about 5-6ms per frame in JS, and ~0.4ms GPU time (but I’m unsure of any platform timings).


Firefox (profile link for FF)

These profiles were taken on Linux with a MacBook Pro Late 2013 laptop.
Overall I’m happy with both browser performances in this scenario, it’s just another thing I noticed.

1 Like

Haha, if we are on the subject of performance, I do have another demo where I see ~2x slower frame times in Firefox vs Chrome:

I see a lot of graphics time in your profile, is that with the latest driver version? On my Mac it’s pretty smooth. You could also test if setting the webgl.out-of-process pref to true in about:config makes a difference. That’s enabled on Mac and Windows but not yet on Linux (there might be issues with it still).

1 Like

Ah, thank you! I tried the webgl.out-of-process preference and it was even slower. I think I’m being hit by Looking at the profile again I see a readPixels call in the Firefox platform. I’m guessing that’s the “readback” for the framebuffer. If I make the window smaller the framerate goes back to 60fps, so I’m guessing that’s my issue.

I’ll play around with some settings to see if I can coax it to use the fast path. Thanks again for your help! I’ll let you know if I find any more JS slowdowns :slight_smile:

1 Like