Skip to content

Commit

Permalink
Optimize mapObj (#202)
Browse files Browse the repository at this point in the history
I did some profiling of StyleSheet.create and noticed that mapObj was a
good target for optimization.

Methodology

I created an HTML document with the following code in it:

```html
<script type="text/javascript" src="./dist/aphrodite.umd.js"></script>
<!-- setup -->
<script type="text/javascript">
// build up an array of styles objects to run our test on
var styles = [];
for (var i = 0; i < 10000; i += 1) {
  styles.push({
    [`a${Math.random()}`]: {
      [`a${Math.random()}`]: Math.random(),
      [`b${Math.random()}`]: String(Math.random()),
      [`c${Math.random()}`]: String(Math.random()),
    },

    [`b${Math.random()}`]: {
      [`a${Math.random()}`]: Math.random(),
      [`b${Math.random()}`]: String(Math.random()),
      [`c${Math.random()}`]: String(Math.random()),
    },

    [`c${Math.random()}`]: {
      [`a${Math.random()}`]: Math.random(),
      [`b${Math.random()}`]: String(Math.random()),
      [`c${Math.random()}`]: String(Math.random()),
    },
  });
}
</script>

<!-- test -->
<script type="text/javascript">
setTimeout(() => {
  performance.mark('start_run');
  for (var i = 0; i < styles.length; i += 1) {
    // prevent caching optimizations
    eval('');
    performance.mark('start_stylesheet_create');
    aphrodite.StyleSheet.create(styles[i]);
    performance.mark('end_stylesheet_create');
    performance.measure(
      'aphrodite.StyleSheet.create',
      'start_stylesheet_create',
      'end_stylesheet_create'
    );
    performance.clearMarks('start_stylesheet_create', 'end_stylesheet_create');
  }
  performance.mark('end_run');
  performance.measure(`Benchmark ${styles.length}`, 'start_run', 'end_run');
  performance.clearMarks();
});
</script>
```

Then, looking at the timeline tool in Chrome, I loaded the page a few
times before and after this change. Similarly, I ran a CPU profile
before and after this change through 5 page reloads each.

In this test, the timeline was not very helpful, I think because of the
testing overhead. However, the CPU profile was very clear. Before this
change, normalizing for the callback showing up in a different part of
the profile, `mapObj` took ~317ms and after this change, it drops to
~211ms. `StyleSheet.create` drops from ~755ms to ~670ms or roughly 11%
faster. The rest of the time in `StyleSheet.create` is spent in
`murmurhash2_32_gc` and `hashObject`.
lencioni authored and xymostech committed Mar 7, 2017
1 parent 5d36d56 commit 5e9a0cb
Showing 1 changed file with 9 additions and 10 deletions.
19 changes: 9 additions & 10 deletions src/util.js
Original file line number Diff line number Diff line change
@@ -10,19 +10,18 @@ type ObjectMap = { [id:string]: any };
// {K1: V1, K2: V2, ...} -> [[K1, V1], [K2, V2]]
export const objectToPairs = (obj /* : ObjectMap */) /* : Pairs */ => Object.keys(obj).map(key => [key, obj[key]]);

// [[K1, V1], [K2, V2]] -> {K1: V1, K2: V2, ...}
const pairsToObject = (pairs /* : Pairs */) /* : ObjectMap */ => {
const result = {};
pairs.forEach(([key, val]) => {
result[key] = val;
});
return result;
};

export const mapObj = (
obj /* : ObjectMap */,
fn /* : PairsMapper */
) /* : ObjectMap */ => pairsToObject(objectToPairs(obj).map(fn))
) /* : ObjectMap */ => {
const keys = Object.keys(obj);
const mappedObj = {};
for (let i = 0; i < keys.length; i += 1) {
const [newKey, newValue] = fn([keys[i], obj[keys[i]]]);
mappedObj[newKey] = newValue;
}
return mappedObj;
}

// Flattens an array one level
// [[A], [B, C, [D]]] -> [A, B, C, [D]]

0 comments on commit 5e9a0cb

Please sign in to comment.