@samitouri / QOS-React / commits / 6b9b80faac

Don't materialize typed array until the transfer

Dan Abramov committed Apr 15, 2019 at 16:27 UTC 6b9b80faace37046e1abf10f4a41f4cfa654c5ba
1 file changed +84 -68
src/backend/renderer.js
+84 -68
@@ -577,22 +577,34 @@ export function attach(
577 );
578 }
579
580 - let pendingOperations: Uint32Array = new Uint32Array(0);
581 - let pendingOperationsQueue: Array<Uint32Array> | null = [];
582 -
583 - function addOperation(
584 - newAction: Uint32Array,
585 - addToStartOfQueue: boolean = false
586 - ): void {
587 - const oldActions = pendingOperations;
588 - pendingOperations = new Uint32Array(oldActions.length + newAction.length);
580 + let pendingOperations: Array<number> = [];
581 + let pendingOperationsQueue: Array<Array<number>> | null = [];
582 +
583 + let nextOperation: Array<number> = [];
584 + function beginNextOperation(size: number): void {
585 + nextOperation.length = size;
586 + }
587 + function endNextOperation(addToStartOfQueue: boolean): void {
588 + if (__DEV__) {
589 + for (let i = 0; i < nextOperation.length; i++) {
590 + if (!Number.isInteger(nextOperation[i])) {
591 + console.error(
592 + 'endNextOperation() was called but some values are not integers.',
593 + nextOperation
594 + );
595 + }
596 + }
597 + }
598 +
599 if (addToStartOfQueue) {
590 - pendingOperations.set(newAction);
591 - pendingOperations.set(oldActions, newAction.length);
600 + pendingOperations.splice.apply(
601 + pendingOperations,
602 + [0, 0].concat(nextOperation)
603 + );
604 } else {
593 - pendingOperations.set(oldActions);
594 - pendingOperations.set(newAction, oldActions.length);
605 + pendingOperations.push.apply(pendingOperations, nextOperation);
606 }
607 + nextOperation.length = 0;
608 }
609
610 function flushPendingEvents(root: Object): void {
@@ -608,10 +620,10 @@ export function attach(
620 // Identify which renderer this update is coming from.
621 // This enables roots to be mapped to renderers,
622 // Which in turn enables fiber props, states, and hooks to be inspected.
611 - const idArray = new Uint32Array(2);
612 - idArray[0] = rendererID;
613 - idArray[1] = getFiberID(getPrimaryFiber(root.current));
614 - addOperation(idArray, true);
623 + beginNextOperation(2);
624 + nextOperation[0] = rendererID;
625 + nextOperation[1] = getFiberID(getPrimaryFiber(root.current));
626 + endNextOperation(true);
627
628 // Let the frontend know about tree operations.
629 // The first value in this array will identify which root it corresponds to,
@@ -623,10 +635,10 @@ export function attach(
635 pendingOperationsQueue.push(pendingOperations);
636 } else {
637 // If we've already connected to the frontend, just pass the operations through.
626 - hook.emit('operations', pendingOperations);
638 + hook.emit('operations', Uint32Array.from(pendingOperations));
639 }
640
629 - pendingOperations = new Uint32Array(0);
641 + pendingOperations = [];
642 }
643
644 function recordMount(fiber: Fiber, parentFiber: Fiber | null) {
@@ -642,13 +654,13 @@ export function attach(
654 const hasOwnerMetadata = fiber.hasOwnProperty('_debugOwner');
655
656 if (isRoot) {
645 - const operation = new Uint32Array(5);
646 - operation[0] = TREE_OPERATION_ADD;
647 - operation[1] = id;
648 - operation[2] = ElementTypeRoot;
649 - operation[3] = isProfilingSupported ? 1 : 0;
650 - operation[4] = hasOwnerMetadata ? 1 : 0;
651 - addOperation(operation);
657 + beginNextOperation(5);
658 + nextOperation[0] = TREE_OPERATION_ADD;
659 + nextOperation[1] = id;
660 + nextOperation[2] = ElementTypeRoot;
661 + nextOperation[3] = isProfilingSupported ? 1 : 0;
662 + nextOperation[4] = hasOwnerMetadata ? 1 : 0;
663 + endNextOperation(false);
664 } else {
665 const { displayName, key, type } = getDataForFiber(fiber);
666 const { _debugOwner } = fiber;
@@ -675,23 +687,25 @@ export function attach(
687 displayName === null ? 0 : encodedDisplayName.length;
688 const encodedKeySize = key === null ? 0 : encodedKey.length;
689
678 - const operation = new Uint32Array(
679 - 7 + encodedDisplayNameSize + encodedKeySize
680 - );
681 - operation[0] = TREE_OPERATION_ADD;
682 - operation[1] = id;
683 - operation[2] = type;
684 - operation[3] = parentID;
685 - operation[4] = ownerID;
686 - operation[5] = encodedDisplayNameSize;
690 + beginNextOperation(7 + encodedDisplayNameSize + encodedKeySize);
691 + nextOperation[0] = TREE_OPERATION_ADD;
692 + nextOperation[1] = id;
693 + nextOperation[2] = type;
694 + nextOperation[3] = parentID;
695 + nextOperation[4] = ownerID;
696 + nextOperation[5] = encodedDisplayNameSize;
697 if (displayName !== null) {
688 - operation.set(encodedDisplayName, 6);
698 + for (let i = 0; i < encodedDisplayName.length; i++) {
699 + nextOperation[6 + i] = encodedDisplayName[i];
700 + }
701 }
690 - operation[6 + encodedDisplayNameSize] = encodedKeySize;
702 + nextOperation[6 + encodedDisplayNameSize] = encodedKeySize;
703 if (key !== null) {
692 - operation.set(encodedKey, 6 + encodedDisplayNameSize + 1);
704 + for (let i = 0; i < encodedKey.length; i++) {
705 + nextOperation[6 + encodedDisplayNameSize + 1 + i] = encodedKey[i];
706 + }
707 }
694 - addOperation(operation);
708 + endNextOperation(false);
709 }
710
711 if (isProfiling) {
@@ -699,11 +713,11 @@ export function attach(
713 // So we have to convert them from milliseconds to microseconds so we can send them as ints.
714 const treeBaseDuration = Math.floor(fiber.treeBaseDuration * 1000);
715
702 - const operation = new Uint32Array(3);
703 - operation[0] = TREE_OPERATION_UPDATE_TREE_BASE_DURATION;
704 - operation[1] = id;
705 - operation[2] = treeBaseDuration;
706 - addOperation(operation);
716 + beginNextOperation(3);
717 + nextOperation[0] = TREE_OPERATION_UPDATE_TREE_BASE_DURATION;
718 + nextOperation[1] = id;
719 + nextOperation[2] = treeBaseDuration;
720 + endNextOperation(false);
721
722 const { actualDuration } = fiber;
723 if (actualDuration > 0) {
@@ -733,19 +747,19 @@ export function attach(
747 }
748 const id = getFiberID(primaryFiber);
749 if (isRoot) {
736 - const operation = new Uint32Array(2);
737 - operation[0] = TREE_OPERATION_REMOVE;
738 - operation[1] = id;
739 - addOperation(operation);
750 + beginNextOperation(2);
751 + nextOperation[0] = TREE_OPERATION_REMOVE;
752 + nextOperation[1] = id;
753 + endNextOperation(false);
754 } else if (!shouldFilterFiber(fiber)) {
755 // Non-root fibers are deleted during the commit phase.
756 // They are deleted in the child-first order. However
757 // DevTools currently expects deletions to be parent-first.
758 // This is why we unshift deletions rather tha
745 - const operation = new Uint32Array(2);
746 - operation[0] = TREE_OPERATION_REMOVE;
747 - operation[1] = id;
748 - addOperation(operation, true);
759 + beginNextOperation(2);
760 + nextOperation[0] = TREE_OPERATION_REMOVE;
761 + nextOperation[1] = id;
762 + endNextOperation(true);
763 }
764 fiberToIDMap.delete(primaryFiber);
765 idToFiberMap.delete(id);
@@ -761,10 +775,10 @@ export function attach(
775 function recordRecursiveRemoveChildren(fiber) {
776 const primaryFiber = getPrimaryFiber(fiber);
777 const id = getFiberID(primaryFiber);
764 - const operation = new Uint32Array(2);
765 - operation[0] = TREE_OPERATION_RECURSIVE_REMOVE_CHILDREN;
766 - operation[1] = id;
767 - addOperation(operation, false);
778 + beginNextOperation(2);
779 + nextOperation[0] = TREE_OPERATION_RECURSIVE_REMOVE_CHILDREN;
780 + nextOperation[1] = id;
781 + endNextOperation(false);
782 }
783
784 function mountFiberRecursively(
@@ -832,11 +846,11 @@ export function attach(
846 // So we have to convert them from milliseconds to microseconds so we can send them as ints.
847 const treeBaseDuration = Math.floor(fiber.treeBaseDuration * 1000);
848
835 - const operation = new Uint32Array(3);
836 - operation[0] = TREE_OPERATION_UPDATE_TREE_BASE_DURATION;
837 - operation[1] = getFiberID(getPrimaryFiber(fiber));
838 - operation[2] = treeBaseDuration;
839 - addOperation(operation);
849 + beginNextOperation(3);
850 + nextOperation[0] = TREE_OPERATION_UPDATE_TREE_BASE_DURATION;
851 + nextOperation[1] = getFiberID(getPrimaryFiber(fiber));
852 + nextOperation[2] = treeBaseDuration;
853 + endNextOperation(false);
854 }
855
856 if (haveProfilerTimesChanged(fiber.alternate, fiber)) {
@@ -868,12 +882,14 @@ export function attach(
882 }
883
884 const numChildren = nextChildren.length;
871 - const operation = new Uint32Array(3 + numChildren);
872 - operation[0] = TREE_OPERATION_RESET_CHILDREN;
873 - operation[1] = getFiberID(getPrimaryFiber(fiber));
874 - operation[2] = numChildren;
875 - operation.set(nextChildren, 3);
876 - addOperation(operation);
885 + beginNextOperation(3 + numChildren);
886 + nextOperation[0] = TREE_OPERATION_RESET_CHILDREN;
887 + nextOperation[1] = getFiberID(getPrimaryFiber(fiber));
888 + nextOperation[2] = numChildren;
889 + for (let i = 0; i < nextChildren.length; i++) {
890 + nextOperation[3 + i] = nextChildren[i];
891 + }
892 + endNextOperation(false);
893 }
894 }
895
@@ -1029,7 +1045,7 @@ export function attach(
1045 // We may have already queued up some operations before the frontend connected
1046 // If so, let the frontend know about them.
1047 localPendingOperationsQueue.forEach(pendingOperations => {
1032 - hook.emit('operations', pendingOperations);
1048 + hook.emit('operations', Uint32Array.from(pendingOperations));
1049 });
1050 } else {
1051 // If we have not been profiling, then we can just walk the tree and build up its current state as-is.