Open Bug 1723891 Opened 4 years ago Updated 3 years ago

outlook.live.com responsiveness login is bad at login time

Categories

(Core :: JavaScript Engine, defect, P3)

Firefox 90
Unspecified
Windows 10
defect

Tracking

()

Performance Impact low
Tracking Status
firefox90 --- affected

People

(Reporter: karlcow, Unassigned)

References

(Blocks 1 open bug, )

Details

(Keywords: perf:pageload)

  1. Go to https://outlook.live.com/
  2. log in

Expected:
Quick display of content to be able to use the email

Actual:
Slower and less responsive than Blink browsers

Here a performance profile

The site is working, but the impression is that it is really slower than an equivalent blink browser.

(In reply to Karl Dubost💡 :karlcow from comment #0)

Here a performance profile

Looks like you forgot to include one. Could you recapture and post the profile here? :)

Flags: needinfo?(kdubost)
Webcompat Priority: P1 → ?

So I recorded a profile again.
https://share.firefox.dev/3F3VObr

While it takes around 10s for Firefox to display the image with the mobile phones
It takes around 5s for Edge on blink.

Flags: needinfo?(kdubost)

Thanks. Here's the most relevant time period from the profile, I think -- the ~10 seconds from blank page to showing image content in the main inbox area: https://share.firefox.dev/39Y4O3q

Looks like nearly all of our samples are in JavaScript, so let's classify this as a JavaScript Engine bug for the time being.

Component: Performance → JavaScript Engine
Whiteboard: [qf] → [qf:p3:pageload]
Severity: -- → S3
Priority: -- → P3

I have not compared to chrome, but only looking at the profile, it seems that we 2 different issues:

  • There is the script delazification, which should be addressable with Bug 1715976
  • There is the c function which seems to be quite high during the time where JS holds the CPU, which deserve some additional investigation.

Iain, can you look at the c function mentioned above, and see if there is something which might be optimized in it.

Flags: needinfo?(iireland)

I've worked out that c appears to be getVisitFn from graphql-js. The typescript source is here; here's the post-transpilation JS (taken from the source map):

export function getVisitFn(visitor, kind, isLeaving) {
  var kindVisitor = visitor[kind];

  if (kindVisitor) {
    if (!isLeaving && typeof kindVisitor === 'function') {
      return kindVisitor;
    }

    var kindSpecificVisitor = isLeaving ? kindVisitor.leave : kindVisitor.enter;

    if (typeof kindSpecificVisitor === 'function') {
      return kindSpecificVisitor;
    }
  } else {
    var specificVisitor = isLeaving ? visitor.leave : visitor.enter;

    if (specificVisitor) {
      if (typeof specificVisitor === 'function') {
        return specificVisitor;
      }

      var specificKindVisitor = specificVisitor[kind];

      if (typeof specificKindVisitor === 'function') {
        return specificKindVisitor;
      }
    }
  }
}

Nothing obvious leaps out at me there.

The other hot functions associated with c are enter, leave, and visit (s in the profile):

return {
    enter: function enter(node) {
      for (var i = 0; i < visitors.length; i++) {
        if (skipping[i] == null) {
          var fn = getVisitFn(visitors[i], node.kind, false);

          if (fn) {
            var result = fn.apply(visitors[i], arguments);

            if (result === false) {
              skipping[i] = node;
            } else if (result === BREAK) {
              skipping[i] = BREAK;
            } else if (result !== undefined) {
              return result;
            }
          }
        }
      }
    },
    leave: function leave(node) {
      for (var i = 0; i < visitors.length; i++) {
        if (skipping[i] == null) {
          var fn = getVisitFn(visitors[i], node.kind,
          /* isLeaving */
          true);

          if (fn) {
            var result = fn.apply(visitors[i], arguments);

            if (result === BREAK) {
              skipping[i] = BREAK;
            } else if (result !== undefined && result !== false) {
              return result;
            }
          }
        } else if (skipping[i] === node) {
          skipping[i] = null;
        }
      }
    }
  };
}
export function visit(root, visitor) {
  var visitorKeys = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : QueryDocumentKeys;

  /* eslint-disable no-undef-init */
  var stack = undefined;
  var inArray = Array.isArray(root);
  var keys = [root];
  var index = -1;
  var edits = [];
  var node = undefined;
  var key = undefined;
  var parent = undefined;
  var path = [];
  var ancestors = [];
  var newRoot = root;
  /* eslint-enable no-undef-init */

  do {
    index++;
    var isLeaving = index === keys.length;
    var isEdited = isLeaving && edits.length !== 0;

    if (isLeaving) {
      key = ancestors.length === 0 ? undefined : path[path.length - 1];
      node = parent;
      parent = ancestors.pop();

      if (isEdited) {
        if (inArray) {
          node = node.slice();
        } else {
          var clone = {};

          for (var _i2 = 0, _Object$keys2 = Object.keys(node); _i2 < _Object$keys2.length; _i2++) {
            var k = _Object$keys2[_i2];
            clone[k] = node[k];
          }

          node = clone;
        }

        var editOffset = 0;

        for (var ii = 0; ii < edits.length; ii++) {
          var editKey = edits[ii][0];
          var editValue = edits[ii][1];

          if (inArray) {
            editKey -= editOffset;
          }

          if (inArray && editValue === null) {
            node.splice(editKey, 1);
            editOffset++;
          } else {
            node[editKey] = editValue;
          }
        }
      }

      index = stack.index;
      keys = stack.keys;
      edits = stack.edits;
      inArray = stack.inArray;
      stack = stack.prev;
    } else {
      key = parent ? inArray ? index : keys[index] : undefined;
      node = parent ? parent[key] : newRoot;

      if (node === null || node === undefined) {
        continue;
      }

      if (parent) {
        path.push(key);
      }
    }

    var result = void 0;

    if (!Array.isArray(node)) {
      if (!isNode(node)) {
        throw new Error("Invalid AST Node: ".concat(inspect(node), "."));
      }

      var visitFn = getVisitFn(visitor, node.kind, isLeaving);

      if (visitFn) {
        result = visitFn.call(visitor, node, key, parent, path, ancestors);

        if (result === BREAK) {
          break;
        }

        if (result === false) {
          if (!isLeaving) {
            path.pop();
            continue;
          }
        } else if (result !== undefined) {
          edits.push([key, result]);

          if (!isLeaving) {
            if (isNode(result)) {
              node = result;
            } else {
              path.pop();
              continue;
            }
          }
        }
      }
    }

    if (result === undefined && isEdited) {
      edits.push([key, node]);
    }

    if (isLeaving) {
      path.pop();
    } else {
      var _visitorKeys$node$kin;

      stack = {
        inArray: inArray,
        index: index,
        keys: keys,
        edits: edits,
        prev: stack
      };
      inArray = Array.isArray(node);
      keys = inArray ? node : (_visitorKeys$node$kin = visitorKeys[node.kind]) !== null && _visitorKeys$node$kin !== void 0 ? _visitorKeys$node$kin : [];
      index = -1;
      edits = [];

      if (parent) {
        ancestors.push(parent);
      }

      parent = node;
    }
  } while (stack !== undefined);

  if (edits.length !== 0) {
    newRoot = edits[edits.length - 1][1];
  }

  return newRoot;
}

Flags: needinfo?(iireland)
Performance Impact: --- → P3
Keywords: perf:pageload
Whiteboard: [qf:p3:pageload]
Webcompat Priority: ? → ---
You need to log in before you can comment on or make changes to this bug.