Skip to content

Commit 47050f4

Browse files
authoredMar 3, 2022
Add client CS benchmark (#1117)
1 parent 6841504 commit 47050f4

File tree

5 files changed

+329
-123
lines changed

5 files changed

+329
-123
lines changed
 

‎.changeset/funny-planets-yell.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@compiled/react': patch
3+
---
4+
5+
Add client CS benchmark

‎packages/react/package.json

+2
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@
7777
"devDependencies": {
7878
"@compiled/benchmark": "^1.0.1",
7979
"@testing-library/react": "^12.1.3",
80+
"@types/jsdom": "^16.2.14",
8081
"@types/react-dom": "^17.0.11",
82+
"jsdom": "^19.0.0",
8183
"react": "^17.0.2",
8284
"react-dom": "^17.0.2"
8385
},
+137-92
Original file line numberDiff line numberDiff line change
@@ -1,111 +1,156 @@
11
import { runBenchmark } from '@compiled/benchmark';
2+
import { JSDOM } from 'jsdom';
23
import * as React from 'react';
4+
import { memo } from 'react';
5+
import { render } from 'react-dom';
36
import { renderToString } from 'react-dom/server';
47

58
import { CC, CS } from '../index';
69

7-
import { StyleArr, StyleStr } from './utils/cs';
10+
const MemoCS = memo(CS, () => true);
11+
12+
import { StyleBucketFromArray, StyleBucketFromString } from './utils/cs';
813

914
describe('CS benchmark', () => {
10-
it('completes with CS (1 array element) or StyleArr as the fastest', async () => {
11-
const stylesArr = [
12-
'._s7n4jp4b{vertical-align:top}',
13-
'._1reo15vq{overflow-x:hidden}',
14-
'._18m915vq{overflow-y:hidden}',
15-
'._1bto1l2s{text-overflow:ellipsis}',
16-
'._o5721q9c{white-space:nowrap}',
17-
'._ca0qidpf{padding-top:0}',
18-
'._u5f31y44{padding-right:4px}',
19-
'._n3tdidpf{padding-bottom:0}',
20-
'._19bv1y44{padding-left:4px}',
21-
'._p12f12xx{max-width:100px}',
22-
'._1bsb1osq{width:100%}',
23-
];
15+
describe.each(['server', 'client'])('on the %s', (env) => {
16+
const document = globalThis.document;
17+
const window = globalThis.window;
18+
19+
beforeAll(() => {
20+
if (env === 'server') {
21+
// @ts-expect-error
22+
delete globalThis.document;
23+
// @ts-expect-error
24+
delete globalThis.window;
25+
} else {
26+
const dom = new JSDOM('<div id="root"></div>');
27+
globalThis.document = dom.window.document;
28+
// @ts-expect-error
29+
globalThis.window = dom.window;
30+
}
31+
});
32+
33+
afterAll(() => {
34+
globalThis.document = document;
35+
globalThis.window = window;
36+
});
37+
38+
const fastest =
39+
env === 'server'
40+
? ['StyleBucketFromArray', 'StyleBucketFromString']
41+
: ['MemoCS (1 array element)', 'MemoCS (n array elements)'];
2442

25-
const stylesStr = stylesArr.join('');
43+
it(`completes with [${fastest.join(', ')}] as the fastest`, async () => {
44+
const stylesArr = [
45+
'._s7n4jp4b{vertical-align:top}',
46+
'._1reo15vq{overflow-x:hidden}',
47+
'._18m915vq{overflow-y:hidden}',
48+
'._1bto1l2s{text-overflow:ellipsis}',
49+
'._o5721q9c{white-space:nowrap}',
50+
'._ca0qidpf{padding-top:0}',
51+
'._u5f31y44{padding-right:4px}',
52+
'._n3tdidpf{padding-bottom:0}',
53+
'._19bv1y44{padding-left:4px}',
54+
'._p12f12xx{max-width:100px}',
55+
'._1bsb1osq{width:100%}',
56+
];
2657

27-
const className = [
28-
'_bfhk1jys',
29-
'_2rko1l7b',
30-
'_vchhusvi',
31-
'_syaz4rde',
32-
'_1e0c1o8l',
33-
'_1wyb1skh',
34-
'_k48p1fw0',
35-
'_vwz4kb7n',
36-
'_p12f1osq',
37-
'_ca0qyh40',
38-
'_u5f3idpf',
39-
'_n3td1l7b',
40-
'_19bvidpf',
41-
'_1p1dangw',
42-
'_s7n41q9y',
43-
].join(' ');
58+
const stylesStr = stylesArr.join('');
4459

45-
const style = {
46-
'--_16owtcm': 'rgb(227, 252, 239)',
47-
'--_kmurgp': 'rgb(0, 102, 68)',
48-
} as any;
60+
const className = stylesArr.map((rule) => rule.slice(1, 10)).join(' ');
61+
const nonce = 'k0Mp1lEd';
4962

50-
const nonce = 'k0Mp1lEd';
63+
const renderJSX =
64+
env === 'server'
65+
? (jsx: (key: number) => JSX.Element) => {
66+
renderToString(<>{Array.from({ length: 10 }).map((_, i) => jsx(i))}</>);
67+
}
68+
: (jsx: (key: number) => JSX.Element) => {
69+
render(
70+
<>{Array.from({ length: 10 }).map((_, i) => jsx(i))}</>,
71+
globalThis.document.getElementById('root')
72+
);
73+
};
5174

52-
const benchmark = await runBenchmark('CS', [
53-
{
54-
name: 'CS (1 array element)',
55-
fn: () => {
56-
renderToString(
57-
<CC>
58-
<CS nonce={nonce}>{[stylesStr]}</CS>
59-
<span className={className} style={style}>
60-
hello world
61-
</span>
62-
</CC>
63-
);
75+
const tests = [
76+
{
77+
name: 'CS (1 array element)',
78+
fn: () => {
79+
renderJSX((key) => (
80+
<CC key={`cs1-${key}`}>
81+
<CS nonce={nonce}>{[stylesStr]}</CS>
82+
<div className={className} />
83+
</CC>
84+
));
85+
},
6486
},
65-
},
66-
{
67-
name: 'CS (n array elements)',
68-
fn: () => {
69-
renderToString(
70-
<CC>
71-
<CS nonce={nonce}>{stylesArr}</CS>
72-
<span className={className} style={style}>
73-
hello world
74-
</span>
75-
</CC>
76-
);
87+
{
88+
name: 'CS (n array elements)',
89+
fn: () => {
90+
renderJSX((key) => (
91+
<CC key={`csn-${key}`}>
92+
<CS nonce={nonce}>{stylesArr}</CS>
93+
<div className={className} />
94+
</CC>
95+
));
96+
},
7797
},
78-
},
79-
{
80-
name: 'StyleArr',
81-
fn: () => {
82-
renderToString(
83-
<CC>
84-
<StyleArr nonce={nonce}>{stylesArr}</StyleArr>
85-
<span className={className} style={style}>
86-
hello world
87-
</span>
88-
</CC>
89-
);
98+
{
99+
name: 'MemoCS (1 array element)',
100+
fn: () => {
101+
renderJSX((key) => (
102+
<CC key={`memo-cs1-${key}`}>
103+
<MemoCS nonce={nonce}>{[stylesStr]}</MemoCS>
104+
<div className={className} />
105+
</CC>
106+
));
107+
},
90108
},
91-
},
92-
{
93-
name: 'StyleStr',
94-
fn: () => {
95-
renderToString(
96-
<CC>
97-
<StyleStr nonce={nonce}>{stylesStr}</StyleStr>
98-
<span className={className} style={style}>
99-
hello world
100-
</span>
101-
</CC>
102-
);
109+
{
110+
name: 'MemoCS (n array elements)',
111+
fn: () => {
112+
renderJSX((key) => (
113+
<CC key={`memo-csn-${key}`}>
114+
<MemoCS nonce={nonce}>{stylesArr}</MemoCS>
115+
<div className={className} />
116+
</CC>
117+
));
118+
},
103119
},
104-
},
105-
]);
120+
...(env === 'server'
121+
? [
122+
{
123+
name: 'StyleBucketFromArray',
124+
fn: () => {
125+
renderJSX((key) => (
126+
<CC key={`sbfa-${key}`}>
127+
<StyleBucketFromArray nonce={nonce}>{stylesArr}</StyleBucketFromArray>
128+
<div className={className} />
129+
</CC>
130+
));
131+
},
132+
},
133+
{
134+
name: 'StyleBucketFromString',
135+
fn: () => {
136+
renderJSX((key) => (
137+
<CC key={`sbfs-${key}`}>
138+
<StyleBucketFromString nonce={nonce}>{stylesStr}</StyleBucketFromString>
139+
<div className={className} />
140+
</CC>
141+
));
142+
},
143+
},
144+
]
145+
: []),
146+
];
106147

107-
expect(benchmark).toMatchObject({
108-
fastest: expect.not.arrayContaining(['StyleStr', 'CS (n array elements)']),
109-
});
110-
}, 30000);
148+
const benchmark = await runBenchmark('CS', tests);
149+
150+
const slowest = tests.map((t) => t.name).filter((n) => !fastest.includes(n));
151+
for (const name of slowest) {
152+
expect(benchmark.fastest).not.toContain(name);
153+
}
154+
}, 60000);
155+
});
111156
});

‎packages/react/src/runtime/__perf__/utils/cs.tsx

+15-15
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,17 @@ import React, { createContext, useContext } from 'react';
22

33
const Cache = createContext<Record<string, true> | null>(null);
44

5-
export const useCache = (): Record<string, true> => {
6-
return useContext(Cache) || {};
7-
};
8-
9-
export type StyleStrProps = {
10-
children: string;
5+
export type StyleBucketFromArrayProps = {
6+
children: string[];
117
nonce: string;
128
};
139

14-
export function StyleStr({ children, nonce }: StyleStrProps): JSX.Element | null {
15-
const inserted = useCache();
10+
export function StyleBucketFromArray({
11+
children: sheets,
12+
nonce,
13+
}: StyleBucketFromArrayProps): JSX.Element | null {
14+
const inserted = useContext(Cache) || {};
1615

17-
// The following code will not exist in the browser bundle.
18-
const sheets = children.split('.');
1916
let toInsert = '';
2017

2118
for (let i = 0; i < sheets.length; i++) {
@@ -33,15 +30,18 @@ export function StyleStr({ children, nonce }: StyleStrProps): JSX.Element | null
3330
return <style nonce={nonce}>{toInsert}</style>;
3431
}
3532

36-
export type StyleArrProps = {
37-
children: string[];
33+
export type StyleBucketFromStringProps = {
34+
children: string;
3835
nonce: string;
3936
};
4037

41-
export function StyleArr({ children: sheets, nonce }: StyleArrProps): JSX.Element | null {
42-
const inserted = useCache();
38+
export function StyleBucketFromString({
39+
children,
40+
nonce,
41+
}: StyleBucketFromStringProps): JSX.Element | null {
42+
const inserted = useContext(Cache) || {};
4343

44-
// The following code will not exist in the browser bundle.
44+
const sheets = children.split('.');
4545
let toInsert = '';
4646

4747
for (let i = 0; i < sheets.length; i++) {

0 commit comments

Comments
 (0)
Please sign in to comment.