@samitouri / QOS-React-2 / commits / 406dcbd8da

Fix a few GC leaks in events system

Summary: - Weren't pooling the Transaction in the batching strategy - Creating a new closure for every event tick due to batchedUpdates() - EnterLeaveEventPlugin creates a new array on each event. I wonder if there is more optimization opportunity in accumulate(). Squinting at the fps graph this seems to be faster and waste less memory but it's hard to conclusively tell. I did verify that these were all hotspots though.

Pete Hunt committed Sep 3, 2013 at 14:15 UTC 406dcbd8daa8bf6e17348006c938168ab6c08e53
4 files changed +20 -11
src/core/ReactDefaultBatchingStrategy.js
+5 -4
@@ -49,6 +49,8 @@ mixInto(ReactDefaultBatchingStrategyTransaction, {
49 }
50 });
51
52 +var transaction = new ReactDefaultBatchingStrategyTransaction();
53 +
54 var ReactDefaultBatchingStrategy = {
55 isBatchingUpdates: false,
56
@@ -56,17 +58,16 @@ var ReactDefaultBatchingStrategy = {
58 * Call the provided function in a context within which calls to `setState`
59 * and friends are batched such that components aren't updated unnecessarily.
60 */
59 - batchedUpdates: function(callback) {
61 + batchedUpdates: function(callback, param) {
62 var alreadyBatchingUpdates = ReactDefaultBatchingStrategy.isBatchingUpdates;
63
64 ReactDefaultBatchingStrategy.isBatchingUpdates = true;
65
66 // The code is written this way to avoid extra allocations
67 if (alreadyBatchingUpdates) {
66 - callback();
68 + callback(param);
69 } else {
68 - var transaction = new ReactDefaultBatchingStrategyTransaction();
69 - transaction.perform(callback);
70 + transaction.perform(callback, null, param);
71 }
72 }
73 };
src/core/ReactEventEmitterMixin.js
+6 -4
@@ -21,6 +21,11 @@
21 var EventPluginHub = require('EventPluginHub');
22 var ReactUpdates = require('ReactUpdates');
23
24 +function runEventQueueInBatch(events) {
25 + EventPluginHub.enqueueEvents(events);
26 + EventPluginHub.processEventQueue();
27 +}
28 +
29 var ReactEventEmitterMixin = {
30 /**
31 * Whether or not `ensureListening` has been invoked.
@@ -77,10 +82,7 @@ var ReactEventEmitterMixin = {
82 );
83
84 // Event queue being processed in the same cycle allows `preventDefault`.
80 - ReactUpdates.batchedUpdates(function() {
81 - EventPluginHub.enqueueEvents(events);
82 - EventPluginHub.processEventQueue();
83 - });
85 + ReactUpdates.batchedUpdates(runEventQueueInBatch, events);
86 }
87 };
88
src/core/ReactUpdates.js
+2 -2
@@ -28,9 +28,9 @@ function ensureBatchingStrategy() {
28 invariant(batchingStrategy, 'ReactUpdates: must inject a batching strategy');
29 }
30
31 -function batchedUpdates(callback) {
31 +function batchedUpdates(callback, param) {
32 ensureBatchingStrategy();
33 - batchingStrategy.batchedUpdates(callback);
33 + batchingStrategy.batchedUpdates(callback, param);
34 }
35
36 function runBatchedUpdates() {
src/eventPlugins/EnterLeaveEventPlugin.js
+7 -1
@@ -35,6 +35,8 @@ var eventTypes = {
35 mouseLeave: {registrationName: keyOf({onMouseLeave: null})}
36 };
37
38 +var extractedEvents = [null, null];
39 +
40 var EnterLeaveEventPlugin = {
41
42 eventTypes: eventTypes,
@@ -99,7 +101,11 @@ var EnterLeaveEventPlugin = {
101 );
102
103 EventPropagators.accumulateEnterLeaveDispatches(leave, enter, fromID, toID);
102 - return [leave, enter];
104 +
105 + extractedEvents[0] = leave;
106 + extractedEvents[1] = enter;
107 +
108 + return extractedEvents;
109 }
110
111 };