react-juce icon indicating copy to clipboard operation
react-juce copied to clipboard

SyntheticEvent Pool

Open nick-thompson opened this issue 5 years ago • 2 comments

After 2d6c9cfda6144134dd2c477af075dbde8d5f1268 we have a pretty good story for #15, and after studying the ReactDOM implementation of the same thing, I think we definitely will want to follow suit and do some pooling: https://github.com/facebook/react/blob/master/packages/react-dom/src/events/SyntheticEvent.js#L45

nick-thompson avatar Sep 08 '20 13:09 nick-thompson

@nick-thompson, once the canvas optimisation goes in I think we're expecting this could be the last major GC churn improvement. Would be cool if we could get a discussion going on here. I'm happy to take this as part of my ongoing "performance rampage".

I'll try to digest some of the SyntheticEvent code you've posted above but if you have any initial/high-level thoughts on the right approach here I'd be most grateful for the wisdom.

If I can glean the approach used in react-dom I'll make suggestions for possible approach also.

JoshMarler avatar Jan 18 '21 16:01 JoshMarler

Sure thing! So there's a very quick approach to pooling that we can do here to get some early performance profiles. We may not need to make any terribly complicated systems here to get the benfits we're looking for. I envision something really quite this simple, initially:

(Imagine the following code roughly replacing this: https://github.com/nick-thompson/blueprint/blob/master/packages/react-juce/src/lib/BlueprintBackend.ts#L234)

    if (SyntheticEvents.isMouseEventHandler(eventType))
      event = SyntheticEventPool.allocateMouseEvent(event);
    else if (SyntheticEvents.isKeyboardEventHandler(eventType))
      event = SyntheticEventPool.allocateKeyboardEvent(event);

    // If mouseDown event we store the target viewId as the last view
    // to recieve a mouseDown for "onClick" book-keeping.
    if (eventType === "onMouseDown") {
      __lastMouseDownViewId = viewId;
      __bubbleEvent(instance, eventType, event);
      return;
    }

    if (eventType === "onMouseUp") {
      __bubbleEvent(instance, eventType, event);

      if (__lastMouseDownViewId && viewId === __lastMouseDownViewId) {
        __lastMouseDownViewId = null;
        __bubbleEvent(instance, "onClick", event);
      }
      return;
    }

    __bubbleEvent(instance, eventType, event);
   SyntheticEventPool.dealloc(event);

So now basically we have this SyntheticEventPool which allocates up front and then holds a big list of synthetic mouse events, keyboard events, etc. When the caller asks to allocate a new event, we pop one off the appropriate list, assign some properties, and hand it over. When the caller is done, they basically give it back and we place it back into one of our lists. What this means is that we never let go of the last reference to a given event object... somebody is always holding it, which means we don't need to gc it. Will need to measure how big we need to make this pool, but if we can keep it fairly small and just circulate the events quickly, there will be way fewer allocations and way fewer objects that need to be cleaned up.

nick-thompson avatar Jan 18 '21 17:01 nick-thompson